Ruby によるデザインパターン 第5章の Observer を JavaScript で写経
変更に追従する: Observer
- 作者: Russ Olsen,ラス・オルセン,小林健一,菅野裕,吉野雅人,山岸夢人,小島努
- 出版社/メーカー: ピアソン桐原
- 発売日: 2009/04/01
- メディア: 単行本
- 購入: 13人 クリック: 220回
- この商品を含むブログ (66件) を見る
log, Array.forEach, Array.filter を追加。
function log(str) { (console) ? console.log(str) : alert(str); }; // https://developer.mozilla.org/ja/Core_JavaScript_1.5_Reference/Global_Objects/Array/forEach if (!Array.prototype.forEach) { Array.prototype.forEach = function(fun /*, thisp*/) { var len = this.length; if (typeof fun != "function") throw new TypeError(); var thisp = arguments[1]; for (var i = 0; i < len; i++) { if (i in this) fun.call(thisp, this[i], i, this); } }; } // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/filter if (!Array.prototype.filter) { Array.prototype.filter = function(fun /*, thisp*/) { var len = this.length >>> 0; if (typeof fun != "function") throw new TypeError(); var res = new Array(); var thisp = arguments[1]; for (var i = 0; i < len; i++) { if (i in this) { var val = this[i]; // in case fun mutates this if (fun.call(thisp, val, i, this)) res.push(val); } } return res; }; }
通知を受ける
では、給料の変更を経理部門 (Payroll) に逐次通知し続ける、ばか正直なコードを追加してみましょう。
var Employee = function () { this.initialize.apply(this, arguments) }; var Payroll = function () {}; Payroll.prototype = { update: function(changed_employee) { log(changed_employee.name + 'のために小切手を送ります'); log('彼の給料は今' + changed_employee.salaly + 'です'); } } Employee.prototype = { initialize: function(name, title, salaly, payroll) { this.name = name; this.title = title; this.salaly = salaly; this.payroll = payroll; }, setSalaly: function(new_salary) { this.salaly = new_salary; this.payroll.update(this); } } var payroll = new Payroll(); var fred = new Employee('Fred', 'Crane Oparator', 30000, payroll); fred.setSalaly(35000);
経理部門に給料の変更を伝える必要があるため、salary フィールドに対しては、attr_accessor を
使用できないことに注意して下さい。代わりに salary= メソッドを書く必要があります。
なるほど、と思ったけど、JavaScript ではどう書けばいいんだろう?
普通にメソッドとして書いた。public メンバーとか private メンバーとか厳密じゃない><
通知を受けるよりよい方法
このコードの問題は、経理部門への給与の変更通知をハードコーディングしている点です。
変化する事柄(誰が給与の変更というニュースを受け取るか)を
Employee オブジェクトの本質から分離するにはどうすればよいのでしょう?
var Employee = function () { this.initialize.apply(this, arguments) }; var Payroll = function () {}; var Taxman = function () {}; Payroll.prototype = { update: function(changed_employee) { log(changed_employee.name + 'のために小切手を送ります'); log('彼の給料は今' + changed_employee.salaly + 'です'); } } Taxman.prototype = { update: function(changed_employee) { log(changed_employee.name + 'に新しい請求書を送ります。'); } } Employee.prototype = { initialize: function(name, title, salaly) { this.name = name; this.title = title; this.salaly = function() { return salaly }; this.observers = []; }, setSalaly: function(new_salary) { this.salaly = new_salary; this.notifyObservers(); }, notifyObservers: function() { this.observers.forEach(function(observer) { observer.update(this); }, this); }, addObservers: function(observer) { this.observers.push(observer); }, deleteObserver: function(target) { this.observers = this.observers.filter(function(observer) { return (observer !== target); }, this); } } var fred = new Employee('Fred', 'Crane Oparator', 30000); var payroll = new Payroll(); var taxman = new Taxman(); fred.addObservers(payroll); fred.addObservers(taxman); fred.setSalaly(35000);
GOF は「何らかのオブジェクトが変化した」というニュースの発信者と消費者の間にきれいなインターフェースを
作るこのようなアイディアを、Observer パターンと呼んでいます。 GOF はニュースを持っているクラスを
サブジェクト(Subject、話題となっている事柄) クラスと呼んでいます。
このあとはオブザーバに対する責務を分離しながら、継承、モジュール化、
Ruby の標準モジュールであるObservable モジュールの紹介、そしてまたコードブロック
みたいな流れで進んでいく。とりあえず今日はここまで。