Skip to main content
 首页 » 编程设计

javascript中介者模式(Mediator)

2022年07月16日30yxwkf
  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 + "\n"; }, 
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>

本文参考链接:https://www.cnblogs.com/webFrontDev/p/3553400.html