자바스크립트 Mediator(중재자) Pattern 과 Facade Pattern 구현


Mediator(중재자) Pattern

객체 설계 시 최대한 객체간의 결합도를 낮춰, 필요 시 유지보수가 용이한 형태로 설계되야 한다.

즉, 객체간의 결합도가 높아지면 높아질수록, 유지보수(수정 비용) 비용 또한 올라간다. 중재자 패턴은 이런 문제를 완하하기 위한 디자인 패턴이며, 아래 코드의 독립된 객체(user1 ~ user4)들은 자신의 상태(score)가 변경되면, 이를 중재자에게 알리고(Mediator.play()), 점수판 객체(Score)의 update 메서드를 호출해 모든 플레이어들이게 변경사항을 통지한다.


- 점수판 객체(Score)는 어떤 유저에 관한 정보도 일체 알지 못하며, 어떤 정보도 저장하지 않는다.


  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3. <head id="Head1" runat="server">
  4. <title></title>
  5. <script type="text/javascript">
  6. //<![CDATA[
  7.  
  8.  
  9. // Mediator(중재자) Pattern
  10.  
  11. var Mediator = new (function () {
  12.  
  13. var Mediator = function () {
  14. this.mediators = [];
  15. return this;
  16. }
  17.  
  18. Mediator.fn = Mediator.prototype = {
  19.  
  20. make: function (user) {
  21.  
  22. if (!user) return false;
  23.  
  24. this.mediators.push({ user: user });
  25.  
  26. return this;
  27. },
  28. remove: function (user) {
  29.  
  30. for (var t in this.mediators) {
  31. if (user) {
  32. if (this.mediators[t].user === user) {
  33. delete this.mediators[t];
  34. }
  35. }
  36. else {
  37. delete this.mediators[t];
  38. }
  39. }
  40.  
  41. return this;
  42. },
  43. play: function () {
  44.  
  45. var names = []
  46. , scores = [];
  47.  
  48. for (var n in this.mediators) {
  49. names.push(this.mediators[n].user.name);
  50. scores.push(this.mediators[n].user.score);
  51. }
  52.  
  53. var score = new Score();
  54. score.update.apply(score, [names, scores]);
  55. }
  56. }
  57.  
  58.  
  59. return Mediator;
  60.  
  61. } ())();
  62.  
  63. function Users(name) {
  64.  
  65. this.name = name;
  66. this.score = 0;
  67.  
  68. // 플레이어 목록 추가
  69. Mediator.make(this);
  70.  
  71. return this;
  72. }
  73.  
  74. Users.prototype.play = function () {
  75.  
  76. this.score++;
  77. // boardcast call
  78. Mediator.play();
  79.  
  80. return false;
  81. }
  82.  
  83. function Score() {
  84. return this;
  85. }
  86.  
  87. Score.prototype.update = function (names, scores) {
  88.  
  89. var elem = document.getElementById('userScoreBoardList')
  90. , h = [];
  91.  
  92. for (var t in names) {
  93. h.push('<li>' + names[t] + '님의 점수는 ' + scores[t] + '</li>');
  94. }
  95.  
  96. elem.innerHTML = h.join('');
  97.  
  98. return this;
  99. }
  100.  
  101. window.onload = function () {
  102. user1 = new Users('user1');
  103. user2 = new Users('user2');
  104. user3 = new Users('user3');
  105. user4 = new Users('user4');
  106.  
  107. //Mediator.remove(user4);
  108. }
  109.  
  110. //]]>
  111. </script>
  112. </head>
  113. <body>
  114. <a href="#" onclick="return user1.play()">user1</a>
  115. <a href="#" onclick="return user2.play()">user2</a>
  116. <a href="#" onclick="return user3.play()">user3</a>
  117. <a href="#" onclick="return user4.play()">user4</a>
  118. <ul id="userScoreBoardList"></ul>
  119. </body>
  120.  
  121. </html>





Facade Pattern

사용되는 빈도가 높은 메서드들을 하나로 감싸 새로운 메서드를 만들어 좀 더 편리한 API제공하는 디자인 패턴이다.

가장 대표적인 예로 아래와 코드에서 처럼 크로스 브라우징을 위한 DOM Event 할당 함수(addEventListener, attachEvent)를 꼽을 수 있다.


  1. var Facade = new (function () {
  2.  
  3. var Facade = function () {
  4. return this;
  5. }
  6.  
  7. Facade.fn = Facade.prototype = {
  8.  
  9. bind: function (elem, type, handler, capture) {
  10.  
  11. type = typeof type === 'string' ? type : '';
  12. handler = typeof handler === 'function' ? handler : function () { ; };
  13. capture = capture || false;
  14.  
  15. if (elem.addEventListener) {
  16. elem.addEventListener(type, handler, capture);
  17. }
  18. else if (elem.attachEvent) {
  19. elem.attachEvent('on' + type, handler);
  20. }
  21.  
  22. return this;
  23. },
  24. unbind: function (elem, type, handler, capture) {
  25.  
  26. type = typeof type === 'string' ? type : '';
  27. handler = typeof handler === 'function' ? handler : function () { ; };
  28. capture = capture || false;
  29.  
  30. if (elem.removeEventListener) {
  31. elem.removeEventListener(type, handler, capture);
  32. }
  33. else if (elem.detachEvent) {
  34. elem.detachEvent('on' + type, handler);
  35. }
  36.  
  37. return this;
  38. }
  39. }
  40.  
  41.  
  42. return Facade;
  43.  
  44. } ())();
  45.  
  46. window.onload = function () {
  47.  
  48. var btn1 = document.getElementById('btn1');
  49. var btn1_callback = function () { alert('btn1'); return false; };
  50.  
  51. var btn2 = document.getElementById('btn2');
  52. var btn2_callback = function () { alert('btn2'); return false; };
  53.  
  54. Facade.bind(btn1, 'click', btn1_callback).unbind(btn1, 'click', btn1_callback);
  55. Facade.bind(btn2, 'click', btn2_callback);
  56. }