トピックは穴にハックされています、私は主張しません...おそらく、経験豊富な開発者にとって、私の記事はほとんど役に立たないでしょう。 私は、彼のコードに何かが欠けていること、そして彼がこの遠い概念-「パターン」を理解するために熟していることに気づき始めた人にそれを読むことをお勧めします。 私自身、かなり長い間テンプレートで混乱し、時には一方が他方とどのように異なるのかさえ理解できなかったことを覚えています。 この事実が私の記事の基礎となった。 その中の例は現実的ではありません。 それらは抽象的で、可能な限りシンプルになります。 ただし、同じ状況での使用の違いを明確に確認できるように、すべての例を単一のコンテキストで保持するようにします。 コードのどの部分がテンプレートに直接関連しているかを理解できるように、不要な機能を持つクラスをロードしません。 例の主役は、工場(工場)と製品(この工場で製造された製品)です。 この姿勢を出発点としてください。 おそらく、これはあまり適切ではないかもしれませんが、非常に明確です...
この記事はいくつかのパートに分かれています。 それぞれで、新しいタイプのデザインパターンについて説明します。 このトピックに興味があるかもしれない人は誰でも、私は猫の下で尋ねます。
ジェネレーティブデザインパターン
あなたの許可を得て、これらの生成パターンが誰であるかを何度も語りません...
ここにウィキペディアへの
リンクを残します 簡潔さは才能の姉妹です。 したがって、すぐに例を提供します。
登録
このテンプレートから始めたいと思います。 それは生成的ではないため、彼は一般的な列から少し外れていますが、将来的には彼の知識が必要になります。 そのため、レジストリはハッシュであり、そのデータは静的メソッドを介してアクセスされます。
例1<?php class Product { protected static $data = array(); public static function set($key, $value) { self::$data[$key] = $value; } public static function get($key) { return isset(self::$data[$key]) ? self::$data[$key] : null; } final public static function removeProduct($key) { if (array_key_exists($key, self::$data)) { unset(self::$data[$key]); } } } Product::set('name', 'First product'); print_r(Product::get('name'));
多くの場合、ArrayAccessおよび/またはIteratorインターフェースを実装するレジストリを見つけることができますが、私の意見では、これは不要です。 レジストリの主な用途は、グローバル変数の安全な代替としてです。
オブジェクトプール
実際、このテンプレートはレジストリの特殊なケースです。 オブジェクトプールは、初期化されたオブジェクトを追加し、必要に応じてそこから取得できるハッシュです。
例2 <?php class Factory { protected static $products = array(); public static function pushProduct(Product $product) { self::$products[$product->getId()] = $product; } public static function getProduct($id) { return isset(self::$products[$id]) ? self::$products[$id] : null; } public static function removeProduct($id) { if (array_key_exists($id, self::$products)) { unset(self::$products[$id]); } } } class Product { protected $id; public function __construct($id) { $this->id = $id; } public function getId() { return $this->id; } } Factory::pushProduct(new Product('first')); Factory::pushProduct(new Product('second')); print_r(Factory::getProduct('first')->getId());
シングルトン
おそらく最も人気のあるテンプレートの1つです。 原則として、誰もが最初にそれを覚えています。 そして仕事を探すとき、彼らはインタビューで彼について尋ねるのが好きです。 最も簡単な例を次に示します。
例3 <?php final class Product { private static $instance; public $a; public static function getInstance() { if (!(self::$instance instanceof self)) { self::$instance = new self(); } return self::$instance; } private function __construct() { } private function __clone() { } private function __sleep() { } private function __wakeup() { } } $firstProduct = Product::getInstance(); $secondProduct = Product::getInstance(); $firstProduct->a = 1; $secondProduct->a = 2; print_r($firstProduct->a);
シングルトンの原理は5セントと同じくらい簡単です。
Productクラスのインスタンスが1つだけ存在することを保証するために、クラスのインスタンスの作成、複製、およびシリアル化のためのすべての魔法のメソッドを閉じました。 オブジェクトを取得する唯一の方法は、静的メソッド
Product :: getInstance()を使用することです。 最初の呼び出しで、クラスはインスタンス化され、静的プロパティ
Product :: $ instanceに配置されます 。 後続の呼び出しで、スクリプトの一部として、メソッドはクラスの以前に作成された同じインスタンスを返します。
パブリックプロパティ$ aをクラスに追加して、lonersの動作を示します。 この例では、$ firstProductと$ secondProductの両方が同じオブジェクトへの参照にすぎないことがわかります。
シングルプール(マルチトン)
おそらく、誰かがプロジェクトで多くの異なるシングルトーンを使用したいと思うでしょう。 次に、テンプレートロジックを特定の実装から分離することはおそらく価値があります。 「Loner」テンプレートと「Object Pool」テンプレートを交差させてみましょう。
例4.1 <?php abstract class FactoryAbstract { protected static $instances = array(); public static function getInstance() { $className = static::getClassName(); if (!(self::$instances[$className] instanceof $className)) { self::$instances[$className] = new $className(); } return self::$instances[$className]; } public static function removeInstance() { $className = static::getClassName(); if (array_key_exists($className, self::$instances)) { unset(self::$instances[$className]); } } final protected static function getClassName() { return get_called_class(); } protected function __construct() { } final protected function __clone() { } final protected function __sleep() { } final protected function __wakeup() { } } abstract class Factory extends FactoryAbstract { final public static function getInstance() { return parent::getInstance(); } final public static function removeInstance() { parent::removeInstance(); } } class FirstProduct extends Factory { public $a = []; } class SecondProduct extends FirstProduct { }
そのため、新しい単一のクラスを追加するには、
Factoryクラスから継承する必要があります。 この例では、このようなクラスを2つ作成し、これらの各クラスに固有のインスタンスがあることを確認しました。
一般的なロジックを2つの抽象クラスに分割したのは偶然ではありませんでした。 次に、この例をもう少し複雑にします。 一意の識別子で区別される、クラスごとにいくつかのlonersを作成しましょう。
例4.2 <?php abstract class RegistryFactory extends FactoryAbstract { final public static function getInstance($id) { $className = static::getClassName(); if (isset(self::$instances[$className])) { if (!(self::$instances[$className][$id] instanceof $className)) { self::$instances[$className][$id] = new $className($id); } } else { self::$instances[$className] = [ $id => new $className($id), ]; } return self::$instances[$className][$id]; } final public static function removeInstance($id = null) { $className = static::getClassName(); if (isset(self::$instances[$className])) { if (is_null($id)) { unset(self::$instances[$className]); } else { if (isset(self::$instances[$className][$id])) { unset(self::$instances[$className][$id]); } if (empty(self::$instances[$className])) { unset(self::$instances[$className]); } } } } protected function __construct($id) { } } class FirstFactory extends RegistryFactory { public $a = []; } class SecondFactory extends FirstFactory { }
一部のORMはこの原理と同様に機能し、すでにロードされ初期化されたモデルを保存できます。
そして今、手遅れになる前に、私は夢想家を天から地上に返します。 Lonerテンプレートとその高度な兄弟は間違いなく便利ですが、必要な場所と必要でない場所を忘れずに彫刻してください。 そのようなアンチパターン、「孤独感」(Singletonitis)があり、それはただシングルトーンの不適切な使用にあることを思い出させてください(または教えてください)。 では、なぜこのテンプレートが必要なのでしょうか? 最も一般的な例は、一度作成されてスクリプト全体で使用されるデータベース接続です。 また、多くのフレームワークでは、レジストリは孤立しており、静的メソッドを持つクラスとしてではなく、オブジェクトとして使用されます。
工場工法
そして今、私は少し程度を下げて、再びルーツに戻ることを提案します。 独自の製品を生産する工場があることを知っているとします。 工場がこの製品をどの程度正確に製造するかは気にしませんが、どの工場にもそれを求める普遍的な方法があることがわかっています。
例5 <?php interface Factory { public function getProduct(); } interface Product { public function getName(); } class FirstFactory implements Factory { public function getProduct() { return new FirstProduct(); } } class SecondFactory implements Factory { public function getProduct() { return new SecondProduct(); } } class FirstProduct implements Product { public function getName() { return 'The first product'; } } class SecondProduct implements Product { public function getName() { return 'Second product'; } } $factory = new FirstFactory(); $firstProduct = $factory->getProduct(); $factory = new SecondFactory(); $secondProduct = $factory->getProduct(); print_r($firstProduct->getName());
この例では、
getProduct()メソッドはfactoryです。
抽象工場
同じタイプの複数のファクトリーがあり、特定のタスクに使用するファクトリーを選択するロジックをカプセル化したい場合があります。 これは、テンプレートが私たちの助けになるところです。
実施例6 <?php class Config { public static $factory = 1; } interface Product { public function getName(); } abstract class AbstractFactory { public static function getFactory() { switch (Config::$factory) { case 1: return new FirstFactory(); case 2: return new SecondFactory(); } throw new Exception('Bad config'); } abstract public function getProduct(); } class FirstFactory extends AbstractFactory { public function getProduct() { return new FirstProduct(); } } class FirstProduct implements Product { public function getName() { return 'The product from the first factory'; } } class SecondFactory extends AbstractFactory { public function getProduct() { return new SecondProduct(); } } class SecondProduct implements Product { public function getName() { return 'The product from second factory'; } } $firstProduct = AbstractFactory::getFactory()->getProduct(); Config::$factory = 2; $secondProduct = AbstractFactory::getFactory()->getProduct(); print_r($firstProduct->getName());
例からわかるように、どの工場を採用するかを心配する必要はありません。 抽象ファクトリー自体が構成設定をチェックし、適切なファクトリーを返します。 もちろん、抽象ファクトリーは構成ファイルによってガイドされる必要はありません。 任意のロジックを選択できます。
遅延初期化
そして、もう一つ興味深い状況があります。 あなたは工場を持っているが、その機能のどの部分が必要で、どの部分が必要でないかわからないことを想像してください。 このような場合、必要な操作は、必要な場合にのみ一度だけ実行されます。
実施例7 <?php interface Product { public function getName(); } class Factory { protected $firstProduct; protected $secondProduct; public function getFirstProduct() { if (!$this->firstProduct) { $this->firstProduct = new FirstProduct(); } return $this->firstProduct; } public function getSecondProduct() { if (!$this->secondProduct) { $this->secondProduct = new SecondProduct(); } return $this->secondProduct; } } class FirstProduct implements Product { public function getName() { return 'The first product'; } } class SecondProduct implements Product { public function getName() { return 'Second product'; } } $factory = new Factory(); print_r($factory->getFirstProduct()->getName());
メソッドが最初に呼び出されると、ファクトリーはオブジェクトを作成し、それ自体に格納します。 再度呼び出されると、完成したオブジェクトを返します。 メソッドを呼び出さなかった場合、オブジェクトはまったく作成されませんでした。 この例にはほとんど意味がありません。 このテンプレートの使用はここでは正当化されません。 その意味を示したかっただけです。 ここで、オブジェクトの作成には複雑な計算、データベースへの複数のアクセスが必要であり、多くのリソースが消費されることを想像してください。 このテンプレートに注意を払う非常に正当な理由。
試作機
一部のオブジェクトは複数回作成する必要があります。 特に初期化に時間とリソースが必要な場合は、初期化を保存するのが理にかなっています。 プロトタイプは、事前に初期化および保存されたオブジェクトです。 必要に応じて、クローンが作成されます。
例8 <?php interface Product { } class Factory { private $product; public function __construct(Product $product) { $this->product = $product; } public function getProduct() { return clone $this->product; } } class SomeProduct implements Product { public $name; } $prototypeFactory = new Factory(new SomeProduct()); $firstProduct = $prototypeFactory->getProduct(); $firstProduct->name = 'The first product'; $secondProduct = $prototypeFactory->getProduct(); $secondProduct->name = 'Second product'; print_r($firstProduct->name);
例からわかるように、2つの無関係なオブジェクトを作成しました。
ビルダー
さて、今日の最後のテンプレートはビルダーです。 複雑なオブジェクトの作成をカプセル化する場合に役立ちます。 製品の作成を委託するビルダーを工場に伝えます。
実施例9 <?php class Product { private $name; public function setName($name) { $this->name = $name; } public function getName() { return $this->name; } } class Factory { private $builder; public function __construct(Builder $builder) { $this->builder = $builder; $this->builder->buildProduct(); } public function getProduct() { return $this->builder->getProduct(); } } abstract class Builder { protected $product; final public function getProduct() { return $this->product; } public function buildProduct() { $this->product = new Product(); } } class FirstBuilder extends Builder { public function buildProduct() { parent::buildProduct(); $this->product->setName('The product of the first builder'); } } class SecondBuilder extends Builder { public function buildProduct() { parent::buildProduct(); $this->product->setName('The product of second builder'); } } $firstDirector = new Factory(new FirstBuilder()); $secondDirector = new Factory(new SecondBuilder()); print_r($firstDirector->getProduct()->getName());
そこで、9つのデザインパターンを見ました。 これはかなり長い記事です。 したがって、私はあなたの意見を知りたいです。 行われた作業に意味はありますか?構造パターンと行動パターンについて話をして、サイクルを完了する必要がありますか?
公開されているすべてのコードは、
githubにもあります。