3

たとえば、 Knp バンドルからサード パーティのバンドルを追加する場合、最初にそれをラップする必要がありますか、それとも自分のコードで直接使用する必要がありますか?

ラッピングすることにした場合、ラッピング コードはどこに配置すればよいですか? 別の新しいバンドルで?私のアプリケーションバンドルで?

明確にするために:

プロジェクトにサードパーティのバンドルを追加する方法については質問していません。バンドルとは何かを尋ねているのではありません。

この質問は、ラッパー クラスの背後にあるサード パーティのコードをカプセル化することを目的としています。バンドルはサード パーティの開発者によって開発されたものであるため、コードが破損する可能性のある予期しない変更が発生する可能性があります。

プロジェクトに追加した後、サードパーティのバンドルをどのようにラップしますか?

4

2 に答える 2

4

これは、一般的に Symfony2 に含まれるサードパーティのバンドルに対する回答でありcomposer、特別なバンドルについて言及するものではありません。

初めに

要求されたバンドルのバージョンを安定したバージョン ( など1.*) に修正composer.jsonしている限り (および開発者が独自のガイドラインに従っている限り)、インターフェースの互換性の問題に問題はないはずです。ラッピングは不要です。

しかし、ラッパー コードで例外をスローしたり、フォールバックを実装したりして、コードの中断を防ぎたいと考えています。これにより、ラッパー コードを使用するすべてのものが引き続き機能するか、少なくとも適切なエラーが表示されます。

ラッピングしたい場合

dev-master特定のサードパーティ バンドルのバージョンを使用する場合、大幅な変更が発生する可能性があります。しかし、安定したバージョンがあるときに本当にを含めたい場合はありません。dev-master

dev-masterとにかく、エラーを表示したり、ログに記録したり、例外をキャッチしたりするためにエラーを含めたい場合、またはラップしたい場合に意味があると思われる2つの方法があります。

サードパーティ バンドルのサービスのすべてのインスタンスを使用する単一のサービス クラスを構築する

このサービス クラスは、サード パーティのバンドルを使用するバンドルの 1 つに含まれている可能性があります。このアプローチでは、追加のバンドルは必要ありません。

acme.thirdparty.clientこのようにして、他のサービスの単一のメソッド呼び出しをラップするような単一のサービスを作成できます。必要なすべてのサードパーティ サービスを注入し (または必要なサブクラスのインスタンスを作成し)、必要なすべてのメソッド呼び出しをラップする必要があります。

# src/Acme/MyBundle/Resources/config/services.yml
parameters:
    acme.thirdparty.wrapper.class: Acme\MyBundle\Service\WrapperClass

services:
    acme.thirdparty.wrapper:
        class: %acme.thirdparty.wrapper.class%
        arguments:
            someService: @somevendor.somebundle.someservice
            someOtherService: @somevendor.somebundle.someotherservice

そしてサービスクラス:

<?php
namespace Acme\MyBundle\Service;

use SomeVendor\SomeBundle\SomeService\ConcreteService;
use SomeVendor\SomeBundle\SomeService\OtherConcreteService;

class WrapperClass
{
    private $someService;

    private $someOtherService;

    public function __construct(ConcreteService $someService, OtherConcreteService $someOtherService)
    {
        $this->someService = $someService;
        $this->someOtherService = $someOtherService;
    }

    /**
     * @see SomeVendor\SomeBundle\SomeService\ConcreteService::someMethod
     */
    public function someMethod($foo, $bar = null)
    {
        // Do stuff
        return $this->someService->someMethod();
    }

    /**
     * @see SomeVendor\SomeBundle\SomeService\ConcreteOtherService::someOtherMethod
     */
    public function someOtherMethod($baz)
    {
        // Do stuff
        return $this->someOtherService->someOtherMethod();
    }
}

次に、これらのメソッド呼び出しにエラー処理を追加して (すべての例外をキャッチしてログに記録するなど)、サービス クラスの外部のコードが破損するのを防ぐことができます。ただし、言うまでもなく、これによってサードパーティ バンドルの予期しない動作が防止されるわけではありません。

または、次のことができます。

複数のサービスを含むバンドルを作成し、それぞれがサード パーティ バンドルの 1 つのサービスをラップします。

バンドル全体には、正確にラップしたいものに対してより柔軟であるという利点があります。サービス全体または単一のリポジトリのみをラップし、ラップされたクラスを独自のものに置き換えることができます。DI コンテナーを使用すると、次のように、注入されたクラスをオーバーライドできます。

# src/Acme/WrapperBundle/Resources/config/services.yml
parameters:
    somevendor.somebundle.someservice.class: Acme\WrapperBundle\Service\WrapperClass

クラス パラメータをオーバーライドすることにより、somevendor.somebundle.someservice.classこのクラスを使用するすべてのサービスが のインスタンスになりますAcme\WrapperBundle\Service\WrapperClass。このラッパー クラスは、基本クラスを拡張することができます。

<?php
namespace Acme\WrapperBundle\Service;

use SomeVendor\SomeBundle\SomeService\ConcreteService;

class WrapperClass extends ConcreteService
{
     /**
      * @see ConcreteService::someMethod
      */
     public function someMethod($foo, $bar = null)
     {
         // Do stuff here
         parent::someMethod($foo, $bar);
         // And some more stuff here
     }
}

...または元のクラスのインスタンスを使用してラップすることもできます:

<?php
namespace Acme\WrapperBundle\Service;

use SomeVendor\SomeBundle\SomeService\ConcreteServiceInterface;
use SomeVendor\SomeBundle\SomeService\ConcreteService;

class WrapperClass implements ConcreteServiceInterface
{
    private $someService;

    /**
     * Note that this class should have the same constructor as the service. 
     * This could be achieved by implementing an interface
     */
    public function __construct($foo, $bar)
    {
        $this->someService = new ConcreteService($foo, $bar);
    }

     /**
      * @see ConcreteService::someMethod
      */
     public function someMethod($foo, $bar = null)
     {
         // Do stuff here
         $this->someService->someMethod($foo, $bar);
         // And some more stuff here
     }
}

別のクラスをオーバーライドしているクラスのインターフェイスを実装することが必須になる場合があることに注意してください。また、2 番目のものは最良のアイデアではない可能性がありますConcreteService。これは、単に置き換えるだけでなく、実際に をラップしているのかどうかがはっきりしないためです。また、これは依存性注入の考え方全体を無視しています。

このアプローチは、より多くの作業を必要とし、より多くのテストを意味しますが、より柔軟性が必要な場合は、これが適しています。

おそらく、目的のサード パーティ バンドルのラッパー バンドルが既に存在する可能性があります ( のSensioBuzzBundle場合Buzz Browserなど)。

結論

開発者を信頼し、安定したバージョン (1.*バグ修正と新機能、または1.0.*バグ修正のみなど) を含めることが道です。安定したバージョンがない場合、または を含めたい場合はdev-master、ラッピングがオプションです。コードをラップする場合は、追加のバンドルを構築する方がより柔軟な方法ですが、ラップするコードがあまりない場合は、単一のサービス クラスで十分な場合があります。

于 2013-12-03T12:50:16.423 に答える