これは、ジレンマの原因を説明しているが、解決策を提供していないため、より長い形式のコメントです。
OOP レッスン 1: クラスを使用しているからといって、オブジェクト指向のコードを書いているわけではありません。
オブジェクトのパブリック プロパティの適切な使用例はめったにありません。OPの例を見てみましょう:
class Gallery {
public $galleryID;
public $galleryName;
// ...
}
プロパティを として定義public
した後、次の 2 つのコード スニペットはどのように異なるのでしょうか?
$gallery = new Gallery;
$gallery->galleryId = 42;
$gallery->galleryName = 'some name';
// vs:
$gallery = array(
'galleryId' => 42,
'galleryName' => 'some name'
);
「まったく変わらない」と言えば正解です。実際、オブジェクトベースのコードは、インスタンス化に伴うオーバーヘッドのために遅くなりますnew
。新しい配列をコピーする代わりにオブジェクトへの参照を渡す機能など、他にもいくつかの要因がありますが、これらはこの特定の状況には影響しません。
OOP レッスン 2: オブジェクトはブラック ボックスです
変更可能なプロパティのコレクションにすぎないオブジェクトを作成する際の問題は、コードの残りの部分がそのオブジェクト内で何が起こっているかを完全に把握していることです。これが悪い理由について話しましょう...
複雑さに関しては、人間はあまり得意ではありません。優れたソフトウェアは、機能を個別のユニットにカプセル化することで、複雑さを最小限に抑えることを目的としています。この場合、「ギャラリー」エンティティのすべてのロジックをGallery
クラスにカプセル化します。これは、ドメイン駆動設計 (DDD) アプローチの一部として理にかなっています。私たちがやりたいことはGallery
、外の世界から隔絶することです。その内部実装は、コードの残りの部分に対して不透明にする必要があります。アプリケーションの残りの部分は、関数がどのように機能するかを認識したり気にしたりする必要はなくGallery
、期待どおりに機能するだけです。ここでの追加の利点は、ギャラリーを想定どおりに機能させることに集中し、その後は忘れることができることです。Gallery
がどのように動作するImage
かを覚えておく必要はありませんRevision
. この疎結合は、OO 設計における最も強力なツールの 1 つです。
非常に小さなスケールでは機能するかもしれませんが、アプリケーション全体のロジックを同時に頭に入れておくことは不可能です。どんなに賢くても、私たちの脳には十分な RAM がありません。
コードに戻ると、アプリケーション コードがGallery
自分自身に名前を割り当てる方法を知っている場合、「ギャラリー性」のロジックがプログラムの残りの部分に漏れることを既に許可しています。新しいギャラリー名が割り当てられたときにその名前を確認することにした場合はどうなりますか? 「ギャラリーらしさ」の抽象的な概念についてすべてを壁で囲んでいないため、ギャラリー名を指定したコード内のあらゆる場所にその検証ロジックを配置する必要があります。はるかに優れた設計はGallery
、オブジェクト自体内にプロパティの割り当てをカプセル化することです。
class Gallery {
private $galleryId;
private $name;
public function setName($name) {
$this->name = $name;
}
public function getName($name) {
return $this->name;
}
}
このようにクラスを構成すると、ギャラリーに名前を割り当てる必要があるときに、常に単一のエントリ ポイントが得られます。これで、ギャラリーの要件が将来的に変更された場合 (そして変更される予定です)、すべてのアプリケーション コード (ギャラリー名の割り当ての背後にあるロジックを認識していない) は破損から分離されます。名前セッターに新しいメソッドを追加するだけで、プログラムの混乱を最小限に抑えることができます。
class Gallery {
private $galleryId;
private $name;
public function setName($name) {
$this->validateName($name);
$this->name = $name;
}
private function validateName($name) {
if (!preg_match('/^[a-z]+$/', $name)) {
throw new Exception;
}
}
public function getName($name) {
return $this->name;
}
}
OPへの対処
Revision
カプセル化されたオブジェクトをより高いレベルのインスタンスのプロパティとして表現する方法の質問に答えるにはGallery
、少しのコンテキストが必要です。OPがやろうとしているのは、バックエンドの永続レイヤー(データベース、フラットテキストファイルなど)に書き込まれ、そこから取得されるモデルドメインエンティティであるようです。
貧血ドメイン モデルはこれを処理する 1 つの方法ですが、一般的にアンチパターンと見なされます。マーティン・ファウラーは次のように書いています。
貧血ドメイン モデルの基本的な症状は、一見本物のように見えることです。ドメイン空間の名詞にちなんで名付けられた多くのオブジェクトがあり、これらのオブジェクトは、真のドメイン モデルが持つ豊富な関係と構造に関連付けられています。ビヘイビアを見ると問題が発生し、これらのオブジェクトにはほとんどビヘイビアがなく、getter と setter のバッグにすぎないことがわかります。実際、多くの場合、これらのモデルには、ドメイン オブジェクトにドメイン ロジックを配置してはならないという設計規則が付属しています。代わりに、すべてのドメイン ロジックをキャプチャする一連のサービス オブジェクトがあります。これらのサービスはドメイン モデルの上にあり、データにドメイン モデルを使用します。
これらの議論を念頭に置いて、DataMapperやGatewayパターンなどを使用して、何らかの形式のバックエンド ストレージに永続化する必要があるドメイン オブジェクトを操作することを検討する必要があります。
代替案
オブジェクトのことはしばらく忘れて、オブジェクトを使用してギャラリーから画像を出力しRevision
たいと考えてみましょう。Slideshow
このクラスは次のようになります。
class Slideshow {
private $gallery;
public function __construct(Gallery $gallery) {
$this->gallery = $gallery;
}
public function play() {
// do something with the gallery here
}
}
php コードは実際にはスライドショーを「再生」するために使用されないという事実を無視してください。これはクライアント側のコードで発生するものです。ここで重要なことは、がCompositionSlideshow
を使用して にアクセスすることです。この構造は、次の理由から、内部を直接ing するよりもはるかに優れています。Gallery
new
Gallery
Slideshow
はSlideshow
プラグ可能になりました。「ギャラリー性」の概念に従う任意のオブジェクトを挿入できます (通常Gallery
、指定されたインターフェイス コントラクトに準拠するように宣言されます)。
はSlideshow
すぐにテスト可能です。Gallery
提供された に適切な画像タイプがない場合、どのように対処すればよいでしょうか? Gallery
a の内部で aを直接インスタンス化する場合、Slideshow
そのような条件をシミュレートする方法はありません。の依存関係を注入することにより、Slideshow
さまざまな操作条件を処理するコードの能力をテストする機会が得られます。
もちろん、別のクラス内でオブジェクトを直接インスタンス化することが適切な場合もあります。このトピックの詳細については、Miško Hevery の記事To "new" or not to "new"のアドバイスをお勧めします。