4

アプリケーションを「正しく」書く方法について質問があります。

サービス/DAO/マッパー/エンティティ モデルに従いたいと思います。完全に理解していると思いますが、やりたいことがこのモデルの一般的な概念と衝突する可能性があることがわかりました。

私はこれらのデータベーステーブルを持っています: 製品テーブルとその関連テーブル - メーカー

データベースから製品のデータを取得し、製品エンティティとして返すシンプルな ProductDbMapper があるので、次のように使用できます。

echo $productEntity->getName();
$productEntity->setPrice(29.9);

次のように何かを使うことができれば素晴らしいと思います:

echo $productEntity->getManufacturer()->getName();

「getManufacturer()」メソッドは、ID によってメーカーの別のサービス/DAO を照会するだけです。製品の製造元を取得する正しい方法は次のとおりであることを知っています。

$product = $productService->getProduct(5);
$manufacturer = $manufacturerService->getManufacturerByProduct($product);

しかし、「流れるような」ソリューションははるかに単純で、理解しやすく、使用するのが楽しいと思います。実際、それはかなり自然なことです。これをコールバックで実装しようとしました。メーカーのサービスを呼び出すコールバックを ProductMapper の Product エンティティに渡しました。

問題は、私が 5 層モデルに従おうとしていると同時に、それを回避しようとしているように見えることです。だから私の質問は: これは良い解決策のように見えますか? それは理にかなっていますか?どうすれば同じ(魔法)をより良い方法で達成できますか?

4

3 に答える 3

9

Data Mapper パターンに固執したい場合は、すべての依存関係 (メーカー、在庫、税金) を含むデータベースから製品を読み込むか、遅延読み込みを使用できます。最初のオプションはお勧めできません。ただし、遅延読み込みを使用するには、仮想プロキシを介して取得した追加のレイヤーが必要になります。

なんで?そうしないと、エンティティ内にいくつかのデータベース コードを配置する必要があり、それらのレイヤーに関する全体的なアイデアが分離されているためです。

では、仮想プロキシとは何ですか?

Martin Fowler (2003) によると:

仮想プロキシは、フィールドにあるはずのオブジェクトのように見えるオブジェクトですが、実際には何も含まれていません。そのメソッドの 1 つが呼び出されたときにのみ、データベースから正しいオブジェクトがロードされます。

インターフェイスは、実際のエンティティと仮想プロキシによって実装されるメソッドを定義します。

// The interface
interface ManufacturerInterface
{
    public function getName();
}

これはエンティティです。おそらくいくつかのジェネリック モデル クラスも拡張しています。

// The concrete Manufacturer class
class Manufacturer implements ManufacturerInterface
{
    private $name;

    public function __construct($name)
    {
        $this->name = $name;
    }

    public function getName()
    {
        return $this->name;
    }
}

そして、これはプロキシです:

// The proxy
class ManufacturerProxy implements ManufacturerInterface
{
    private $dao;
    private $manufacturer;
    private $manufacturerId;

    public function __construct($dao, $manufacturerId)
    {
        $this->dao = $dao;

        // set manufacturer to NULL to indicate we haven't loaded yet
        $this->manufacturer = null;
    }

    public function getName()
    {
        if ($this->manufacturer === null)
        {
            $this->lazyLoad();
        }
        return $this->manufacturer->getName();
    }

    private function lazyLoad()
    {
        $this->manufacturer = $this->dao->getById($this->manufacturerId);
    }
}

プロキシは、製品など、製造元と何らかの関係を持つ他のクラスをインスタンス化するときに、Data Mapper によって使用されます。そのため、製品がインスタンス化されると、そのフィールドが入力され、manufacturer フィールドは Manufacturer の代わりに ManufacturerProxy のインスタンスを受け取ります。

Data Mapper の実装は異なるため、簡単な例を示します。

class ProductMapper {
    private $dao;
    private $manufacturerDao;

    // .......

    public function find($id) {
        // call the DAO to fetch the product from database (or other source)
        // returns an associative array
        $product = $this->dao->find($id);
        
        // instantiate the class with the associative array
        $obj = new Product($product);
        
        // create the virtual proxy
        $obj->manufacturer = new ManufacturerProxy($this->manufacturerDao, $product['manufacturer_id']);
        
        return $obj;
    }
}

前述したように、上記の例は非常に単純です。あなたが使用している DAO を含めましたが、Martin Fowler のような作成者は、SQL クエリやその他の基礎となるテクノロジを処理するタスクを Data Mapper に任せています。しかし、Nock (2004) のように、Data Mapper と Data Accessor の両方を採用している著者もいます。

より複雑な Data Mapper では、XML や YAML のような言語、Doctrine や Hibernate に似た言語のファイルしか持つことができませんでした。

最後に、サービス:

class ProductService {
    private $productMapper;
    
    /**
     * Execute some service
     * @param {int} $productId The id of the product
     */
    public function someService($producId) {
        // get the product from the database
        // in this case the mapper is handling the DAO
        // maybe the data mapper shouldn't be aware of the DAO
        $product = $this->productMapper->find($productId);
        
        // now the manufacturer has been loaded from the database
        $manufacturerName = $product->manufacturer->getName();
    }
}

おそらく、DAO で Data Mapper を呼び出してエンティティを作成し、サービスで DAO を使用することができます。メソッドを 2 回繰り返す必要がないため、実際にはより簡単ですが、それはあなた次第です。

オルタナティブ

仮想プロキシを実装したくないが流暢なインターフェイスが必要な場合は、Active Record パターンに切り替えることができます。PHP-ActiveRecordParisなど、この作業を実行できるライブラリがいくつかあります。または、自分でやりたい場合は、こちらこちらをご覧ください。Martin Fowler の本も良いスタートです。

参考文献

フォウラー、マーティン。エンタープライズ アプリケーション アーキテクチャのパターン。Addison-Wesley、2003 年。NOCK、CLIFTON。データ アクセス パターン: オブジェクト指向アプリケーションにおけるデータベースの相互作用。アディソン・ウェズリー、2004年。

于 2012-12-01T20:22:42.577 に答える
3

私は数日間いくつかの調査を行ってきましたが、私が望むものを達成するための最良の方法は、自分でコーディングしようとするのをやめ、既存の ORM の使用法を学ぶことであるという結論に達しました。私がやろうとしてきたことはすべて、独自の ORM を作成することだったからです。そして、私はこれが悪い考えであることを知っています。Doctrine 2 に固執します。

于 2012-12-07T18:07:51.457 に答える
1

誤解しないでほしいのですが、私は批評家になりたくありません。これらの 2 つのテーブルを結合して、1 つのクラスだけが別のクラスを拡張しないのはなぜですか? あなたが持っているように$this->manufacturer_name = $row['name']。また、他のメーカーの行についても同様です。

于 2012-12-01T18:45:49.847 に答える