科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道应用软件利用ColdFusion组件实现状态模式

利用ColdFusion组件实现状态模式

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

本文向大家展示了如何利用状态模式解决CFC中的方法或条件爆炸问题。虽然模式的应用可能会给系统带来另外的复杂性,但是对它所带来的优点来说这还是值得的。

作者:builder.com.cn 2007年3月26日

关键字:

  • 评论
  • 分享微博
  • 分享邮件

状态模式定义

状态模式的概念在由Gamma, Helms, Johnson,和 Vlissides(“四人帮”)所著的《设计模式》一书中首次提出。在这本书中,作者指出状态模式的意图是“当对象的内部状态改变时,允许改变它的行为。对象将看起来能够改变它的类”。

即使不了解状态模式,仅上面的描述就可能使你惊奇。“当对象的内部状态改变时,允许改变它的行为”听起来像是我们想要内容项完成很多功能。

应用状态模式解决问题

实质上,状态模式所做的就是允许我们将不同的状态和每个状态需要的行为封装到CFCs中,每个状态知道如何对不同的行为作出反应,且根据不同的环境设定内容项的状态。我们给每个内容项(为了清楚起见,在这种解决方案中我将它称为ContentItemContext)提供一个简单的接口,但是避免了上面例子中的条件爆炸问题。ContentItemContext见列表E,状态CFC代码如列表F,运行测试如列表G。

列表 E

<cfcomponent name="ContentItemContext" hint="I am a per-reqeust CFC that represents a Content Item. All instance data about the item would go here.">
<cffunction name="init" access="public" returntype="ContentItemContext" hint="Constructor.">
<cfargument name="draftState" type="DraftState" required="true" />
<cfargument name="reviewState" type="ReviewState" required="true" />
<cfargument name="approvePublishState" type="ApprovePublishState" required="true" />
<cfargument name="publishedState" type="PublishedState" required="true" />
<cfargument name="initialState" type="string" required="true" />
<cfset variables.instance.draftState = arguments.draftState />
<cfset variables.instance.reviewState = arguments.reviewState />
<cfset variables.instance.approvePublishState = arguments.approvePublishState />
<cfset variables.instance.publishedState = arguments.publishedState />
<cfset createStateNameMappings() />
<cfset setStateByName(arguments.initialState) />
<cfreturn this />
</cffunction>
<cffunction name="save" access="public" returntype="void" output="true" hint="">
<cfset var local = structNew() />
<cfset getCurrentState().save(this) />
</cffunction>
<cffunction name="approve" access="public" returntype="void" output="true" hint="">
<cfset var local = structNew() />
<cfset getCurrentState().approve(this) />
</cffunction>
<cffunction name="reject" access="public" returntype="void" output="true" hint="">
<cfset var local = structNew() />
<cfset getCurrentState().reject(this) />
</cffunction>

<!--- State-related methods --->
<cffunction name="createStateNameMappings" access="private" returntype="void" output="false" hint="">
<cfset var local = structNew() />
<cfset variables.stateMappings['draft'] = variables.instance.draftState />
<cfset variables.stateMappings['review'] = variables.instance.reviewState />
<cfset variables.stateMappings['publishApproval'] = variables.instance.approvePublishState />
<cfset variables.stateMappings['published'] = variables.instance.publishedState />
</cffunction>

<cffunction name="setStateByName" access="public" returntype="void" output="false" hint="">
<cfargument name="stateName" type="string" required="true" />
<cfset setCurrentState(getStateByName(arguments.stateName)) />
</cffunction>

<cffunction name="getStateByName" access="private" returntype="AbstractContentState" output="false" hint="">
<cfargument name="stateName" type="string" required="true" />
<cftry>
<cfreturn variables.stateMappings[arguments.stateName] />
<cfcatch type="any">
<cfthrow message="No state with a mapping of '#arguments.stateName#' could be found." />
</cfcatch>
</cftry>
</cffunction>

<cffunction name="getCurrentState" access="private" returntype="AbstractContentState" output="false" roles="" hint="I return the currentState.">
<cfreturn variables.instance.currentState />
</cffunction>

