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 + "\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>

 

你可能感兴趣的:(JavaScript)