はじめに
ソフトウェア開発分野が拡大するにつれて、開発者は常に最新のテクノロジーをつかもうとしています。 幸いなことに、サポートされているコードを書く技術は言語に依存しません。この一連の投稿では、時代を超越した強力なツールセットであるデザインパターンに焦点を当てます。
Russ Olsenの本、RubyのDesign Patternsを強くお勧めします。 投稿のサイクルはそこからインスピレーションを引き出し、簡単な絞りのようなものになります。 したがって、あなたが読んだものが好きなら(そして私はそう願っています!)、この本は素晴らしい続編になります。
さまざまな設計パターンを検討し、それらを適用する方法を学びます。 今日のトピックは、最も単純なデザインパターンであるテンプレートメソッドです。
建設初日
適切なツール
簡単に言えば、設計パターンはソフトウェアの設計に役立つツールです。 それでも、他のツールと同様に、特定のタスクごとに適切なツールを選択できる必要があります。 もちろん、ハンマーでネジを打つこともできますが、おそらくドライバーを使用するほうが適切です。 多くのデザインパターンのいずれかを使用する前に、解決しようとしている問題を理解することが非常に重要です。
設計パターンを使用して、意図しない問題を解決するのは正しくありません。 言い換えれば、解決するために前述の設計パターンを必要としないタスクにパターンを使用することは、悪い形と見なされます。
壁を作りましょう
今日、私たちの職長は私たちにいくつかの壁を作るように言った。 すべての壁は同じサイズで、同じ素材で作られています(この設計プロジェクトでは、職長から非常に簡単な要件が提示されました)。
# (Wall) require 'minitest/autorun' describe Wall do let(:wall) { Wall.new } it 'should state its dimensions' do wall.dimensions.must_equal 'I am 30ft. long and 20ft. wide!' end it 'should be made from brick' do wall.made_from.must_equal 'I am made from brick!' end end
なんて良いボスなのか、彼は私たちに青写真を与えてくれました! 小さいので、壁を作りましょう。
class Wall def dimensions 'I am 30ft. long and 20ft. wide!' end def made_from 'I am made from brick!' end end
いいね! 私たちのテストに合格し、みんなが幸せで、ついに夕食に行きます!
ハンマーまたはネイルガン?
私たちが戻ったとき、フォアマンはもっと壁が必要だと言った。 「ここにケーキがあります」と私たちは言った。壁を作るのがどれほど簡単だったかを思い出した。
「みんな、そんなに速くないよ」と監督は異議を唱えた。 新しい壁の要件を持つ新しい設計図があります。
# (BrickWall) describe BrickWall do let(:brick_wall) { BrickWall.new } it 'should state its dimensions' do brick_wall.dimensions.must_equal 'I am 30ft. long and 20ft. wide!' end it 'should be made from brick' do brick_wall.made_from.must_equal 'I am made from brick!' end end # (ConcreteWall) describe ConcreteWall do let(:concrete_wall) { ConcreteWall.new } it 'should state its dimensions' do concrete_wall.dimensions.must_equal 'I am 30ft. long and 20ft. wide!' end it 'should be made from concrete' do concrete_wall.made_from.must_equal 'I am made from concrete!' end end # (WoodWall) describe WoodWall do let(:wood_wall) { WoodWall.new } it 'should state its dimensions' do wood_wall.dimensions.must_equal 'I am 10ft. long and 20ft. wide!' end it 'should be made from wood' do wood_wall.made_from.must_equal 'I am made from wood!' end end
うーん...いくつかのアイデアが頭に浮かびました。 Wallクラスの原則に従い、
WoodWall
、
ConcreteWall
WoodWall
、
WoodWall
ハードコードされた出力文字列を使用して各メソッドを定義
WoodWall
。 考え方は悪くないと思われますが、各インスタンスメソッドをハードコーディングする必要があります。 家庭に数十種類の壁が必要な場合はどうなりますか?
あそこの箱を開けて!
午後のコーヒーを磨きながら、タスクに適したツールであるPatternメソッドパターンがあることに気付きました。
パターンメソッドに続いて、 スケルトンクラス ( 骨格クラス)を作成すると、 サブクラスまたは具象クラスの基盤が構築されます。 スケルトンクラスには抽象メソッドが付属しており、これはサブクラスでオーバーライドできます。 つまり、
Wall
クラス(スケルトンクラス)とそのサブクラス
BrickWall
、
ConcreteWall
、
WoodWall
ます。
図面を確認した後、3つの異なる壁クラスすべてに、わずかに異なる行を返す
#dimensions
および
#made_from
が含まれていることに気付きました。 これを念頭に置いて、wallクラスとそのサブクラスを実装しましょう。
class Wall def dimensions "I am #{length}ft. long and #{width}ft. wide!" end def made_from "I am made from #{material}!" end private def length 30 end end class BrickWall < Wall private def width 20 end def material 'brick' end end class ConcreteWall < Wall private def width 20 end def material 'concrete' end end class WoodWall < Wall private def length 10 end def width 20 end def material 'wood' end end
議論
フックメソッド
Wall
クラスでは、
BrickWall
と
ConcreteWall
長さが同じであることがわかるため、プライベートメソッド
#length
れています。
WoodWall
クラスについては、値
10
返すように
#length
を再定義しただけです。 これは、フックメソッドの例です。
フックメソッドは2つの目的に使用されます。
1)骨格実装をオーバーライドし、新しい何かを実装する
2)またはデフォルトの実装を使用します。
スケルトンクラスのデフォルトの実装を定義する必要はありません。 たとえば、次のようにします。
class Wall ... private def length raise NotImplementedError, 'Sorry, you have to override length' end end class BrickWall < Wall private ... def length 30 end end
(約-これはルビーのベストプラクティスではありませんが、詳細はこちら 、「継承を必要としない」セクション)
上記の例では、
Wall
クラスの
#length
メソッドは、具体的なクラスである
BrickWall
スタブとして
#lenght
れています。 本質的に、フックメソッドは、指定されたメソッドをオーバーライドする必要があることをすべての具象クラスに通知します。 基本実装が定義されていない場合、サブクラスはフックメソッドを実装する必要があります。
これが良い壁です
私たちの職長は仕事の結果に魅了されており、おそらくこれが今日の終わりです。 見てきたように、テンプレートメソッドパターンを使用することはまったく難しくありません。 最初に、サブクラスでオーバーライドできる必要なフックメソッドを定義する基本クラスを作成しました。 もちろん、この特定のデザインパターンは考えられる問題を解決するものではありませんが、継承によってコードをクリーンに保つのに役立ちます。
次に、Strategyメソッドについて説明します。 連絡を取り合いましょう!