<cffunction name="setCurrentState" access="private" returntype="void" output="false" roles="" hint="I set the currentState.">
<cfargument name="currentState" type="AbstractContentState" required="true" hint="currentState" />
<cfset variables.instance.currentState = arguments.currentState />
</cffunction>

</cfcomponent>

列表 F


<cfcomponent output="false">

<cffunction name="init" access="public" returntype="AbstractContentState" hint="Constructor.">
<cfargument name="stateName" type="string" required="true" />
<cfset setStateName(arguments.stateName) />
<cfreturn this />
</cffunction>

<cffunction name="save" access="public" returntype="void" output="true" hint="">
<cfthrow message="State '#getStateName()#' does not implement the save method." />
</cffunction>

<cffunction name="approve" access="public" returntype="void" output="true" hint="">
<cfthrow message="State '#getStateName()#' does not implement the approve method." />
</cffunction>

<cffunction name="deploy" access="public" returntype="void" output="true" hint="">
<cfthrow message="State '#getStateName()#' does not implement the deploy method." />
</cffunction>

<cffunction name="makeDraft" access="public" returntype="void" output="true" hint="">
<cfthrow message="State '#getStateName()#' does not implement the makeDraft method." />
</cffunction>

<cffunction name="getStateName" access="private" returntype="string" output="false" roles="" hint="I return the stateName.">
<cfreturn variables.instance.stateName />
</cffunction>

<cffunction name="setStateName" access="private" returntype="void" output="false" roles="" hint="I set the stateName.">
<cfargument name="stateName" type="string" required="true" hint="stateName" />
<cfset variables.instance.stateName = arguments.stateName />
</cffunction>

</cfcomponent>

<cfcomponent name="DraftState" extends="AbstractContentState" hint="I am a Singleton state object. I have no instance data, I only manage logic.">

<cffunction name="init" access="public" returntype="DraftState" hint="Constructor.">
<cfset super.init('Draft') />
<cfreturn this />
</cffunction>

<cffunction name="save" access="public" returntype="void" output="true" hint="">
<cfargument name="context" type="ContentItemContext" required="true" />
<cfset var local = structNew() />
<cfoutput>
State '#getStateName()#' saving content item...<br/>
</cfoutput>
</cffunction>

<cffunction name="approve" access="public" returntype="void" output="true" hint="">
<cfargument name="context" type="ContentItemContext" required="true" />
<cfset var local = structNew() />
<cfoutput>
State '#getStateName()#' saving content item and alerting reviewer about content for review...<br/>
</cfoutput>
<cfset arguments.context.setStateByName('review') />
</cffunction>

</cfcomponent>

<cfcomponent name="ReviewState" extends="AbstractContentState" hint="I am a Singleton state object. I have no instance data, I only manage logic.">

<cffunction name="init" access="public" returntype="ReviewState" hint="Constructor.">
<cfset super.init('Review') />
<cfreturn this />
</cffunction>

<cffunction name="approve" access="public" returntype="void" output="true" hint="">
<cfargument name="context" type="ContentItemContext" required="true" />
<cfset var local = structNew() />
<cfoutput>
State '#getStateName()#' alerting content author that content is approved...<br/>
State '#getStateName()#' marking content as ready for publish approval...<br/>
</cfoutput>
<cfset arguments.context.setStateByName('publishApproval') />
</cffunction>

<cffunction name="reject" access="public" returntype="void" output="true" hint="">
<cfargument name="context" type="ContentItemContext" required="true" />
<cfset var local = structNew() />
<cfoutput>
State '#getStateName()#' alerting content author that content is rejected...<br/>
State '#getStateName()#' setting back to draft mode...<br/>
</cfoutput>
<cfset arguments.context.setStateByName('draft') />
</cffunction>

</cfcomponent>

<cfcomponent name="ApprovePublishState" extends="AbstractContentState" hint="I am a Singleton state object. I have no instance data, I only manage logic.">

