2017-10-24 20 views
0

2つのオブジェクトを1つのオプショナルオブジェクトに結合し、一般的でクリーンなAPIを維持したいと思います。例を提供するには:PHPが2つのクラスを組み合わせたPHPの場合、1つはコンポジションでオプションになります

class Composition implements Object1Interface, Object2Interface 
{ 
    private $object1; 

    private $object2; 

    public function __construct($object1, $object2 = null) 
    { 
     $this->$object1 = $object1; 
     $this->$object2 = $object2; 
    } 

    // This will always work since the object is mandatory. 
    public function getId() { 
     return $this->object1->getId(); 
    } 

    // This may fail because the object2 can be null. 
    public function getName() { 
     return $this->object2->getName(); 
    } 
} 

をあなたは私がここにnullオブジェクトにプロキシすることができので、これはかなり速い失敗を開始することを見ることができました。これを解決するにはどうすればよいでしょうか?

$object2を作成する必要がありますが、すべてのコンストラクタのデータが常に$object2になっていない可能性がありますが、コンストラクタのオプションとしてすべての引数を設定する必要があります。これはもはや必須の引数を持たないため、大きなノーノのように思えます。

オプションは$object2を返すメソッドを作成することですが、これはそうのように、ユーザが連鎖する必要があります。これは許容することができ

$composition->getObject2()->getName() 

が、私は私が持っているクリーンなAPIを破壊していますし、今の連鎖していますメソッド。

これを修正するためにここで行うことができるものはありますか、または単に前述のメソッドソリューションの連鎖と一緒に行く必要がありますか?

+0

'$ object2'にアクセスしてもそれが供給されなかった場合、どのような動作が期待できますか?また、連鎖はあまり役に立ちませんが、コード内の別の箇所で失敗するだけです。 – Yoshi

+0

'public function getName(){if($ this-> object2)は$ this-> object2-> getName()を返します。新しい\ Exception( '...')をスロー} ' –

答えて

1

私はnull-safe operatorのようなものだと思います。

Hackとは異なり、PHPにはこの演算子(該当するRFC)がないため、nullの値はexplicitly checkである必要があります。また、Option Typeのようなものを使用することもできます。

class Composition implements Object1Interface, Object2Interface 
{ 
    private $object1; 

    // ... 

    public function __construct(Object1Interface $object1 = null, Object2Interface $object2 = null) 
    { 
     if ($object1 !== null) { 
      $this->object1 = $object1; 
     } else { 
      $this->object1 = new class implements Object1Interface 
      { 
       public function getId() 
       { 
        return null; 
       } 
      }; 
     } 

     // ... 
    } 

    public function getId() { 
     return $this->object1->getId(); // Always works. 
    } 
} 
1

あなたは2つのオプションがあります:どちらかが常にオブジェクトはそれから何かを返す前に設定されているかどうかを確認、またはの代わりに「ダミー」オブジェクトを使用し、少なくともあなたは、動的依存オブジェクトの「デフォルト」バージョンを作成することができます提供されていない場合はユーザー提供のものです。

あなたはすべてのものの周りに警備員を追加する必要があるので、前者はかなり乱雑になる可能性があるので、私は個人的に後者を好むでしょう。

後者のほうが簡単に実装できることの1つは、インターフェイスを使用して期待オブジェクトの仕様を指定し、実際のオブジェクトが指定されていない場合はコンストラクタでダミーオブジェクトをインスタンス化することです。

interface AThingThatDoesSomething 
{ 
    public function getValue() : integer 
} 

class RealClass implements AThingThatDoesSomething 
{ 
    public function getValue() : integer 
    { 
     return mt_rand(); 
    } 
} 

class DummyClass implements AThingThatDoesSomething 
{ 
    public function getValue() : integer 
    { 
     // You could return a dummy value here, or throw an exception, or whatever you deem to be the correct behaviour for the dummy class. This implementation just returns 0 for simplicity 
     return 0; 
    } 
} 

class ConsumingClass 
{ 
    private $requiredInstance = null; 
    private $optionalInstance = null; 

    public function __construct(AThingThatDoesSomething $requiredInstance, AThingThatDoesSomething $optionalInstance = null) 
    { 
     if (null === $optionalInstance) 
     { 
      $optionalInstance = new DummyClass(); 
     } 

     $this->requiredInstance = $requiredInstance; 
     $this->optionalInstance = $optionalInstance; 
    } 

    public function getRequiredVal() : integer 
    { 
     return $this->requiredInstance->getValue(); 
    } 

    // You don't need to worry if the user supplied an optional instance here because if they didn't then the dummy instance will have been instantiated instead 
    public function getOptionalVal() : integer 
    { 
     return $this->optionalInstance->getValue(); 
    } 
} 

これは不自然な例のように見えるかもしれません、そしてもちろん、あなたは正しいだろうが、それはまた、設計によって契約と呼ばれるパターンの利点の一つを示しています。オブジェクトが特定の基準(この場合はインタフェースを実装すること)を満たすことを約束している限り、オブジェクトが実際に何もしない場合であっても、その基準を満たすオブジェクトを代用することができます。

実生活で私はロギングが必要なクラスでこれを使用します。私はpsr \ logパッケージを使用し、コンストラクタで新しいNullLoggerを設定します。実際のロギングが必要な場合は、ロガーを渡すためにsetLogger()を使用しますが、そうでなければ、常に設定されているので$ this-> loggerが失敗することを心配する必要はありません。

関連する問題