概要
Javaの場合だと、抽象クラスを書く場合には”abstract”宣言をして、具体のクラスで実装すべきメソッドの宣言だけを行うが、CoffeeScript/Javascriptの場合はこれと異なる。
準備
まず、同じ名前execution
のメソッドを持つ2つのクラスConcrete1
とConcrete2
を考える。メソッド名は同じだが、各クラスで処理内容は異なる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Concrete1 execution: -> console.log("execution in Concrete 1") class Concrete2 execution: -> console.log("execution in Concrete 2") ins = new Concrete1() ins.execution() ins = new Concrete2() ins.execution() # 実行結果 # execution in Concrete 1 # execution in Concrete 2 |
次にexecution()
について共通な2つのクラスを抽象化したAbstract
クラスを導入する。
抽象クラスの導入
CoffeeScript/Javascriptではabstract
キーワードがなく、abstractなメソッドも中身が空のもので定義。ただし抽象クラスのままでインスタンス化したときの注意喚起のメッセージを実装。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class Abstract execution: -> console.log("execution of Abstrac must be overridden") class Concrete1 execution: -> console.log("execution in Concrete 1") class Concrete2 execution: -> console.log("execution in Concrete 2") ins = new Abstract() ins.execution() ins = new Concrete1() ins.execution() ins = new Concrete2() ins.execution() # 実行結果 # execution of Abstrac must be overridden # execution in Concrete 1 # execution in Concrete 2 |
Javaなどでは型指定が厳格なので、異なる2つのクラスをAbstract
クラスで抽象化して、同じメソッドを呼び出すことでクラスごとに特有の処理をさせる。
CoffeeScript/Javascriptでは変数の型がないため、抽象化をしなくてもメソッド名を同じにしておくだけで同じ効果を得る。ただしコンパイル時のチェックなどがない点に注意。
共通部分がある場合
以下のように、2つのクラスで共通の処理と個別の処理が混在している場合を考える。
共通の処理を各クラスごとに実装しているが、コピー/ペースト操作で行うとしてもエラーが入り込みやすく、その後の保守性も悪い。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
class Concrete1 execution: -> @commonExecution() @specificExecution() commonExecution: -> console.log("common execution") specificExecution: -> console.log("execution for Concrete 1") class Concrete2 execution: -> @commonExecution() @specificExecution() commonExecution: -> console.log("opening execution") specificExecution: -> console.log("execution for Concrete 2") ins = new Concrete1() ins.execution() ins = new Concrete2() ins.execution() # 実行結果 # common execution # execution for Concrete 1 # opening execution # execution for Concrete 2 |
共通部分の集約(1)
エラー回避と保守性の向上のため、共通部分を一つにまとめる。
- 抽象クラス
Abstract
を導入 Abstract
にexecution()
メソッドのフレームを集約- 共通処理部分
commonExecution()
をAbstract
のメソッドとして実装 - 2つのクラスで
Abstract
を継承 - これらの子クラスで、それぞれの個別処理部分のみを実装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
class Abstract execution: -> @commonExecution() @specificExecution() commonExecution: -> console.log("common execution") class Concrete1 extends Abstract specificExecution: -> console.log("execution for Concrete 1") class Concrete2 extends Abstract specificExecution: -> console.log("execution for Concrete 2") ins = new Concrete1() ins.execution() ins = new Concrete2() ins.execution() # 実行結果 # common execution # execution for Concrete 1 # opening execution # execution for Concrete 2 |
Concrete1
とConcrete2
がAbstract
を継承したことによって2つの具象クラスがAbstract
クラスのexecute()
メソッドも継承され、具象クラスでexecute()
メソッドを呼ぶと、Abstract
のexecute()
メソッドが実行される。
その後、共通部分のcommonExecution()
はAbstract
のメソッドが呼ばれ、specificExecution()
については、各具象クラスでオーバーライドされたメソッドが呼ばれる。
共通部分の集約(2)
各具象クラスでexecution()
のから書いていくこともできるが、この場合は共通処理が一括してsuper()で実行されるため、「共通の前処理と後処理の間に各クラス独自の処理を置きたい」という場合には適さない。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
class Abstract execution: -> @commonExecution() @specificExecution() commonExecution: -> console.log("common execution") class Concrete1 extends Abstract execution: -> super() @specificExecution specificExecution: -> console.log("execution for Concrete 1") class Concrete2 extends Abstract execution: -> super() @specificExecution specificExecution: -> console.log("execution for Concrete 2") ins = new Concrete1() ins.execution() ins = new Concrete2() ins.execution() # 実行結果 # common execution # execution for Concrete 1 # opening execution # execution for Concrete 2 |