<cffunction name="init" access="public" returntype="ApprovePublishState" hint="Constructor.">
<cfset super.init('ApprovePublish') />
<cfreturn this />
</cffunction>

<cffunction name="approve" access="public" returntype="void" output="true" hint="">
<cfargument name="context" type="ContentItemContext" required="true" />
<cfset var local = structNew() />
<cfoutput>
State '#getStateName()#' Marking content as deployed...<br/>
State '#getStateName()#' Pushing live and updating content cache...<br/>
</cfoutput>
<cfset arguments.context.setStateByName('published') />
</cffunction>

<cffunction name="reject" access="public" returntype="void" output="true" hint="">
<cfargument name="context" type="ContentItemContext" required="true" />
<cfset var local = structNew() />
<cfoutput>
State '#getStateName()#' alerting reviewer that publishing is rejected...<br/>
State '#getStateName()#' setting back to review mode...<br/>
</cfoutput>
<cfset arguments.context.setStateByName('review') />
</cffunction>

</cfcomponent>

<cfcomponent name="PublishedState" extends="AbstractContentState" hint="I am a Singleton state object. I have no instance data, I only manage logic.">

<cffunction name="init" access="public" returntype="PublishedState" hint="Constructor.">
<cfset super.init('Published') />
<cfreturn this />
</cffunction>

<cffunction name="save" access="public" returntype="void" output="true" hint="">
<cfargument name="context" type="ContentItemContext" required="true" />
<cfset var local = structNew() />
<cfoutput>
State '#getStateName()#' setting the content to draft mode...<br/>
</cfoutput>
<cfset arguments.context.setStateByName('draft') />
</cffunction>

</cfcomponent>

列表G


<h2>Now, using the State pattern:</h2>
<cfset draftState = createObject('component','DraftState').init() />
<cfset reviewState = createObject('component','ReviewState').init() />
<cfset approvePublishState = createObject('component','ApprovePublishState').init() />
<cfset publishedState = createObject('component','PublishedState').init() />
States created...<br/>
<hr/>

<cfset content = createObject('component','ContentItemContext').init(draftState,reviewState,approvePublishState,publishedState,'draft') />
Context created in <strong>draft</strong>...<br/>
<cfset content.save() />
<cfset content.approve() />
<cfset content.approve() />
<cfset content.approve() />
<cfset content.save() />
<cfset content.approve() />
<cfset content.reject() />

<hr/>
<cfset content2 = createObject('component','ContentItemContext').init(draftState,reviewState,approvePublishState,publishedState,'review') />
Context created in <strong>review</strong>...<br/>
<cfset content2.reject() />
<cfset content2.save() />
<cfset content2.approve() />
<cfset content2.approve() />
<cfset content2.reject() />

ContentItemContext所做的是,当调用方法时将每个状态的参数“this”传递给自身。通过这种方式,状态CFC可以执行它的行为,然后指示ContentItemContext进入下个状态。

如果需要添加状态,我们只要创建一个新的状态对象,然后添加部分代码让ContentItemContext知道新添加的状态。所有将来添加的状态的行为被封装起来,尽量避免改变已经存在的代码。

当然,绝大多数情况下,每个状态是一个单独对象,也就是说它们不包含实例数据,且它们只需要创建一次。可能我们需要大量ContentItemContext CFCs(每个内容项对应一个),但是均可用同样一个实例。这就减少了我们需要创建的CFCs数量。虽然并不是总是如此,但也时有发生。

为了展示状态模式,为大家附加一个UML图,如下表A。对于熟悉模式的人,可能会注意到对象之间的关系与策略模式和桥模式类似。虽然它们间的关系类似,但是每种模式的用途不同。

表 A

利用ColdFusion组件实现状态模式

状态模式UML图

值得一试

本文向大家展示了如何利用状态模式解决CFC中的方法或条件爆炸问题。虽然模式的应用可能会给系统带来另外的复杂性,但是对它所带来的优点来说这还是值得的。

责任编辑:德东

查看本文国际来源

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章