設計パターンは、断続的な問題を解決する方法です。 より正確には、これらは特定の問題を解決するためのガイドラインです。 これらは、アプリケーションに挿入して魔法をかけることができるクラス、パッケージ、またはライブラリではありません。
ウィキペディアが言うように:
ソフトウェアエンジニアリングでは、アプリケーション設計パターンは、アプリケーションアーキテクチャの特定のコンテキスト内で定期的に発生する問題に対する再利用可能なソリューションです。 テンプレートは、ソースコードまたはマシンコードに直接変換できる不完全なアーキテクチャソリューションです。 これは、さまざまな状況に適用できる問題を解決するためのアプローチの説明です。
気をつけて
- デザインパターンは特効薬ではありません。
- それらを強制しようとしないでください、結果は否定的かもしれません。 テンプレートは問題ではなく解決策であることを忘れないでください。 だから無理しないでください。
- 正しく適切な場所に適用されれば、救いになります。 そうしないと、さらに多くの問題が発生します。
この記事では、PHP 7の例を提供していますが、混同しないでください。テンプレートに組み込まれている原則は変わらないからです。 さらに、 他の言語のサポートが導入されています。
設計パターンの種類
ジェネレーティブデザインパターン
簡単に
生成パターンは、オブジェクトまたは関連オブジェクトのグループの作成(インスタンス化)を記述します。
ウィキペディア
ソフトウェアエンジニアリングでは、ジェネレータはテンプレートと呼ばれ、オブジェクトを作成するメカニズムを使用して、特定の状況に適した方法でオブジェクトを作成します。 作成の基本的な方法は、アーキテクチャの問題や複雑化を招く可能性があります。 パターンの生成では、オブジェクトの作成方法を制御することにより、これらの問題を解決しようとします。
シンプルファクトリー
アナロジー
あなたが家を建てていて、ドアが必要だとします。 あなたがドアを必要とするたびに、あなたは道具であなた自身を武装させて、建設現場でそれをするならば、混乱があるでしょう。 代わりに、工場でドアを注文します。
簡単に
単純なファクトリは、インスタンスロジックを提供せずに、クライアントのインスタンスを生成するだけです。
ウィキペディア
オブジェクト指向プログラミングでは、ファクトリは他のオブジェクトを作成するオブジェクトです。 正式には、ファクトリは、新しいと見なされるメソッドの呼び出しから、異なるプロトタイプまたはクラスのオブジェクトを返す関数またはメソッドです。
例
まず、ドアインターフェイスとその実装が必要です。
interface Door { public function getWidth(): float; public function getHeight(): float; } class WoodenDoor implements Door { protected $width; protected $height; public function __construct(float $width, float $height) { $this->width = $width; $this->height = $height; } public function getWidth(): float { return $this->width; } public function getHeight(): float { return $this->height; } }
次に、ドアを作成して返すドアファクトリを構築しましょう。
class DoorFactory { public static function makeDoor($width, $height): Door { return new WoodenDoor($width, $height); } }
使用法:
$door = DoorFactory:makeDoor(100, 200); echo 'Width: ' . $door->getWidth(); echo 'Height: ' . $door->getHeight();
いつ使用しますか?
オブジェクトを作成するときに、いくつかの割り当てだけでなく何らかのロジックが必要な場合、どこでも同じコードを繰り返すのではなく、専用のファクトリーにタスクを委任することは理にかなっています。
工場方式
アナロジー
1人の人事担当者は、すべての役職のすべての候補者との面接を行うことはできません。 欠員に応じて、彼女はインタビューのさまざまな段階をさまざまな従業員に委任できます。
簡単に
これは、インスタンス化ロジックを子クラスに委任する方法です。
ウィキペディア
クラスベースのプログラミングでは、ファクトリメソッドは、特定のクラスを指定せずにオブジェクトを作成する問題を解決するファクトリメソッドを使用する生成デザインパターンと呼ばれます。 オブジェクトは、コンストラクターではなく、インターフェイスで定義され、子クラスによって実装されるか、基本クラスに実装され、オプションで派生クラスによってオーバーライドされる生成メソッドを呼び出すことによって作成されます。
例
最初に、インタビュアーのインターフェースと、彼のためのいくつかの実装を作成します。
interface Interviewer { public function askQuestions(); } class Developer implements Interviewer { public function askQuestions() { echo 'Asking about design patterns!'; } } class CommunityExecutive implements Interviewer { public function askQuestions() { echo 'Asking about community building'; } }
HiringManager HiringManager
作成します。
abstract class HiringManager { // abstract public function makeInterviewer(): Interviewer; public function takeInterview() { $interviewer = $this->makeInterviewer(); $interviewer->askQuestions(); } }
どの子クラスでもそれを拡張し、適切なインタビュー対象者を提供できます。
class DevelopmentManager extends HiringManager { public function makeInterviewer(): Interviewer { return new Developer(); } } class MarketingManager extends HiringManager { public function makeInterviewer(): Interviewer { return new CommunityExecutive(); } }
使用法:
$devManager = new DevelopmentManager(); $devManager->takeInterview(); // Output: . $marketingManager = new MarketingManager(); $marketingManager->takeInterview(); // Output: .
いつ使用しますか?
このテンプレートは、クラスでの一般的な処理に役立ちますが、必要なサブクラスは実行時に動的に決定されます。 つまり、クライアントが必要なサブクラスを知らないときです。
抽象工場
アナロジー
Simple Factoryのドアを使用した例に戻りましょう。 ニーズに応じて、ある店舗で木製のドアを、別の店舗でスチールを、別の店舗でプラスチックを購入できます。 設置には、さまざまな専門家が必要になります。木製のドアには、大工さん、鉄製のもの、溶接機、プラスチック製のもの、PVCプロファイル専用のものが必要です。
簡単に
これは工場の工場です。 つまり、個々の相互接続された/相互依存する工場を特定のクラスを指定せずにグループ化するファクトリーです。
ウィキペディア
Abstract Factoryテンプレートは、特定のクラスを指定せずに、特定のトピックによって結合された個々のファクトリーのグループをカプセル化する方法を説明します。
例
Doorインターフェースとそのためのいくつかの実装を作成しましょう。
interface Door { public function getDescription(); } class WoodenDoor implements Door { public function getDescription() { echo 'I am a wooden door'; } } class IronDoor implements Door { public function getDescription() { echo 'I am an iron door'; } }
次に、各タイプのドアの設置に専門家が必要です。
interface DoorFittingExpert { public function getDescription(); } class Welder implements DoorFittingExpert { public function getDescription() { echo 'I can only fit iron doors'; } } class Carpenter implements DoorFittingExpert { public function getDescription() { echo 'I can only fit wooden doors'; } }
オブジェクトのファミリまたは関連オブジェクトを作成できる抽象ファクトリを取得しました。 つまり、木製のドアの工場は木製のドアとその設置のための人、鋼鉄のドアの工場-鋼鉄のドアと対応する専門家などを作成します。
interface DoorFactory { public function makeDoor(): Door; public function makeFittingExpert(): DoorFittingExpert; } // class WoodenDoorFactory implements DoorFactory { public function makeDoor(): Door { return new WoodenDoor(); } public function makeFittingExpert(): DoorFittingExpert { return new Carpenter(); } } // class IronDoorFactory implements DoorFactory { public function makeDoor(): Door { return new IronDoor(); } public function makeFittingExpert(): DoorFittingExpert { return new Welder(); } }
使用法:
$woodenFactory = new WoodenDoorFactory(); $door = $woodenFactory->makeDoor(); $expert = $woodenFactory->makeFittingExpert(); $door->getDescription(); // Output: $expert->getDescription(); // Output: // Same for Iron Factory $ironFactory = new IronDoorFactory(); $door = $ironFactory->makeDoor(); $expert = $ironFactory->makeFittingExpert(); $door->getDescription(); // Output: $expert->getDescription(); // Output:
ここでは、木製のドアの工場はcarpenter
とwooden door
、 iron door
工場- iron door
とwelder
カプセル化しました。 つまり、作成されたドアごとに、適切なスペシャリストを確実に確保できます。
いつ使用しますか?
それほど単純ではない作成ロジックと関係がある場合。
ビルダー
アナロジー
あなたが飲食店に来て、その日のハンバーガーを注文し、あなたがそれを問題なく与えられたと仮定します。 これは、シンプルファクトリの例です。 ただし、作成のロジックが複数のステップで構成される場合があります。 たとえば、その日のハンバーガーを注文するとき、パン、トッピング、ソース、追加の材料にはいくつかのオプションがあります。 このような状況では、Builderパターンが役立ちます。
簡単に
このテンプレートを使用すると、コンストラクタの汚染を回避して、オブジェクトのさまざまなプロパティを作成できます。 これは、オブジェクトが複数のプロパティを持つことができる場合に便利です。 または、オブジェクトを作成する場合、多数のステップで構成されます。
ウィキペディア
「ビルダー」テンプレートは、テレスコープコンストラクターアンチパターンの問題の解決策を見つけるように設計されています。
Telescopingコンストラクターのアンチパターンとは何かを説明します。 私たちはそれぞれ、同様のコンストラクターに出くわしました。
public function __construct($size, $cheese = true, $pepperoni = true, $tomato = false, $lettuce = true) { }
ご覧のとおり、パラメーターの数は急速に増加する可能性があり、その構造を理解することは難しくなります。 さらに、将来新しいオプションを追加する場合、このオプションのリストは増え続けます。 これは、Telescopingコンストラクターのアンチパターンです。
例
合理的な代替手段は、Builderテンプレートです。 まず、ハンバーガーを作成します。
class Burger { protected $size; protected $cheese = false; protected $pepperoni = false; protected $lettuce = false; protected $tomato = false; public function __construct(BurgerBuilder $builder) { $this->size = $builder->size; $this->cheese = $builder->cheese; $this->pepperoni = $builder->pepperoni; $this->lettuce = $builder->lettuce; $this->tomato = $builder->tomato; } }
そして、「ビルダー」を追加します。
class BurgerBuilder { public $size; public $cheese = false; public $pepperoni = false; public $lettuce = false; public $tomato = false; public function __construct(int $size) { $this->size = $size; } public function addPepperoni() { $this->pepperoni = true; return $this; } public function addLettuce() { $this->lettuce = true; return $this; } public function addCheese() { $this->cheese = true; return $this; } public function addTomato() { $this->tomato = true; return $this; } public function build(): Burger { return new Burger($this); } }
使用法:
$burger = (new BurgerBuilder(14)) ->addPepperoni() ->addLettuce() ->addTomato() ->build();
いつ使用しますか?
オブジェクトが複数のプロパティを持つことができ、テレスコープコンストラクターを回避する必要がある場合。 Simple Factoryテンプレートとの主な違いは、シングルステージ作成で使用され、Builderがマルチステージで使用されることです。
試作機
アナロジー
クローン羊のドリーを覚えていますか? そのため、このデザインパターンは、クローン作成に関するものです。
簡単に
オブジェクトは、既存のオブジェクトを複製することにより作成されます。
ウィキペディア
プロトタイプテンプレートは、作成されたオブジェクトのタイプが、新しいオブジェクトを作成するために複製されたプロトタイプインスタンスによって決定される場合に使用されます。
つまり、テンプレートを使用すると、既存のオブジェクトを複製し、必要に応じてコピーを変更できます。 オブジェクトを最初から作成して設定するのに問題はありません。
例
PHPでは、これはclone
使用して簡単に実行できます。
class Sheep { protected $name; protected $category; public function __construct(string $name, string $category = 'Mountain Sheep') { $this->name = $name; $this->category = $category; } public function setName(string $name) { $this->name = $name; } public function getName() { return $this->name; } public function setCategory(string $category) { $this->category = $category; } public function getCategory() { return $this->category; } }
次に、次のようにクローンを作成できます。
$original = new Sheep('Jolly'); echo $original->getName(); // echo $original->getCategory(); // // , $cloned = clone $original; $cloned->setName('Dolly'); echo $cloned->getName(); // echo $cloned->getCategory(); //
__clone
マジックメソッドを使用して、クローン作成手順を変更することもできます。
いつ使用しますか?
必要なオブジェクトが既存のオブジェクトに似ている場合、またはゼロから作成する場合は、クローン作成よりも費用がかかります。
ロナー
アナロジー
1つの国には1人の大統領しかいません。 彼は状況と義務が要求するときに行動しなければなりません。 この場合、大統領は孤独です。
簡単に
テンプレートを使用すると、作成されたオブジェクトがクラス内の唯一のオブジェクトであることを確認できます。
ウィキペディア
「Loner」テンプレートを使用すると、クラスの作成を単一のオブジェクトに制限できます。 これは、システム内のアクションの調整で、オブジェクトがクラス内で一意である必要がある場合に便利です。
実際、「Loner」テンプレートはアンチパターンと見なされます;あまり夢中にならないでください。 必ずしも悪いわけではなく、時には役立つこともあります。 ただし、Lonerはアプリケーション内でグローバルな状態を作成するため、注意して使用してください。1つの場所での変更は他のすべてのユースケースに影響を与える可能性があり、デバッグは簡単な作業ではありません。 テンプレートのその他の欠点:コードが密結合され、「Loners」のプロトタイピング(モック)が困難になる場合があります。
例
コンストラクタをプライベートにし、拡張機能を無効にし、インスタンスを保持する静的変数を作成します。
final class President { private static $instance; private function __construct() { // } public static function getInstance(): President { if (!self::$instance) { self::$instance = new self(); } return self::$instance; } private function __clone() { // } private function __wakeup() { // } }
使用法:
$president1 = President::getInstance(); $president2 = President::getInstance(); var_dump($president1 === $president2); // true
構造設計パターン
簡単に
これらのテンプレートは、主にオブジェクトの構成専用です。 つまり、エンティティが相互に使用する方法です。 別の説明:構造パターンは、「ソフトウェアコンポーネントの構築方法」という質問への回答に役立ちます。
ウィキペディア
構造は、エンティティ間の関係を実装する簡単な方法を定義することにより、設計を容易にするテンプレートです。
アダプター
アナロジー
メモリカードに写真があるとします。 それらをコンピューターに転送する必要があります。 メモリカードを挿入できるコンピューターの入力ポートと互換性のあるアダプターが必要です。 この例では、アダプターはカードリーダーです。 別の例:ロシアのコンセントでアメリカの電源を使用できるアダプター。 3番目の例:翻訳者は、異なる言語を話す2人をつなぐアダプターです。
簡単に
「Adapter」テンプレートを使用すると、互換性のないオブジェクトをラッパーに配置して、別のクラスとの互換性を確保できます。
ウィキペディア
アダプター設計パターンにより、既存のクラスのインターフェースを別のインターフェースとして使用できます。 このテンプレートは、ソースコードを変更せずに一部のクラスが他のクラスと連携するようにするためによく使用されます。
例
ライオンハンターを想像してください。
すべての種類のライオンを実装するLion
インターフェースを作成しましょう。
interface Lion { public function roar(); } class AfricanLion implements Lion { public function roar() { } } class AsianLion implements Lion { public function roar() { } }
ハンターは、 Lion
インターフェースのすべての実装を探さなければなりません。
class Hunter { public function hunt(Lion $lion) { } }
ハンターが狩ることができる野生の犬WildDog
を追加します。 しかし、犬には異なるインターフェースがあるため、直接行うことはできません。 ハンターと互換性を持たせるには、適切なアダプターを作成する必要があります。
// class WildDog { public function bark() { } } // class WildDogAdapter implements Lion { protected $dog; public function __construct(WildDog $dog) { $this->dog = $dog; } public function roar() { $this->dog->bark(); } }
WildDog
はWildDog
アクションをWildDog
できるようになりWildDogAdapter
。
$wildDog = new WildDog(); $wildDogAdapter = new WildDogAdapter($wildDog); $hunter = new Hunter(); $hunter->hunt($wildDogAdapter);
橋
アナロジー
いくつかのページがあるウェブサイトがあるとしましょう。 ユーザーがページレイアウトを変更できるようにします。 どうしますか? トピックごとに各ページの複数のコピーを作成したか、ユーザーの好みに応じて個別のトピックを作成してロードしましたか? Bridgeテンプレートを使用すると、2番目のアプローチを実装できます。
簡単に
Bridgeテンプレートは、継承のレイアウト設定です。 実装の詳細は、ある階層から別の階層を持つ別のオブジェクトに渡されます。
ウィキペディア
「ブリッジ」テンプレートとは、抽象化と実装の分離を意味するため、両方を互いに独立して変更できます。
例
上記の例をWebページで実装します。 WebPage
階層を作成しましょう。
interface WebPage { public function __construct(Theme $theme); public function getContent(); } class About implements WebPage { protected $theme; public function __construct(Theme $theme) { $this->theme = $theme; } public function getContent() { return "About page in " . $this->theme->getColor(); } } class Careers implements WebPage { protected $theme; public function __construct(Theme $theme) { $this->theme = $theme; } public function getContent() { return "Careers page in " . $this->theme->getColor(); } }
トピックの階層を分離します。
interface Theme { public function getColor(); } class DarkTheme implements Theme { public function getColor() { return 'Dark Black'; } } class LightTheme implements Theme { public function getColor() { return 'Off white'; } } class AquaTheme implements Theme { public function getColor() { return 'Light blue'; } }
両方の階層:
$darkTheme = new DarkTheme(); $about = new About($darkTheme); $careers = new Careers($darkTheme); echo $about->getContent(); // "About page in Dark Black"; echo $careers->getContent(); // "Careers page in Dark Black";
リンカー
アナロジー
各企業は従業員で構成されています。 各従業員には同じ特性があります:給与、責任、誰かへの報告、従属...
簡単に
リンカーテンプレートを使用すると、クライアントは個々のオブジェクトを単一の順序で処理できます。
ウィキペディア
「リンカー」テンプレートは、オブジェクトの単一インスタンスであるかのように、オブジェクトのグループの一般的な処理順序を記述します。 テンプレートの本質は、特定の階層から全体への階層を表すために、オブジェクトをツリー構造にコンパイルすることです。 このテンプレートにより、顧客は個々のオブジェクトとオブジェクトのグループに等しく適用できます。
例
従業員の種類は次のとおりです。
interface Employee { public function __construct(string $name, float $salary); public function getName(): string; public function setSalary(float $salary); public function getSalary(): float; public function getRoles(): array; } class Developer implements Employee { protected $salary; protected $name; public function __construct(string $name, float $salary) { $this->name = $name; $this->salary = $salary; } public function getName(): string { return $this->name; } public function setSalary(float $salary) { $this->salary = $salary; } public function getSalary(): float { return $this->salary; } public function getRoles(): array { return $this->roles; } } class Designer implements Employee { protected $salary; protected $name; public function __construct(string $name, float $salary) { $this->name = $name; $this->salary = $salary; } public function getName(): string { return $this->name; } public function setSalary(float $salary) { $this->salary = $salary; } public function getSalary(): float { return $this->salary; } public function getRoles(): array { return $this->roles; } }
そして、ここにさまざまなタイプの従業員で構成される会社があります:
class Organization { protected $employees; public function addEmployee(Employee $employee) { $this->employees[] = $employee; } public function getNetSalaries(): float { $netSalary = 0; foreach ($this->employees as $employee) { $netSalary += $employee->getSalary(); } return $netSalary; } }
使用法:
// $john = new Developer('John Doe', 12000); $jane = new Designer('Jane Doe', 15000); // $organization = new Organization(); $organization->addEmployee($john); $organization->addEmployee($jane); echo "Net salaries: " . $organization->getNetSalaries(); // Net Salaries: 27000
デコレータ
アナロジー
さまざまなサービスを提供する独自のカーサービスがあるとします。 顧客への請求方法 サービスとそのコストを順番に追加します-そして、最終的に合計金額を支払います。 ここでは、各タイプのサービスは「装飾者」です。
簡単に
デコレータテンプレートを使用すると、デコレータクラスのオブジェクトにオブジェクトをラップすることで、実行時にオブジェクトの動作を動的に変更できます。
ウィキペディア
デコレータテンプレートを使用すると、同じクラスの他のオブジェクトの動作に影響を与えることなく、追加の動作をオブジェクトに(静的または動的に)接続できます。 テンプレートは、特定の問題を解決するためにクラス間で機能を共有できるため、単一責任の原則に従うためによく使用されます。
例
例としてコーヒーを取ります。 まず、インターフェイスを単に実装します。
interface Coffee { public function getCost(); public function getDescription(); } class SimpleCoffee implements Coffee { public function getCost() { return 10; } public function getDescription() { return 'Simple coffee'; } }
必要に応じて、コードを拡張可能にして、変更を加えることができます。 「装飾」を追加します。
class MilkCoffee implements Coffee { protected $coffee; public function __construct(Coffee $coffee) { $this->coffee = $coffee; } public function getCost() { return $this->coffee->getCost() + 2; } public function getDescription() { return $this->coffee->getDescription() . ', milk'; } } class WhipCoffee implements Coffee { protected $coffee; public function __construct(Coffee $coffee) { $this->coffee = $coffee; } public function getCost() { return $this->coffee->getCost() + 5; } public function getDescription() { return $this->coffee->getDescription() . ', whip'; } } class VanillaCoffee implements Coffee { protected $coffee; public function __construct(Coffee $coffee) { $this->coffee = $coffee; } public function getCost() { return $this->coffee->getCost() + 3; } public function getDescription() { return $this->coffee->getDescription() . ', vanilla'; } }
コーヒーを作る:
$someCoffee = new SimpleCoffee(); echo $someCoffee->getCost(); // 10 echo $someCoffee->getDescription(); // Simple Coffee $someCoffee = new MilkCoffee($someCoffee); echo $someCoffee->getCost(); // 12 echo $someCoffee->getDescription(); // Simple Coffee, milk $someCoffee = new WhipCoffee($someCoffee); echo $someCoffee->getCost(); // 17 echo $someCoffee->getDescription(); // Simple Coffee, milk, whip $someCoffee = new VanillaCoffee($someCoffee); echo $someCoffee->getCost(); // 20 echo $someCoffee->getDescription(); // Simple Coffee, milk, whip, vanilla
ファサード
アナロジー
コンピューターの電源を入れる方法は? 「電源ボタンを押してください」と言うでしょう。 これは、外部のコンピューターが提供するシンプルなインターフェイスを使用しているためです。 そしてその中にはたくさんのプロセスがあります。 複雑なサブシステムのシンプルなインターフェイスはファサードです。
簡単に
Facadeテンプレートは、複雑なサブシステムの簡素化されたインターフェイスを提供します。
ウィキペディア
ファサードは、クラスライブラリなど、より大きなコード本体に簡単なインターフェイスを提供するオブジェクトです。
例
クラスコンピューターを作成します。
class Computer { public function getElectricShock() { echo "Ouch!"; } public function makeSound() { echo "Beep beep!"; } public function showLoadingScreen() { echo "Loading.."; } public function bam() { echo "Ready to be used!"; } public function closeEverything() { echo "Bup bup bup buzzzz!"; } public function sooth() { echo "Zzzzz"; } public function pullCurrent() { echo "Haaah!"; } }
今、「ファサード」:
class ComputerFacade { protected $computer; public function __construct(Computer $computer) { $this->computer = $computer; } public function turnOn() { $this->computer->getElectricShock(); $this->computer->makeSound(); $this->computer->showLoadingScreen(); $this->computer->bam(); } public function turnOff() { $this->computer->closeEverything(); $this->computer->pullCurrent(); $this->computer->sooth(); } }
使用法:
$computer = new ComputerFacade(new Computer()); $computer->turnOn(); // Ouch! Beep beep! Loading.. Ready to be used! $computer->turnOff(); // Bup bup buzzz! Haah! Zzzzz
日和見主義者
アナロジー
通常、ケータリング施設では、お茶は顧客ごとに別々に醸造されるのではなく、すぐに特定の大容量で醸造されます。 これにより、ガス/電気、時間などのリソースを節約できます。アダプティブテンプレートは共有するだけです。
簡単に
テンプレートは、可能な限り多くの同一オブジェクトの合計使用によるメモリ使用量または計算コストを最小限に抑えるために使用されます。
ウィキペディア
「Adapt」は、他の同じオブジェクトで可能な限り多くのデータを使用するため、メモリ使用を最小限に抑えるオブジェクトです。 これは、単純な反復表現が許容できないメモリ消費につながる場合に複数のオブジェクトを適用する方法です。
例
お茶とティーポットの種類を作りましょう。
// — , . // — . class KarakTea { } // class TeaMaker { protected $availableTea = []; public function make($preference) { if (empty($this->availableTea[$preference])) { $this->availableTea[$preference] = new KarakTea(); } return $this->availableTea[$preference]; } }
TeaShop
注文を受け入れて処理する飲食店を作りましょう:
class TeaShop { protected $orders; protected $teaMaker; public function __construct(TeaMaker $teaMaker) { $this->teaMaker = $teaMaker; } public function takeOrder(string $teaType, int $table) { $this->orders[$table] = $this->teaMaker->make($teaType); } public function serve() { foreach ($this->orders as $table => $tea) { echo "Serving tea to table# " . $table; } } }
使用法:
$teaMaker = new TeaMaker(); $shop = new TeaShop($teaMaker); $shop->takeOrder('less sugar', 1); $shop->takeOrder('more milk', 2); $shop->takeOrder('without sugar', 5); $shop->serve(); // Serving tea to table# 1 // Serving tea to table# 2 // Serving tea to table# 5
副
アナロジー
アクセスカードまたはボタンを使用してセキュリティシステムをバイパスする電子ロックでドアを開くことができます。つまり、ドアの主な機能は開くことであり、その上には、まだ何らかの種類の機能があります-「代理」。
簡単に
代替テンプレートを使用すると、クラスは別のクラスの機能を表します。
ウィキペディア
«» — , -. -, , «» . «» , : ; (preconditions) .
例
:
interface Door { public function open(); public function close(); } class LabDoor implements Door { public function open() { echo "Opening lab door"; } public function close() { echo "Closing the lab door"; } }
«», :
class Security { protected $door; public function __construct(Door $door) { $this->door = $door; } public function open($password) { if ($this->authenticate($password)) { $this->door->open(); } else { echo "Big no! It ain't possible."; } } public function authenticate($password) { return $password === '$ecr@t'; } public function close() { $this->door->close(); } }
使用法:
$door = new Security(new LabDoor()); $door->open('invalid'); // Big no! It ain't possible. $door->open('$ecr@t'); // Opening lab door $door->close(); // Closing lab door
(data-mapper). ODM (Object Data Mapper) MongoDB. «» mongo-, __call()
. mongo- «», . find
findOne
, Cursor
.
簡単に
(responsibilities) . , , , . « ?»
ウィキペディア
. .
アナロジー
, ( A
, B
C
). : A
— 100 , B
— 300, C
— 1000. : , , . - 210 . « » . — , . — , . .
簡単に
« » . , .
ウィキペディア
« » . , , , .
例
, , .
abstract class Account { protected $successor; protected $balance; public function setNext(Account $account) { $this->successor = $account; } public function pay(float $amountToPay) { if ($this->canPay($amountToPay)) { echo sprintf('Paid %s using %s' . PHP_EOL, $amountToPay, get_called_class()); } elseif ($this->successor) { echo sprintf('Cannot pay using %s. Proceeding ..' . PHP_EOL, get_called_class()); $this->successor->pay($amountToPay); } else { throw new Exception('None of the accounts have enough balance'); } } public function canPay($amount): bool { return $this->balance >= $amount; } } class Bank extends Account { protected $balance; public function __construct(float $balance) { $this->balance = $balance; } } class Paypal extends Account { protected $balance; public function __construct(float $balance) { $this->balance = $balance; } } class Bitcoin extends Account { protected $balance; public function __construct(float $balance) { $this->balance = $balance; } }
(Bank, Paypal, Bitcoin) :
// // $bank->$paypal->$bitcoin // // // , Paypal // Paypal , Bitcoin $bank = new Bank(100); // 100 $paypal = new Paypal(200); // Paypal 200 $bitcoin = new Bitcoin(300); // Bitcoin 300 $bank->setNext($paypal); $paypal->setNext($bitcoin); // $bank->pay(259); // // ============== // . ... // Paypal. ... // 259 Bitcoin!
チーム
アナロジー
. ( Client
) ( Invoker
) ( Command
). - ( Receiver
), , . : ( Client
) ( Command
) ( Receiver
) ( Invoker
).
簡単に
«» . — .
ウィキペディア
«» , . ; , ; .
例
, , .
// Receiver class Bulb { public function turnOn() { echo "Bulb has been lit"; } public function turnOff() { echo "Darkness!"; } }
, . .
interface Command { public function execute(); public function undo(); public function redo(); } // Command class TurnOn implements Command { protected $bulb; public function __construct(Bulb $bulb) { $this->bulb = $bulb; } public function execute() { $this->bulb->turnOn(); } public function undo() { $this->bulb->turnOff(); } public function redo() { $this->execute(); } } class TurnOff implements Command { protected $bulb; public function __construct(Bulb $bulb) { $this->bulb = $bulb; } public function execute() { $this->bulb->turnOff(); } public function undo() { $this->bulb->turnOn(); } public function redo() { $this->execute(); } }
Invoker
, .
// Invoker class RemoteControl { public function submit(Command $command) { $command->execute(); } }
, :
$bulb = new Bulb(); $turnOn = new TurnOn($bulb); $turnOff = new TurnOff($bulb); $remote = new RemoteControl(); $remote->submit($turnOn); // ! $remote->submit($turnOff); // !
«» . , . , . undo
.
アナロジー
— . - , /. .
簡単に
— .
ウィキペディア
. . - , , .
例
PHP PHP. RadioStation
.
class RadioStation { protected $frequency; public function __construct(float $frequency) { $this->frequency = $frequency; } public function getFrequency(): float { return $this->frequency; } }
:
use Countable; use Iterator; class StationList implements Countable, Iterator { /** @var RadioStation[] $stations */ protected $stations = []; /** @var int $counter */ protected $counter; public function addStation(RadioStation $station) { $this->stations[] = $station; } public function removeStation(RadioStation $toRemove) { $toRemoveFrequency = $toRemove->getFrequency(); $this->stations = array_filter($this->stations, function (RadioStation $station) use ($toRemoveFrequency) { return $station->getFrequency() !== $toRemoveFrequency; }); } public function count(): int { return count($this->stations); } public function current(): RadioStation { return $this->stations[$this->counter]; } public function key() { return $this->counter; } public function next() { $this->counter++; } public function rewind() { $this->counter = 0; } public function valid(): bool { return isset($this->stations[$this->counter]); } }
使用法:
$stationList = new StationList(); $stationList->addStation(new RadioStation(89)); $stationList->addStation(new RadioStation(101)); $stationList->addStation(new RadioStation(102)); $stationList->addStation(new RadioStation(103.2)); foreach($stationList as $station) { echo $station->getFrequency() . PHP_EOL; } $stationList->removeStation(new RadioStation(89)); // Will remove station 89
アナロジー
- , . , . — .
簡単に
«» («») («»). (coupling) , , .
ウィキペディア
, .
例
: («»), («») .
«»:
interface ChatRoomMediator { public function showMessage(User $user, string $message); } // class ChatRoom implements ChatRoomMediator { public function showMessage(User $user, string $message) { $time = date('M d, y H:i'); $sender = $user->getName(); echo $time . '[' . $sender . ']:' . $message; } }
«»:
class User { protected $name; protected $chatMediator; public function __construct(string $name, ChatRoomMediator $chatMediator) { $this->name = $name; $this->chatMediator = $chatMediator; } public function getName() { return $this->name; } public function send($message) { $this->chatMediator->showMessage($this, $message); } }
使用法:
$mediator = new ChatRoom(); $john = new User('John Doe', $mediator); $jane = new User('Jane Doe', $mediator); $john->send('Hi there!'); $jane->send('Hey!'); // // Feb 14, 10:58 [John]: Hi there! // Feb 14, 10:58 [Jane]: Hey!
アナロジー
(«»), («»), - («»).
簡単に
«» , .
ウィキペディア
«» ( — undo via rollback).
, .
例
, - .
«», .
class EditorMemento { protected $content; public function __construct(string $content) { $this->content = $content; } public function getContent() { return $this->content; } }
(«»), «».
class Editor { protected $content = ''; public function type(string $words) { $this->content = $this->content . ' ' . $words; } public function getContent() { return $this->content; } public function save() { return new EditorMemento($this->content); } public function restore(EditorMemento $memento) { $this->content = $memento->getContent(); } }
使用法:
$editor = new Editor(); // - $editor->type('This is the first sentence.'); $editor->type('This is second.'); // : This is the first sentence. This is second. $saved = $editor->save(); // $editor->type('And this is third.'); // Output: echo $editor->getContent(); // This is the first sentence. This is second. And this is third. // $editor->restore($saved); $editor->getContent(); // This is the first sentence. This is second.
アナロジー
: , , , , .
簡単に
, «» .
ウィキペディア
«» («»), «» («») , .
例
, , .
class JobPost { protected $title; public function __construct(string $title) { $this->title = $title; } public function getTitle() { return $this->title; } } class JobSeeker implements Observer { protected $name; public function __construct(string $name) { $this->name = $name; } public function onJobPosted(JobPost $job) { // Do something with the job posting echo 'Hi ' . $this->name . '! New job posted: '. $job->getTitle(); } }
, .
class JobPostings implements Observable { protected $observers = []; protected function notify(JobPost $jobPosting) { foreach ($this->observers as $observer) { $observer->onJobPosted($jobPosting); } } public function attach(Observer $observer) { $this->observers[] = $observer; } public function addJob(JobPost $jobPosting) { $this->notify($jobPosting); } }
使用法:
// $johnDoe = new JobSeeker('John Doe'); $janeDoe = new JobSeeker('Jane Doe'); // $jobPostings = new JobPostings(); $jobPostings->attach($johnDoe); $jobPostings->attach($janeDoe); // , $jobPostings->addJob(new JobPost('Software Engineer')); // Output // Hi John Doe! New job posted: Software Engineer // Hi Jane Doe! New job posted: Software Engineer
訪問者
アナロジー
. (). , , . - — . «» .
簡単に
«» .
ウィキペディア
«» — , . — . / (open/closed principle).
例
: , .
// interface Animal { public function accept(AnimalOperation $operation); } // interface AnimalOperation { public function visitMonkey(Monkey $monkey); public function visitLion(Lion $lion); public function visitDolphin(Dolphin $dolphin); }
:
class Monkey implements Animal { public function shout() { echo 'Ooh oo aa aa!'; } public function accept(AnimalOperation $operation) { $operation->visitMonkey($this); } } class Lion implements Animal { public function roar() { echo 'Roaaar!'; } public function accept(AnimalOperation $operation) { $operation->visitLion($this); } } class Dolphin implements Animal { public function speak() { echo 'Tuut tuttu tuutt!'; } public function accept(AnimalOperation $operation) { $operation->visitDolphin($this); } }
:
class Speak implements AnimalOperation { public function visitMonkey(Monkey $monkey) { $monkey->shout(); } public function visitLion(Lion $lion) { $lion->roar(); } public function visitDolphin(Dolphin $dolphin) { $dolphin->speak(); } }
使用法:
$monkey = new Monkey(); $lion = new Lion(); $dolphin = new Dolphin(); $speak = new Speak(); $monkey->accept($speak); // --! $lion->accept($speak); // ! $dolphin->accept($speak); // !
, . . , , :
class Jump implements AnimalOperation { public function visitMonkey(Monkey $monkey) { echo 'Jumped 20 feet high! on to the tree!'; } public function visitLion(Lion $lion) { echo 'Jumped 7 feet! Back on the ground!'; } public function visitDolphin(Dolphin $dolphin) { echo 'Walked on water a little and disappeared'; } }
使用法:
$jump = new Jump(); $monkey->accept($speak); // Ooh oo aa aa! $monkey->accept($jump); // Jumped 20 feet high! on to the tree! $lion->accept($speak); // Roaaar! $lion->accept($jump); // Jumped 7 feet! Back on the ground! $dolphin->accept($speak); // Tuut tutt tuutt! $dolphin->accept($jump); // Walked on water a little and disappeared
戦略
アナロジー
. , . (Quick sort). , . , , — .
簡単に
«» .
ウィキペディア
«» .
例
. .
interface SortStrategy { public function sort(array $dataset): array; } class BubbleSortStrategy implements SortStrategy { public function sort(array $dataset): array { echo "Sorting using bubble sort"; // Do sorting return $dataset; } } class QuickSortStrategy implements SortStrategy { public function sort(array $dataset): array { echo "Sorting using quick sort"; // Do sorting return $dataset; } }
, .
class Sorter { protected $sorter; public function __construct(SortStrategy $sorter) { $this->sorter = $sorter; } public function sort(array $dataset): array { return $this->sorter->sort($dataset); } }
使用法:
$dataset = [1, 5, 4, 3, 2, 8]; $sorter = new Sorter(new BubbleSortStrategy()); $sorter->sort($dataset); // Output : $sorter = new Sorter(new QuickSortStrategy()); $sorter->sort($dataset); // Output :
アナロジー
, «». : . . .
簡単に
.
ウィキペディア
«» . :
- «»,
- (state transitions) , (superclass).
«» — «», , .
例
, , . . — . .
:
interface WritingState { public function write(string $words); } class UpperCase implements WritingState { public function write(string $words) { echo strtoupper($words); } } class LowerCase implements WritingState { public function write(string $words) { echo strtolower($words); } } class Default implements WritingState { public function write(string $words) { echo $words; } }
:
class TextEditor { protected $state; public function __construct(WritingState $state) { $this->state = $state; } public function setState(WritingState $state) { $this->state = $state; } public function type(string $words) { $this->state->write($words); } }
使用法:
$editor = new TextEditor(new Default()); $editor->type('First line'); $editor->setState(new UpperCase()); $editor->type('Second line'); $editor->type('Third line'); $editor->setState(new LowerCase()); $editor->type('Fourth line'); $editor->type('Fifth line'); // Output: // First line // SECOND LINE // THIRD LINE // fourth line // fifth line
アナロジー
, . :
- .
- .
- .
- .
. — . . : , , , .
簡単に
« » , .
ウィキペディア
« » — , , .
例
, , , (lint), , ( , . .), .
.
abstract class Builder { // final public function build() { $this->test(); $this->lint(); $this->assemble(); $this->deploy(); } abstract public function test(); abstract public function lint(); abstract public function assemble(); abstract public function deploy(); }
:
class AndroidBuilder extends Builder { public function test() { echo 'Running android tests'; } public function lint() { echo 'Linting the android code'; } public function assemble() { echo 'Assembling the android build'; } public function deploy() { echo 'Deploying android build to server'; } } class IosBuilder extends Builder { public function test() { echo 'Running ios tests'; } public function lint() { echo 'Linting the ios code'; } public function assemble() { echo 'Assembling the ios build'; } public function deploy() { echo 'Deploying ios build to server'; } }
使用法:
$androidBuilder = new AndroidBuilder(); $androidBuilder->build(); // Output: // Android- // Android- // Android- // Android- $iosBuilder = new IosBuilder(); $iosBuilder->build(); // Output: // iOS- // iOS- // iOS- // iOS-
. . .