web-dev-qa-db-ja.com

名前付きコンストラクタをファクトリパターンで処理する

現在のプロジェクトでは、コードをリファクタリングしてDBALを取得しています。データベーステーブルをモデル化するすべてのクラスの基本クラスである_class Entity_があります。したがって、EntityDocumentなど、Articleから継承するクラスがいくつかあります。

_abstract class Entity {
    /** @var DatabaseRequest $dbReuest */
    protected $dbRequest;

    /** @var Login $login */
    protected $login;

    /* some methods like insert(), update(), JsonSerialize(), etc.

}
_

これらすべてのクラスには同じコンストラクタ__construct( DatabaseRequest $dbRequest, Login $login )があり、これらの2つのパラメータを使いたくないので、これも作成しました。

_class EntityFactory {

  public function __construct( DatabaseRequest $dbRequest, Login $login )
  {
      $this->dbRequest = $dbRequest;
      $this->login = $login;
  }

  public function makeEntity( string $class )
  {

    if ( $this->extendsEntity( $class ) ) {

        $reflection = new \ReflectionClass( $class );
        $construct = $reflection->getConstructor();
        $params = $construct->getParameters();


        return new $class( clone $this->dbRequest, clone $this->login );
    }

    throw new APIException( "Class $class does not extend " . Entity::class, JsonResponse::DEBUG );
  }

}
_

次のようなメソッドを呼び出します:$factory->makeEntity( Document::class )これにより、そのクラスのオブジェクトが取得されます。

このように、Entityコンストラクターの変更により、リファクタリングの労力が最小限に抑えられました。ただし、EntityIを拡張するクラスでは、テーブル間の関係についていくつかのメソッドも定義しました。例えば。:

_class DocumentAddressee extends Entity {

    /* ... */

    public static function createFromCustomer( Address $address )
    {
        $self = new DocumentAddressee( clone $address->dbRequest, clone $address->login );

        /* transferring data from Address to $self */

        return $self;
    }

}
_

verraes.net によれば、これは、名前付きコンストラクタとしての静的メソッドの合法的な使用です)。

そして、このようなメソッドはかなり頻繁に発生します(テーブル内の外部キーごとにおよそ1〜2つのメソッド)。この方法で依存データに簡単にアクセスできるので、これらのメソッドを保持したいと思います。ただし、これらのコンストラクターをファクトリーに保持したいので、Entityコンストラクターが変更されたときに100以上のEntityクラスすべてをリファクタリングする必要はありません(これは、将来QueryBuilderを使用する場合に発生する可能性があります。

これらのメソッドを処理するためのベストプラクティスはすでにありますか?これらの関係をファクトリ内で適切に処理するか、それらの関係を追加のクラスでモデル化する必要がありますか?

3
Tekay37

理想的には、一貫性のためにこれらのオブジェクトを作成するためにファクトリを使用するだけです。したがって、工場では次のようなものが得られます。

_class EntityFactory {

    public function __construct( DatabaseRequest $dbRequest, Login $login )
    {
        $this->dbRequest = $dbRequest;
        $this->login = $login;
    }

    public function create($className)
    {
        $reflection = new \ReflectionClass($className);
        if ($reflection->implementsInterface(Initialisable::class)) {
            throw new APIException( "Please use createFromEntity() to create $className");
        }
        return new $class( clone $this->dbRequest, clone $this->login );
    };

    public function createFromEntity(Entity $entity, $className)
    {
        $newEntity = $this->create($className);
        return $newEntity->initWithEntity($entity);
    }
}
_

基本的な実装として、initWithEntity()Entityクラスに追加することができます。

_class Entity {

    public function initWithEntity(Entity $entity) {
        $this->dbRequest = clone $entity->dbRequest;
        $this->login = clone $entity->login;
        return $this;
    }
}
_

そして、更新されたInitialisableを備えたDocumentAdresseeインターフェイスは次のとおりです。

_interface Initialisable {
    public function initWithEntity(Entity $entity);
}

class DocumentAdressee implements Initialisable {
     // got initWithEntity() from parent class
}
_

initWithEntity()を使用すると、EntityEntityFactoryプロパティへのアクセスを開く必要がないため、リフレクションを使用する必要がありません。 DocumentAdresseeオブジェクトを作成するには、以下を使用します。

_$factory->createFromEntity($address, DocumentAdressee::class);
_

これは、エンティティが1対1の関係を持つことができる、たとえば、 DocumentAdressee。 1対多の関係があるクラスの場合、initWithEntities()のようなものを作成し、Entitysの配列を渡す必要があります。

ベストプラクティスについては、使用または確認できるORMがあります。教義。

編集

作成するクラスをエンティティで初期化する必要があるかどうかを確認できるように、Initialisableインターフェイスを追加しました。

2
imel96