• javascript设计模式--中介者模式(Mediator)


      1 <!doctype html>
      2 <html lang="en">
      3 <head>
      4     <meta charset="UTF-8">
      5     <title>中介者模式</title>
      6 </head>
      7 <body>
      8 <script>    
      9 function extend(subclass, superclass) {
     10     var F = function () {
     11     };
     12     F.prototype = superclass.prototype;
     13     subclass.prototype = new F();
     14     subclass.prototype.constructor = subclass;
     15     subclass.super = superclass.prototype;
     16 
     17     if (superclass.prototype.constructor === Object.prototype.constructor) {
     18         superclass.prototype.constructor = superclass;
     19     }
     20 }
     21 
     22 function override(targetObj, obj, deep) {
     23     if (Object.prototype.toString.call(obj) !== '[object Object]') {
     24         return;
     25     }
     26     for (var i in obj) {
     27         if (obj.hasOwnProperty(i)) {
     28             if (deep === true) {
     29                 targetObj[i] = targetObj[i] || {};
     30                 rewrite(targetObj[i], obj[i], deep);
     31             } else {
     32                 targetObj[i] = obj[i];
     33             }
     34         }
     35     }
     36 }
     37 </script>
     38 <script>
     39 /**
     40  * 中介者模式
     41  *
     42  * 定义:
     43  * 用一个中介对象来封装一系列的对象交互。中介者使得各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
     44  *
     45  * 本质:封装交互
     46  *
     47  * 中介者模式的解决私立很简单,它通过引入一个中介对象,让其他的对象都只和中介对象交互,而中介对象知道如何和其他所有的对象交互,这样对象之间的交互关系就没有了,从而实现了对象之间的解耦。
     48  * 对于中介对象而言,所有的相互交互的对象,被视为同事类,中介者对象就是来维护各个同事之间的关系,而所有的同事类都只是和中介对象交互。
     49  * 每个同事对象,当自己发生变化的时候,不需要知道这会引起其他对象有什么变化,它只需要通知中介者就可以了,然后由中介者去与其他对象交互。这样松散耦合带来的好处是,除了让同事对象之间相互没有关联外,还有利于功能的修改和扩展。
     50  * 有了中介者之后,所有的交互都封装到中介者对象里面,各个对象就不再需要维护这些关系了。扩展关系的时候也只需要扩展或修改中介者对象就可以了。
     51  * 
     52  *
     53  * 同事关系
     54  * 在标准的中介者模式中,将使用中介者对象来交互的那些对象称为同事类,在中介者模式中,要求这些类都要继承相同的类。也就是说,这些对象从某个角度讲是同一个类型,算是兄弟对象。
     55  * 正是这些兄弟对象之间的交互关系很复杂,才产生了把这些交互关系分离出来,单独做成中介者对象。
     56  *
     57  * 同事和中介者的关系
     58  * 在中介者模式中,当一个同事对象发生了改变,需要主动通知中介者,让中介者去处理与其他同事对象相关的交互。
     59  * 这就导致了同事对象和中介者对象之间必须有关系,首先是同事对象需要知道中介者对象是谁;反过来,中介者对象也需要知道相关的同事对象,这样它才能与同事对象进行交互。也就是说中介者对象和同事对象之间是相互依赖的。
     60  *
     61  * 如何实现同事和中介者的通信
     62  * 一个同事对象发生了改变,会通知中介者对象,中介者对象会处理与其他同事的交互,这就产生了同事对象和中介者对象的相互通信。
     63  * 一个实现方式是在Mediator接口中定义一个特殊的通知接口,作为一个通用的方法,让各个同事类来调用这个方法,在中介者模式结构图里画的就是这种方式。例如定义了一个通用的changed方法,并且把同事对象当作参数传入,这样在中介者对象里面,就可以去获取这个同事对象的实例数据了。
     64  * 另外一种实现方式是可以采用观察者模式,把Mediator实现成为观察者,而各个同事类实现成为Subject,这样同事类发生了改变,会通知Mediator。Mediator在接到通知以后,会于相应的同事对象进行交互。
     65  *
     66  */
     67 
     68 // 示例代码
     69 
     70 (function(){
     71     function Colleague(mediator){
     72     this.mediator = mediator;
     73 }
     74 Colleague.prototype = {
     75     getMediator: function(){
     76         return this.mediator;
     77     }
     78 };
     79 
     80 // 同事类A
     81 function ColleagueA(mediator){
     82     ColleagueA.super.constructor.apply(this, arguments);
     83 }
     84 extend(ColleagueA, Colleague);
     85 ColleagueA.prototype.someOperation= function(){
     86     // some code..
     87      
     88     // 在需要跟其他同事通信的时候,通知中介者对象
     89     this.getMediator().changed(this);
     90 }
     91 
     92 function ColleagueB(mediator){
     93     ColleagueB.super.constructor.call(this);
     94 }
     95 extend(ColleagueB, Colleague);
     96 ColleagueB.prototype.someOperation= function(){
     97     // some code..
     98     
     99     // 在需要跟其他同事通信的时候,通知中介者对象
    100     this.getMediator().changed(this);
    101 };
    102 
    103 // 中介者
    104 function Mediator(){
    105     var colleagueA, colleagueB;
    106 
    107     // 设置中介者需要了解并维护的同事A对象
    108     this.setColleagueA = function(colleague){
    109         colleagueA = colleague;
    110     };
    111 
    112     // 设置中介者需要了解并维护的同事B对象
    113     this.setColleagueB = function(colleague){
    114         colleagueB = colleague;
    115     };
    116 
    117     this.changed = function(colleague){
    118         // 某个同事类发生了变化,通常需要与其他同事交互
    119         // 具体协调相应的同事对象来实现协作行为
    120     };
    121 }
    122 }());
    123 
    124 (function(){
    125     // 抽象同事类
    126     var Colleague = function(mediator){
    127         this.mediator = mediator;
    128     };
    129     Colleague.prototype = {
    130         getMediator: function(){
    131             return this.mediator;
    132         }
    133     };
    134 
    135     // 光驱类
    136     var CDDriver = function(){
    137         CDDriver.super.constructor.apply(this, arguments);
    138 
    139         this.data = '';
    140     };
    141     extend(CDDriver, Colleague);
    142     override(CDDriver.prototype, {
    143         getData: function(){
    144             return this.data;
    145         },
    146         readCD: function(){
    147             this.data = 'CDDriver Data, SoundCard Data';
    148             // 通知主板,自己的状态发生了变化
    149             this.getMediator().changed(this);
    150         }
    151     });
    152 
    153     // CPU类
    154     var CPU = function(){
    155         CPU.super.constructor.apply(this, arguments);
    156 
    157         this.videoData = '';
    158         this.soundData = '';
    159     };
    160     extend(CPU, Colleague);
    161     override(CPU.prototype, {
    162         // 获取分解出来的视频数据
    163         getVideoData: function(){
    164             return this.videoData;
    165         },
    166         // 获取分解出来的声音数据
    167         getSoundData: function(){
    168             return this.soundData;
    169         },
    170         executeData: function(data){
    171             var ss = data.split(',');
    172             this.videoData = ss[0];
    173             this.soundData = ss[1];
    174             // 通知主板,CPU的工作完成
    175             this.getMediator().changed(this);
    176         }
    177     });
    178 
    179     // 显卡类
    180     var VideoCard = function(){
    181         VideoCard.super.constructor.apply(this, arguments);
    182     };
    183     extend(VideoCard, Colleague);
    184     override(VideoCard.prototype, {
    185         showData: function(data){
    186             console.log('您正在观看的是:' + data);
    187         }
    188     });
    189 
    190     // 声卡类
    191     var SoundCard = function(){
    192         SoundCard.super.constructor.apply(this, arguments);
    193     };
    194     extend(SoundCard, Colleague);
    195     override(SoundCard.prototype, {
    196         soundData: function(data){
    197             console.log('画外音:' + data);
    198         }
    199     });
    200 
    201     // 中介对象接口
    202     var Mediator = function(){};
    203     Mediator.prototype = {
    204         changed: function(colleague){}
    205     };
    206 
    207     var MotherBoard = function(){
    208     };
    209     extend(MotherBoard, Mediator);
    210     override(MotherBoard.prototype, {
    211         setCdDriver: function(cdDriver){
    212             this.cdDriver = cdDriver;
    213         },
    214         setCpu: function(cpu){
    215             this.cpu = cpu;
    216         },
    217         setVideoCard: function(videoCard){
    218             this.videoCard = videoCard;
    219         },
    220         setSoundCard: function(soundCard){
    221             this.soundCard = soundCard;
    222         },
    223 
    224         changed: function(colleague){
    225             switch(colleague) {
    226                 case this.cdDriver:
    227                     this.opeCDDriverReadData(colleague);
    228                     break;
    229                 case this.cpu:
    230                     this.opeCPU(colleague);
    231                     break;
    232                 default:
    233                     break;
    234             }
    235         },
    236 
    237         opeCDDriverReadData: function(cd){
    238             this.cpu.executeData(cd.getData());
    239         },
    240         opeCPU: function(cpu){
    241             this.videoCard.showData(cpu.getVideoData());
    242             this.soundCard.soundData(cpu.getSoundData());
    243         }
    244     });
    245 
    246     void function run(){
    247         var mediator = new MotherBoard();
    248         var cd = new CDDriver(mediator);
    249         var cpu = new CPU(mediator);
    250         var vc = new VideoCard(mediator);
    251         var sc = new SoundCard(mediator);
    252 
    253         mediator.setCdDriver(cd);
    254         mediator.setCpu(cpu);
    255         mediator.setVideoCard(vc);
    256         mediator.setSoundCard(sc);
    257 
    258         cd.readCD();
    259     }();
    260 
    261 }());
    262 
    263 /**
    264  * 广义中介者
    265  *
    266  * 在实际开发中,经常会简化中介者模式,比如有如下简化:
    267  * 1.通常会去掉同事对象的父类,这样可以让人意的对象,只要需要相互交互,就可以成为同事。
    268  * 2.通常不定义Mediator接口,把具体的中介者对象实现成为单例。
    269  * 3.同事对象不再持有中介者,而是在需要的时候直接获取中介者对象并调用;中介者也不再持有同事对象,而是在具体处理方法里面去创建,或者获取,或者从参数传入需要的同事对象。
    270  */
    271 
    272 // 部门与人员的交互
    273 (function(){
    274     // 部门类
    275     var Dep = function(){
    276         // 描述部门编号
    277         this.depId = '';
    278         // 描述部门名称
    279         this.depName = '';
    280     };
    281     Dep.prototype = {
    282         getDepId: function(){
    283             return this.depId;
    284         },
    285         setDepId: function(depId){
    286             this.depId = depId;
    287         },
    288         getDepName: function(){
    289             return this.depName;
    290         },
    291         setDepName: function(depName){
    292             this.depName = depName;
    293         },
    294         // 撤销部门
    295         deleteDep: function(){
    296             // 要先通过中介者去除掉所有与这个部门相关的部门和人员的关系。
    297             var mediator = DepUserMediatorImpl.getInstance();
    298             mediator.deleteDep(this.depId);
    299 
    300             // 然后才能真正地清除掉这个部门
    301             // 在实际开发中,这些业务功能可能会做到业务层去
    302             // 而且实际开发中对于已经使用的业务数据通常不会被删除
    303             // 而是会被作为历史数据保留
    304             return true;
    305         }
    306     };
    307 
    308     // 人员类
    309     var User = function(){
    310         // 人员编号
    311         this.userId = '';
    312         // 人员名称
    313         this.userName = '';
    314     };
    315     User.prototype = {
    316         getUserId: function(){
    317             return this.userId;
    318         },
    319         setUserId: function(userId){
    320             this.userId = userId;
    321         },
    322         getUserName: function(){
    323             return this.userName;
    324         },
    325         setUserName: function(userName){
    326             this.userName = userName;
    327         },
    328         // 人员离职
    329         dimission: function(){
    330             var mediator = DepUserMediatorImpl.getInstance();
    331             mediator.deleteUser(this.userId);
    332 
    333             return true;
    334         }
    335     };
    336 
    337     // 描述部门和人员关系的类
    338     var DepUserModel = function(){
    339         // 用于部门和人员关系的编号,用作主键
    340         this.depUserId = '';
    341         this.depId = '';
    342         this.userId = '';
    343     };
    344     DepUserModel.prototype = {
    345         setDepUserId: function(depUserId){
    346             this.depUserId = depUserId;
    347         },
    348         getDepUserId: function(){
    349             return this.depUserId;
    350         },
    351         setDepId: function(depId){
    352             this.depId = depId;
    353         },
    354         getDepId: function(){
    355             return this.depId;
    356         },
    357         setUserId: function(userId){
    358             this.userId = userId;
    359         },
    360         getUserId: function(){
    361             return this.userId;
    362         }
    363     };
    364 
    365     // 中介者对象
    366     var DepUserMediatorImpl = function(){
    367         // 记录部门和人员的关系
    368         this.depUserCol = [];
    369         this.initTestData();
    370     };
    371     DepUserMediatorImpl.getInstance = function(){
    372         if(!(DepUserMediatorImpl.instance instanceof DepUserMediatorImpl)) {
    373             DepUserMediatorImpl.instance = new DepUserMediatorImpl();
    374         }
    375         return DepUserMediatorImpl.instance;
    376     };
    377     DepUserMediatorImpl.prototype = {
    378         // 初始化测试数据
    379         initTestData: function(){
    380             var du1 = new DepUserModel();
    381             du1.setDepUserId('du1');
    382             du1.setDepId('d1');
    383             du1.setUserId('u1');
    384             this.depUserCol.push(du1);
    385 
    386             var du2 = new DepUserModel();
    387             du2.setDepUserId('du2');
    388             du2.setDepId('d1');
    389             du2.setUserId('u2');
    390             this.depUserCol.push(du2);
    391 
    392             var du3 = new DepUserModel();
    393             du3.setDepUserId('du3');
    394             du3.setDepId('d2');
    395             du3.setUserId('u3');
    396             this.depUserCol.push(du3);
    397 
    398             var du4 = new DepUserModel();
    399             du4.setDepUserId('du4');
    400             du4.setDepId('d2');
    401             du4.setUserId('u4');
    402             this.depUserCol.push(du4);
    403 
    404             var du5 = new DepUserModel();
    405             du5.setDepUserId('du5');
    406             du5.setDepId('d2');
    407             du5.setUserId('u1');
    408             this.depUserCol.push(du5);
    409         },
    410         // 完成因撤销部门的操作所引起的与人员的交互,需要去除相应的关系
    411         deleteDep: function(depId){
    412             for(var i = 0; i < this.depUserCol.length; i++){
    413                 if(this.depUserCol[i].depId === depId){
    414                     this.depUserCol.splice(i--, 1);
    415                 }
    416             }
    417 
    418             return true;
    419         },
    420         // 完成因人员离职引起的与部门的交互
    421         deleteUser: function(userId){
    422             for(var i = 0; i < this.depUserCol.length; i++){
    423                 if(this.depUserCol[i].userId === userId){
    424                     this.depUserCol.splice(i--, 1);
    425                 }
    426             }
    427 
    428             return true;
    429         },
    430         // 显示一个部门想啊的所有人员
    431         showDepUsers: function(dep){
    432             var du;
    433             for(var i = 0, len = this.depUserCol.length; i < len; i++){
    434                 du = this.depUserCol[i];
    435                 if(du.depId === dep.depId) {
    436                     console.log('部门编号=' + dep.depId + '下面拥有的人员,其编号是:' + du.userId);
    437                 }
    438             }
    439         },
    440         // 显示一个人员所属的部门
    441         showUserDeps: function(user){
    442             var du;
    443             for(var i = 0, len = this.depUserCol.length; i < len; i++){
    444                 du = this.depUserCol[i];
    445                 if(du.userId === user.userId) {
    446                     console.log('人员编号=' + user.userId + '属于部门编号是' + du.depId);
    447                 }
    448             }
    449         },
    450         cjageDep: function(){
    451             // ..省略
    452             return false;
    453         },
    454         joinDep: function(colDepIds, newDep){
    455             // ...省略
    456             return false;
    457         }
    458     };
    459 
    460     var mediator = DepUserMediatorImpl.getInstance();
    461     var dep = new Dep();
    462     dep.setDepId('d1');
    463     var dep2 = new Dep();
    464     dep2.setDepId('d2');
    465     var user = new User();
    466     user.setUserId('u1');
    467 
    468     console.log('撤销部门前---------------------');
    469     mediator.showUserDeps(user);
    470     dep.deleteDep();
    471     console.log('撤销部门后---------------------');
    472     mediator.showUserDeps(user);
    473 
    474     console.log('-----------人员离职前-----------');
    475     mediator.showDepUsers(dep2);
    476     user.dimission();
    477     console.log('----------人员离职后------------');
    478     mediator.showDepUsers(dep2);
    479 
    480 }());
    481 
    482 /**
    483  * 中介者模式的优点:
    484  * 1.松散耦合
    485  * 中介者模式用过把多个同事对象之间的交互封装到中介者对象里面,从而使得同事对象之间松散耦合,基本上可以做到互不依赖。这样一来,同事对象就可以独立变化和复用,从而不再像以前那样“牵一发而动全身”。
    486  *
    487  * 2.集中控制交互
    488  * 多个同事对象的交互,被封装在中介者对象里面集中管理,使得这些交互行为发生变化的时候,只需要修改中介者对象就可以了,当然如果是已经做好的系统,那就扩展中介者对象,而各个同事类不需要做修改。
    489  *
    490  * 3.多对多变成一对多
    491  * 没有使用中介者模式的时候,同事对象之间的关系通常是多对多的,引入中介者对象之后,中介者对象和同事对象的关系通常变成了双向的一对多,这会让对象的关系更让哦难以理解和实现。
    492  *
    493  *
    494  * 中介者模式的缺点:
    495  * 1.过度集中化
    496  * 如果同事对象的交互非常多,而且比较复杂,当这些复杂性全部集中到中介者的时候,会导致中介者对象变得十分复杂,而且难于管理和维护。
    497  *
    498  * 何时选用中介者模式
    499  * 
    500  * 1.如果一组对象之间的通信比较复杂,导致相互依赖,结构混乱,可以采用中介者模式,吧这些对象相互的交互管理起来,各个对象都只需要和中介者交互,从而使得各个对象松散耦合,结构也更清晰易懂。
    501  *
    502  * 2.如果一个对象引用很多对象,并直接跟这些对象交互,导致难以复用该对象,可以采用中介者模式,把这个对象跟其他对象的交互封装到中介者对象里面,这个对象只需要和中介者对象交互就可以了。
    503  *
    504  * 相关模式
    505  *
    506  * 中介者模式和外观模式
    507  * 这两个模式有相似的地方,也存在很大的不同。
    508  * 外观模式多用来封装一个子系统内部的多个模块,目的是想子系统外部提供简单易用的接口。也就是说外观模式封装的是子系统外部和子系统内部模块间的交互;而中介者模式是提供多个平等的同事对象之间交互关系的封装,一般是用在内部实现上。
    509  * 另外,外观模式是实现单向的交互,是从子系统外部来调用子系统内部,不会反着来;而中介者模式实现的是内部多个模块间多向的交互。
    510  *
    511  * 中介者模式和观察者模式
    512  * 这两个模式可以组合使用。
    513  * 中介者模式可以组合使用观察者模式,来实现当同事对象发生改变的时候,通知中介者对象,让中介对象去进行与其他相关对象的交互。
    514  */
    515 
    516 // example
    517 /* Title: Mediator
    518  Description: allows loose coupling between classes by being the only class that has detailed knowledge of their methods
    519  */
    520 
    521 (function(){
    522     function Player(name) {
    523     this.points = 0;
    524     this.name = name;
    525 }
    526 Player.prototype.play = function () {
    527     this.points += 1;
    528     mediator.played();
    529 };
    530 var scoreboard = {
    531 
    532     // HTML element to be updated
    533     element:document.getElementById('results'),
    534 
    535     // update the score display
    536     update:function (score) {
    537         var i, msg = '';
    538         for (i in score) {
    539             if (score.hasOwnProperty(i)) {
    540                 msg += '<p><strong>' + i + '</strong>: ';
    541                 msg += score[i];
    542                 msg += '</p>';
    543             }
    544         }
    545         this.element.innerHTML = msg;
    546     }
    547 };
    548 
    549 var mediator = {
    550 
    551     // all the player
    552     players:{},
    553 
    554     // initialization
    555     setup:function () {
    556         var players = this.players;
    557         players.home = new Player('Home');
    558         players.guest = new Player('Guest');
    559     },
    560 
    561     // someone plays, update the score
    562     played:function () {
    563         var players = this.players,
    564             score = {
    565                 Home:players.home.points,
    566                 Guest:players.guest.points
    567             };
    568 
    569         scoreboard.update(score);
    570     },
    571 
    572     // handle user interactions
    573     keypress:function (e) {
    574         e = e || window.event; // IE
    575         if (e.which === 49) { // key "1"
    576             mediator.players.home.play();
    577             return;
    578         }
    579         if (e.which === 48) { // key "0"
    580             mediator.players.guest.play();
    581             return;
    582         }
    583     }
    584 };
    585 
    586 // go!
    587 mediator.setup();
    588 window.onkeypress = mediator.keypress;
    589 
    590 // game over in 30 seconds
    591 setTimeout(function () {
    592     window.onkeypress = null;
    593     console.log('Game over!');
    594 }, 30000);
    595 }());
    596 
    597 
    598 
    599 // http://www.dofactory.com/javascript-mediator-pattern.aspx
    600 
    601 (function(){
    602     var Participant = function(name) {
    603     this.name = name;
    604     this.chatroom = null;
    605 };
    606 
    607 Participant.prototype = {
    608     send: function(message, to) {
    609         this.chatroom.send(message, this, to);
    610     },
    611     receive: function(message, from) {
    612         log.add(from.name + " to " + this.name + ": " + message);
    613     }
    614 };
    615 
    616 var Chatroom = function() {
    617     var participants = {};
    618     return {
    619         register: function(participant) {
    620             participants[participant.name] = participant;
    621             participant.chatroom = this;
    622         },
    623         send: function(message, from, to) {
    624             if (to) {                      // single message
    625                 to.receive(message, from);    
    626             } else {                       // broadcast message
    627                 for (key in participants) {   
    628                     if (participants[key] !== from) {
    629                         participants[key].receive(message, from);
    630                     }
    631                 }
    632             }
    633         }
    634     };
    635 };
    636 
    637 // log helper
    638 var log = (function() {
    639     var log = "";
    640     return {
    641         add: function(msg) { log += msg + "
    "; },
    642         show: function() { alert(log); log = ""; }
    643     }
    644 })();
    645 
    646 
    647 function run() {
    648 
    649     var yoko = new Participant("Yoko");
    650     var john = new Participant("John");
    651     var paul = new Participant("Paul");
    652     var ringo = new Participant("Ringo");
    653 
    654     var chatroom = new Chatroom();
    655     chatroom.register(yoko);
    656     chatroom.register(john);
    657     chatroom.register(paul);
    658     chatroom.register(ringo);
    659 
    660     yoko.send("All you need is love.");
    661     yoko.send("I love you John.");
    662     john.send("Hey, no need to broadcast", yoko);
    663     paul.send("Ha, I heard that!");
    664     ringo.send("Paul, what do you think?", paul);
    665 
    666     log.show();
    667 }
    668 }());
    669 
    670 </script>
    671 </body>
    672 </html>
  • 相关阅读:
    Kaggle网站流量预测任务第一名解决方案:从模型到代码详解时序预测
    点击率预估
    论文列表——text classification
    retrofit+RXjava二次封装
    Twitter Lite以及大规模的高性能React渐进式网络应用
    《设计模式》结构型模式
    maven多module项目中千万不要引入其它模块的单元測试代码
    Jenkins配置基于角色的项目权限管理
    读《百度基础架构技术发展之路》有感
    <html>
  • 原文地址:https://www.cnblogs.com/webFrontDev/p/3553400.html
Copyright © 2020-2023  润新知