4

助けと指示を事前に感謝します。私はついに線形計画法からOOPに切り替えています。私はこれまでにファーストクラスに取り組んでおり、少し方向性を使うことができました。私の最初のクラスは、次のプロパティを持つギャラリーです

class Gallery
    {
        //Gallery Name
        public $galleryID;
        public $galleryName;

        //Client Name
        public $clientName;

        //Gallery Options
        public $bg_color;
        public $albumAgreement;
        public $maxChanges;
        public $sharing_on;

        //Revisions
        public $revisions;
}

したがって、私の出力は次のようになります。

Gallery Object
(
    [galleryID] => 
    [galleryName] => 
    [clientName] => 
    [bg_color] => 
    [albumAgreement] => 
    [maxChanges] => 
    [sharing_on] => 
    [revisions] => 
)

次のステップは、「リビジョン」もオブジェクトにして、出力が次のようになるようにすることです。

Gallery Object
(
    [galleryID] => 
    [galleryName] => 
    [clientName] => 
    [bg_color] => 
    [albumAgreement] => 
    [maxChanges] => 
    [sharing_on] => 
    [revisions] => Revisions Object (
        [revisionID] =>
        [revisionName] =>
    )
)

このようなものにはどのような方向に進み、クラスはどのように見えるでしょうか?

ありがとう

4

4 に答える 4

22

これは、ジレンマの原因を説明しているが、解決策を提供していないため、より長い形式のコメントです。

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 のバッグにすぎないことがわかります。実際、多くの場合、これらのモデルには、ドメイン オブジェクトにドメイン ロジックを配置してはならないという設計規則が付属しています。代わりに、すべてのドメイン ロジックをキャプチャする一連のサービス オブジェクトがあります。これらのサービスはドメイン モデルの上にあり、データにドメイン モデルを使用します。

これらの議論を念頭に置いて、DataMapperGatewayパターンなどを使用して、何らかの形式のバックエンド ストレージに永続化する必要があるドメイン オブジェクトを操作することを検討する必要があります。

代替案

オブジェクトのことはしばらく忘れて、オブジェクトを使用してギャラリーから画像を出力し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 するよりもはるかに優れています。GallerynewGallerySlideshow

  1. Slideshowプラグ可能になりました。「ギャラリー性」の概念に従う任意のオブジェクトを挿入できます (通常Gallery、指定されたインターフェイス コントラクトに準拠するように宣言されます)。

  2. Slideshowすぐにテスト可能です。Gallery提供された に適切な画像タイプがない場合、どのように対処すればよいでしょうか? Gallerya の内部で aを直接インスタンス化する場合、Slideshowそのような条件をシミュレートする方法はありません。の依存関係を注入することにより、Slideshowさまざまな操作条件を処理するコードの能力をテストする機会が得られます。

もちろん、別のクラス内でオブジェクトを直接インスタンス化することが適切な場合もあります。このトピックの詳細については、Miško Hevery の記事To "new" or not to "new"のアドバイスをお勧めします。

于 2012-11-16T20:02:34.437 に答える
10

PHPは動的に型付けされるので、コードはほとんど同じです。初期化するときは、次のようにオブジェクトrevisionsのインスタンスになるように初期化するだけです。Revisions

$gallery = new Gallery();
$gallery->revisions = new Revisions()  // assuming you have defined the Revisions class

var_dumpに基づいて、クラスがどのように表示されるかについては、次のようになります。

class Revisions {
  public $revisionID;
  public $revisionName;

  public function __construct($id, $name) {
     $this->revisionID = $id;
     $this->revisionName = $name;
  }

  // if required, define a default constructor as well that does not take any parameters

}

質問から何revisionsが完全に明確ではないので、 (複数形ではなく)Revision代わりにオブジェクトを作成し、の配列にすることをお勧めします。Revisions$gallery->revisionsRevision

于 2012-11-16T19:28:01.597 に答える
0

基本的にRevisions、 の場合と同様に、の別のクラスを作成しますGallery

class Gallery
{
    //Gallery Name
    public $galleryID;
    public $galleryName;

    //Client Name
    public $clientName;

    //Gallery Options
    public $bg_color;
    public $albumAgreement;
    public $maxChanges;
    public $sharing_on;

    //Revisions
    public $revisions;
}
class Revisions {
    public $revisionID;
    public $revisionName;
}

//Set the revisions within gallery
$gallery = new Gallery();
$gallery->revisions = new Revisions();
于 2012-11-16T19:31:05.057 に答える
0

セッターとゲッターまたはコンストラクターについて調査します。以下のそれぞれを使用して、同じ最終結果になる例を示します。

class Gallery
{
    //Gallery Name
    protected $galleryID;
    protected $galleryName;

    //Client Name
    protected $clientName;

    //Gallery Options
    protected $bg_color;
    protected $albumAgreement;
    protected $maxChanges;
    protected $sharing_on;

    //Revisions
    protected $revisions;


    public function setGalleryID($id)
    {
         $this->galleryID = $id;
    }

    public function getGalleryID()
    {
         return $this->galleryID;
    }

    public function setRevisions(Revisions $revisions)
    {
         $this->revisions = $revisions;
    }

    public function getRevisions()
    {
         return $this->revisions;
    }
}

$gallery = new Gallery();
$gallery->setRevisions(new Revisions());
var_dump($gallery->getRevisions());

またはコンストラクタを使用して

class Gallery
{
    //Gallery Name
    protected $galleryID;
    protected $galleryName;

    //Client Name
    protected $clientName;

    //Gallery Options
    protected $bg_color;
    protected $albumAgreement;
    protected $maxChanges;
    protected $sharing_on;

    //Revisions
    protected $revisions;


    public function __construct($id, Revisions $revisions)
    {
         $this->galleryID = $id;
         $this->revisions = $revisions;
    }

    public function getGalleryID()
    {
         return $this->galleryID;
    }

    public function getRevisions()
    {
         return $this->revisions;
    }
}

$gallery = new Gallery(1, new Revisions());
var_dump($gallery->getRevisions());

このようにして、何が入るか、何が出るか、読み取り専用 (取得可能)、書き込み専用 (設定可能)、またはその両方をより詳細に制御できます。たとえば、setter では、$revision の前に Revisions が書き込まれていることがわかります。これにより、リビジョン オブジェクトのみが $this->revisions に設定されるようになります。

于 2012-11-16T19:37:15.033 に答える