zoukankan      html  css  js  c++  java
  • javascript设计模式--状态模式(State)

      1 <!doctype html>
      2 <html lang="en">
      3 <head>
      4     <meta charset="UTF-8">
      5     <title>State Pattern</title>
      6 </head>
      7 <body>
      8 
      9 <script>
     10 /**
     11  * 状态模式
     12  *
     13  * 定义:
     14  * 允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
     15  *
     16  * 本质:
     17  * 根据状态来分离和选择行为
     18  *
     19  * 1.状态和行为
     20  * 所谓对象的状态,通常指的就是对象实例的属性的值;而行为指的就是对象的功能,再具体点,行为大多可以对应到方法上。
     21  * 状态模式的功能就是分离状态和行为,通过维护状态的变化,来调用不同状态的的不同功能。
     22  * 也就是说,状态和行为是相关联的,它们的关系可以描述为:状态决定行为。
     23  *
     24  * 2.行为的平行性
     25  * 平行性指的是各个状态的行为所处的层次是一样的,相互是独立的,没有关联的,是根据不同的状态来决定到底走平行线哪一条。行为是不用的,当然对应的实现也是不同的,相互之间是不可替换的。
     26  * 平等性强调的是可替换性,大家是同一行为的不同描述或实现,因此在同一个行为发生的时候,可以根据条件挑选任意的一个实现来进行相应的处理。
     27  * 状态模式和策略模式的结构完全一样。状态模式的行为是平行性的,不可相互替换的;而策略模式的行为是平等性的,可相互替换的。
     28  *
     29  * 3.上下文和状态处理对象呢
     30  * 在状态模式中,上下文是持有状态的对象,但是上下文自身并不处理跟状态相关的行为,而是把处理状态的功能委托给了状态对应的状态处理类来处理。
     31  * 在具体的状态处理类中经常需要获取上下文自身的数据,甚至在必要的时候会回调上下文的方法,因此,通常将上下文自身当作一个参数传递给具体的状态处理类。
     32  * 客户端一般只和上下文交互。客户端可以用状态对象来配置一个上下文,一旦配置完毕,就不需要再和状态对象打交道了。
     33  *
     34  * 4.不完美的OCP体验
     35  * 由于状态的维护和转换在状态模式结构里面,不管你是扩展了状态实现类,还是新添加了状态实现类,都需要修改状态维护和转换的地方。
     36  *
     37  * 5.创建和销毁状态对象
     38  * 究竟何时创建和销毁状态对象?
     39  * 1)当需要使用状态对象的时候创建,使用完后销毁它们
     40  * 2)提前创建它们并始终不销毁。
     41  * 3)采用延迟加载和缓存合用的方式,就是当第一次需要使用状态对象的时候创建,使用完后并不销毁对象,而是把这个对象缓存起来,等待下一次使用,而且在合适的时候,会有缓存框剪销毁状态对象。
     42  * 如果状态在运行时是不可知的,而且上下文比较稳定,建议选择1.
     43  * 如果状态改变很频繁,而且状态对象还存储着大量的数据信息,建议选择2.
     44  * 如果无法确定状态改变是否频繁,而且有些状态对象的状态数据量大,有些较小,建议选择3.
     45  *
     46  * 6.状态模式的调用顺序
     47  * 在Context中进行状态的维护和转换:
     48  * 1)调用上下文的方法来处理业务请求。
     49  * 2)判断并维护状态。
     50  * 3)根据状态来调用相应的状态处理对象的方法。
     51  *
     52  * 采用让状态对象来维护和转换状态的调用顺序
     53  * 1)调用上下文的方法来处理业务请求。
     54  * 2)获取State对象。
     55  * 3)委托让相应的状态对象去处理。
     56  * 4)调用构造方法得到下一个状态对象的实例。
     57  * 5)设置下一个状态处理对象。
     58  * 6)再到6),直到结束。
     59  *
     60  * 状态的维护和转换控制
     61  * 所谓状态的维护,指的是维护状态的数据,给状态设置不用的状态值;而状态的转换,指的是根据状态的变化来选择不用的状态处理对象。在状态模式中,通常有两个地方可以进行状态的维护和转换控制。
     62  * 一个就是在上下文中。因为状态本身通常被实现为上下文对象的状态,因此可以在上下文中进行状态维护,当然也就可以控制状态的转换了。
     63  * 另外一个地方就是在状态的处理类中。当每个状态处理对象处理完自身状态所对应的功能后,可以根据需要指定后继状态,以便让应用能正确处理后继的请求。
     64  *
     65  * 如何选择这两种方式?
     66  * 1.如果状态转换的规则是一定的,一般不需要进行什么扩展规则,那么就适合在上下文中统一进行状态的维护。
     67  * 2.如果状态的转换取决于前一个状态动态处理的结果,或者是依赖于外部数据,为了增强灵活性,这种情况下,一般是在状态处理类中进行状态的维护。
     68  *
     69  * 还可以使用数据库来维护状态
     70  *
     71  */
     72 
     73 (function () {
     74     // 示例代码
     75 
     76     // 实现一个与Context的一个特定状态相关的行为
     77     function ConcreteStateA() {}
     78 
     79     ConcreteStateA.prototype.handle = function () {};
     80 
     81     function ConcreteStateB() {}
     82 
     83     ConcreteStateB.prototype.handle = function () {};
     84 
     85     // 定义客户感兴趣的接口,通常会维护一个State的对象实例
     86     function Context(state) {
     87         this.state = state;
     88     }
     89 
     90     Context.prototype = {
     91         request: function (param) {
     92             this.state.handle(param);
     93         }
     94     };
     95 }());
     96 
     97 (function () {
     98     // 示例
     99 
    100     function NormalVoteState() {}
    101 
    102     NormalVoteState.prototype.vote = function (user, voteItem, voteManager) {
    103         voteManager.mapVote[user] = voteItem;
    104     };
    105 
    106     function RepeatVoteState() {}
    107 
    108     RepeatVoteState.prototype.vote = function (user, voteItem, voteManager) {
    109         console.log('请不要重复投票');
    110     };
    111 
    112     function SpliteVoteState() {}
    113 
    114     SpliteVoteState.prototype.vote = function (user, coteItem, voteManager) {
    115         var s = voteManager.mapVote[user];
    116         if (s) {
    117             delete voteManager.mapVote[user];
    118         }
    119 
    120         console.log('你有恶意刷票行为,取消投票资格');
    121     };
    122 
    123     function BlackVoteState() {}
    124 
    125     BlackVoteState.prototype.vote = function (user, voteItem, voteManager) {
    126         console.log('进入黑名单,将禁止登录和使用本系统');
    127     };
    128 
    129     function VoteManager() {
    130         this.state = null;
    131         this.mapVote = {};
    132         this.mapVoteCount = {};
    133     }
    134 
    135     VoteManager.prototype = {
    136         vote: function (user, voteItem) {
    137             var oldVoteCount = this.mapVoteCount[user] || 0;
    138 
    139             this.mapVoteCount[user] = ++oldVoteCount;
    140 
    141             if (oldVoteCount == 1) {
    142                 this.state = new NormalVoteState();
    143             } else if (oldVoteCount > 1 && oldVoteCount < 5) {
    144                 this.state = new RepeatVoteState();
    145             } else if (oldVoteCount >= 5 && oldVoteCount < 8) {
    146                 this.state = new SpliteVoteState();
    147             } else if (oldVoteCount >= 8) {
    148                 this.state = new BlackVoteState();
    149             }
    150 
    151             this.state.vote(user, voteItem, this);
    152         }
    153     };
    154 
    155     var vm = new VoteManager();
    156     for (var i = 0; i < 8; i++) {
    157         vm.vote('u1', 'A');
    158     }
    159 
    160 
    161     // another
    162     var States = {
    163         normal: function (user, voteItem, voteManager) {
    164             voteManager.mapVote[user] = voteItem;
    165         },
    166         repeat: function (user, voteItem, voteManager) {
    167             console.log('请不要重复投票');
    168         },
    169         splite: function (user, coteItem, voteManager) {
    170             var s = voteManager.mapVote[user];
    171             if (s != null) {
    172                 delete voteManager.mapVote[user];
    173             }
    174 
    175             console.log('你有恶意刷票行为,取消投票资格');
    176         },
    177         black: function (user, voteItem, voteManager) {
    178             console.log('进入黑名单,将禁止登录和使用本系统');
    179         }
    180     };
    181 
    182     function VoteManager2() {
    183         this.state = null;
    184         this.mapVote = {};
    185         this.mapVoteCount = {};
    186     }
    187 
    188     VoteManager2.prototype = {
    189         vote: function (user, voteItem) {
    190             var oldVoteCount = this.mapVoteCount[user] || 0;
    191 
    192             this.mapVoteCount[user] = ++oldVoteCount;
    193 
    194             var state;
    195             if (oldVoteCount == 1) {
    196                 state = 'normal';
    197             } else if (oldVoteCount > 1 && oldVoteCount < 5) {
    198                 state = 'repeat';
    199             } else if (oldVoteCount >= 5 && oldVoteCount < 8) {
    200                 state = 'splite';
    201             } else if (oldVoteCount >= 8) {
    202                 state = 'black';
    203             }
    204 
    205             this.state = States[state];
    206 
    207             this.state(user, voteItem, this);
    208         }
    209     };
    210 
    211     var vm = new VoteManager2();
    212     for (var i = 0; i < 8; i++) {
    213         vm.vote('u1', 'A');
    214     }
    215 }());
    216 
    217 (function () {
    218     // 在状态处理类中进行后继状态的维护和转换
    219 
    220     function NormalVoteState() {
    221         this.vote = function (user, voteItem, voteManager) {
    222             voteManager.mapVote[user] = voteItem;
    223             console.log('恭喜你投票成功');
    224             // 正常投票完毕,维护下一个状态,同一个人再投票就重复了
    225             voteManager.mapState[user] = new RepeatVoteState();
    226         };
    227     }
    228 
    229     function RepeatVoteState() {
    230         this.vote = function (user, voteItem, voteManager) {
    231             console.log('请不要重复投票');
    232             if (voteManager.mapVoteCount[user] >= 4) {
    233                 voteManager.mapState[user] = new SpliteVoteState();
    234             }
    235         };
    236     }
    237 
    238     function SpliteVoteState() {
    239         this.vote = function (user, voteItem, voteManager) {
    240             var s = voteManager.mapVote[user];
    241 
    242             if (s != null) {
    243                 delete voteManager.mapVote[user];
    244             }
    245 
    246             console.log('你有恶意刷票行为,取消投票资格');
    247 
    248             if (voteManager.mapVoteCount[user] >= 7) {
    249                 voteManager.mapState[user] = new BlackVoteState();
    250             }
    251         };
    252     }
    253 
    254     function BlackVoteState() {
    255         this.vote = function (user, voteItem, voteManager) {
    256             console.log('进入黑名单,将禁止登录和使用本系统');
    257         };
    258     }
    259 
    260     function VoteManager() {
    261         this.mapState = {};
    262         this.mapVote = {};
    263         this.mapVoteCount = {};
    264 
    265         this.vote = function (user, voteItem) {
    266             var oldVoteCount = this.mapVoteCount[user];
    267 
    268             if (oldVoteCount == null) {
    269                 oldVoteCount = 0;
    270             }
    271             this.mapVoteCount[user] = ++oldVoteCount;
    272 
    273             var state = this.mapState[user];
    274             if (state == null) {
    275                 state = new NormalVoteState();
    276             }
    277 
    278             state.vote(user, voteItem, this);
    279         };
    280     }
    281 
    282 
    283     var vm = new VoteManager();
    284     for (var i = 0; i < 8; i++) {
    285         vm.vote('u1', 'A');
    286     }
    287 
    288     // another way
    289 
    290     function VoteManager2() {
    291         var mapState = {};
    292         var mapVote = {};
    293         var mapVoteCount = {};
    294 
    295         this.vote = function (user, voteItem) {
    296             var oldVoteCount = mapVoteCount[user];
    297 
    298             if (oldVoteCount == null) {
    299                 oldVoteCount = 0;
    300             }
    301             mapVoteCount[user] = ++oldVoteCount;
    302 
    303             var state = mapState[user];
    304             if (state == null) {
    305                 state = voteNormal;
    306             }
    307 
    308             state(user, voteItem);
    309         };
    310 
    311         function voteNormal(user, voteItem) {
    312             mapVote[user] = voteItem;
    313             console.log('恭喜你投票成功');
    314             // 正常投票完毕,维护下一个状态,同一个人再投票就重复了
    315             return mapState[user] = voteRepeat;
    316         }
    317 
    318         function voteRepeat(user, voteItem) {
    319             console.log('请不要重复投票');
    320             if (mapVoteCount[user] >= 4) {
    321                 return mapState[user] = voteSplite;
    322             }
    323         }
    324 
    325         function voteSplite(user, voteItem) {
    326             var s = mapVote[user];
    327 
    328             if (s != null) {
    329                 delete mapVote[user];
    330             }
    331 
    332             console.log('你有恶意刷票行为,取消投票资格');
    333 
    334             if (mapVoteCount[user] >= 7) {
    335                 return mapState[user] = voteBlack;
    336             }
    337         }
    338 
    339         function voteBlack(user, voteItem) {
    340             console.log('进入黑名单,将禁止登录和使用本系统');
    341         }
    342     }
    343 
    344     var vm = new VoteManager2();
    345     for (var i = 0; i < 8; i++) {
    346         vm.vote('u1', 'A');
    347     }
    348 }());
    349 
    350 (function () {
    351     // 模拟工作流
    352     /*
    353     请假流程,需项目经理和部门经理审批
    354      */
    355 
    356     // 公共状态处理机
    357     function LeaveRequestContext() {
    358         // 持有一个状态对象
    359         this.state = null;
    360         // 包含流程处理需要的业务数据对象
    361         this.businessVO = null;
    362     }
    363 
    364     LeaveRequestContext.prototype = {
    365         // 执行工作
    366         doWork: function () {
    367             this.state.doWork(this);
    368         }
    369     };
    370 
    371     // 定义请假单的业务数据模型
    372     function LeaveRequestModel() {
    373         // 请假人
    374         this.user = '';
    375         // 请假开始时间
    376         this.beginDate = '';
    377         // 请假天数
    378         this.leaveDays = '';
    379         // 审核结果
    380         this.result = '';
    381     }
    382 
    383     function ProjectManagerState() {
    384         this.doWork = function (request) {
    385             var leaveRequestModel = request.businessVO;
    386 
    387             console.log('项目经理审核中,请稍候。。');
    388             console.log(leaveRequestModel.user + '申请从'
    389                 + leaveRequestModel.beginDate + '开始请假'
    390                 + leaveRequestModel.leaveDays + '天,请项目经理审核(1为同意,2为不同意)');
    391 
    392             var answer = window.prompt('1为同意,2为不同意');
    393             var result = answer == 1 ? '同意' : '不同意';
    394             leaveRequestModel.result = '项目经理审核结果:' + result;
    395 
    396             if (answer == 1) {
    397                 if (leaveRequestModel.leaveDays > 3) {
    398                     request.state = new DepManagerState();
    399                 } else {
    400                     request.state = new AuditOverState();
    401                 }
    402             } else {
    403                 request.state = new AuditOverState();
    404             }
    405 
    406             request.doWork();
    407         };
    408     }
    409 
    410     function DepManagerState() {
    411         this.doWork = function (request) {
    412             var leaveRequestModel = request.businessVO;
    413 
    414             console.log('部门经理审核中,请稍候。。');
    415             console.log(leaveRequestModel.user + '申请从'
    416                 + leaveRequestModel.beginDate + '开始请假'
    417                 + leaveRequestModel.leaveDays + '天,请项目经理审核(1为同意,2为不同意)');
    418 
    419             var answer = window.prompt('1为同意,2为不同意');
    420             var result = answer == 1 ? '同意' : '不同意';
    421             leaveRequestModel.result = '部门经理审核结果:' + result;
    422 
    423             request.state = new AuditOverState();
    424 
    425             request.doWork();
    426         };
    427     }
    428 
    429     function AuditOverState() {
    430         this.doWork = function (request) {
    431             var leaveRequestModel = request.businessVO;
    432             // do sth
    433             console.log(leaveRequestModel.user + ',你的请假申请已经审核结束,结果是:' + leaveRequestModel.result);
    434         };
    435     }
    436 
    437     var lrm = new LeaveRequestModel();
    438     lrm.user = '小林';
    439     lrm.beginDate = '2014-4-2';
    440     lrm.leaveDays = 5;
    441 
    442     var request = new LeaveRequestContext();
    443     request.businessVO = lrm;
    444     request.state = new ProjectManagerState();
    445 
    446     request.doWork();
    447 
    448 
    449     // another
    450 
    451     function LeaveRequestContext2() {
    452         this.state = null;
    453         // 包含流程处理需要的业务数据对象
    454         this.businessVO = null;
    455         this.doWork = function () {
    456             if (typeof this.state == 'function') {
    457                 this.state = this.state(this);
    458                 this.doWork();
    459             }
    460         };
    461     }
    462 
    463     function projectManagerState(request) {
    464         var leaveRequestModel = request.businessVO;
    465 
    466         console.log('项目经理审核中,请稍候。。');
    467         console.log(leaveRequestModel.user + '申请从'
    468             + leaveRequestModel.beginDate + '开始请假'
    469             + leaveRequestModel.leaveDays + '天,请项目经理审核(1为同意,2为不同意)');
    470 
    471         var answer = window.prompt('1为同意,2为不同意');
    472         var result = answer == 1 ? '同意' : '不同意';
    473         leaveRequestModel.result = '项目经理审核结果:' + result;
    474 
    475         var state;
    476         if (answer == 1) {
    477             if (leaveRequestModel.leaveDays > 3) {
    478                 state = depManagerState;
    479             } else {
    480                 state = auditOverState;
    481             }
    482         } else {
    483             state = auditOverState;
    484         }
    485 
    486         return state;
    487     }
    488 
    489     function depManagerState(request) {
    490         var leaveRequestModel = request.businessVO;
    491 
    492         console.log('部门经理审核中,请稍候。。');
    493         console.log(leaveRequestModel.user + '申请从'
    494             + leaveRequestModel.beginDate + '开始请假'
    495             + leaveRequestModel.leaveDays + '天,请项目经理审核(1为同意,2为不同意)');
    496 
    497         var answer = window.prompt('1为同意,2为不同意');
    498         var result = answer == 1 ? '同意' : '不同意';
    499         leaveRequestModel.result = '部门经理审核结果:' + result;
    500 
    501         return auditOverState;
    502     }
    503 
    504     function auditOverState(request) {
    505         var leaveRequestModel = request.businessVO;
    506         // do sth
    507         console.log(leaveRequestModel.user + ',你的请假申请已经审核结束,结果是:' + leaveRequestModel.result);
    508     }
    509 
    510     var lrm = new LeaveRequestModel();
    511     lrm.user = '小林';
    512     lrm.beginDate = '2014-4-2';
    513     lrm.leaveDays = 5;
    514 
    515     var request = new LeaveRequestContext2();
    516     request.businessVO = lrm;
    517     request.state = projectManagerState;
    518 
    519     request.doWork();
    520 
    521 }());
    522 
    523 /*
    524 何时选用状态模式
    525 1.如果一个对象的行为取决于它的状态,而且它必须在运行时刻根据状态来改变它的行为,可以使用状态模式把状态和行为分离。
    526 2.如果一个操作中含有庞大的多分支语句,而且这些分支依赖于该对象的状态,可以使用状态模式,把各个分支的处理分散包装到单独的对象处理类中。
    527 
    528 
    529 相关模式
    530 
    531 状态模式和策略模式
    532 两个结构相同。状态模式的行为是平行性的,不可相互替换的;而策略模式的行为是平等性的,可相互替换的。
    533 
    534 状态模式和观察者模式
    535 相似但又有区别,可以组合使用。
    536 这两个模式都是在状态发生改变的时候触发行为,只不过观察者模式的行为是固定的,那就是通知所有观察者;而状态模式是根据状态来选择不同的处理。
    537 从表面来看,两个模式相似,观察者模式中的被观察对象就好比状态模式中的上下文,观察者模式中当被观察对象的状态发生改变的时候,触发的通知所有观察者的方法就好比状态模式中,根据状态的变化选择对应的状态处理。
    538 但实际这两个模式是不同的,观察者模式的目的是在被观察者的状态发生改变的时候,触发观察联动,具体如何处理观察者模式不管;而状态模式的主要目的在于根据状态来分离和选择行为,当状态发生改变的时候,动态地改变行为。
    539 这两个模式可以组合使用,比如在观察者模式的观察者部分,当被观察对象的状态发生改变,触发通知了所有的观察者后,使用状态模式根据通知过来的状态选择相应的处理。
    540 
    541 状态模式和单例模式
    542 可以组合使用
    543 把状态模式中的状态处理类实现成单例。
    544 
    545 状态模式和享元模式
    546 可以组合使用
    547 由于状态模式把状态对应的行为分散到多个状态对象中,会造成很多细粒度的状态对象,可以把这些状态处理对象通过享元模式来共享,从而节省资源。
    548  */
    549 
    550 (function () {
    551     // http://www.dofactory.com/javascript-state-pattern.aspx
    552     var TrafficLight = function () {
    553 
    554         var count = 0;
    555         var currentState = new Red(this);
    556 
    557         this.change = function (state) {
    558             // limits number of changes
    559             if (count++ >= 10) return;
    560 
    561             currentState = state;
    562             currentState.go();
    563         };
    564 
    565         this.start = function () {
    566             currentState.go();
    567         };
    568     }
    569 
    570     var Red = function (light) {
    571         this.light = light;
    572 
    573         this.go = function () {
    574             log.add("Red --> for 1 minute");
    575             light.change(new Green(light));
    576         }
    577     };
    578 
    579     var Yellow = function (light) {
    580         this.light = light;
    581 
    582         this.go = function () {
    583             log.add("Yellow --> for 10 seconds");
    584             light.change(new Red(light));
    585         }
    586     };
    587 
    588     var Green = function (light) {
    589         this.light = light;
    590 
    591         this.go = function () {
    592             log.add("Green --> for 1 minute");
    593             light.change(new Yellow(light));
    594         }
    595     };
    596 
    597     // log helper
    598 
    599     var log = (function () {
    600         var log = "";
    601         return {
    602             add: function (msg) { log += msg + "
    "; },
    603             show: function () {
    604                 alert(log);
    605                 log = "";
    606             }
    607         }
    608     })();
    609 
    610     function run() {
    611 
    612         var light = new TrafficLight();
    613         light.start();
    614 
    615         log.show();
    616     }
    617 }());
    618 </script>
    619 </body>
    620 </html>
  • 相关阅读:
    IOC和DI的区别
    hdu 1217(Floyed)
    hdu 2112(字典树+最短路)
    hdu 4081(次小生成树)
    hdu 1811(缩点+拓扑排序+并查集)
    poj 3026(BFS+最小生成树)
    hdu 3635(并查集)
    hdu 3047(扩展并查集)
    hdu 1116(并查集+欧拉路径)
    poj 1679(次小生成树)
  • 原文地址:https://www.cnblogs.com/webFrontDev/p/3641937.html
Copyright © 2011-2022 走看看