18

依存性注入コンテナをプロジェクトに統合することを検討しています。私が見たすべてのDICは、連想配列や魔法の方法を使用しています。たとえば、Pimpleページのサンプルを次に示します。

$container['session_storage'] = function ($c) {
    return new $c['session_storage_class']($c['cookie_name']);
};

$container['session'] = function ($c) {
    return new Session($c['session_storage']);
};

これには理由がありますか?どこかに表示されるリテラル文字列以外のものとして、コードに文字列を含めるのは嫌いです。IDEの能力の多くを失うことになります(これにより、コードの保守が難しくなり、回避しようとしています!)。

私の好みはもっと似ているでしょう:

class Container {

    function getSessionStorage()
    {
        return new $this->getSessionStorageClass($this->getCookieName);
    }

    function getSession()
    {
        return new Session($this->getSessionStorage());
    }

}

これをしない理由はありますか?このルートに行くと機能しないにきびの魔法が欠けていますか?

4

5 に答える 5

10

ArrayAccessPimpleの拡張機能の「魔法」は、完全に再利用可能で相互運用可能であるということです。DICとしてのPimpleの大きな特徴の1つは、定義されたサービスが以前に定義されたサービスやパラメーターを利用できることです。Session(何らかの理由で)インスタンスを必要とするオブジェクトがあったとしましょうFilter。DICがなければ、次のように書くことができます。

$session = new Session(new Filter);

にきびを使用すると、次のように書くことができます。

$pimple['filter'] = function($c) {
    return new Filter;
};
$pimple['session'] = function($c) {
    return new Session($c['filter']);
}

Pimpleは、Sessionオブジェクトのインスタンス化で以前に登録された「Filter」サービスを使用します。この利点は、を実装するDICに固有のものではありませんArrayAccessが、再利用性はコードの再利用と共有に非常に役立ちます。確かに、特定のサービスまたはそれらすべてのゲッター/セッターをハードコーディングすることはできますが、再利用性の利点はほとんど失われます。

もう1つのオプションは、魔法のメソッドをゲッター/セッターとして使用することです。これにより、DICはコードに必要なAPIに近づき、PimpleArrayAccessコードのラッパーとして使用することもできます(ただし、その時点で専用のDICを作成する方がよい場合があります)。Pimpleの既存のメソッドをラップすると、次のようになります。

public function __call($method, $args) {
    if("set" === substr($method, 0, 3)) {
        return $this[substr($method, 3)];
    }
    if("get" === substr($method, 0, 3) && isset($args[0])) {
        return $this[substr($method, 3)] = $args[0];
    }
    return null;
}

__setまた、次のように、DICのサービスとパラメータへのオブジェクトのようなアクセスを使用して与えることもできます__get:(まだPimpleのArrayAccessメソッドをラップしています)

public function __set($key, $value) {
    return $this[$key] = $value;
}

public function __get($key) {
    return $this[$key];
}

それを超えて、DICを完全に書き直して、魔法のメソッドを排他的に使用し、実装する代わりにオブジェクトのようなAPI構文を使用することもできますがArrayAccess、それはかなり簡単に理解できるはずです:]

于 2013-02-13T00:05:45.593 に答える
6

コンテナをサービスロケータとして使用するため、つまりコンテナを呼び出すため、IDEのオートコンプリートに関心があります。

理想的にはそうすべきではありません。サービスロケーターパターンはアンチパターンです。必要な依存性を注入する(依存性注入)代わりに、コンテナーからそれらをフェッチします。これは、コードがコンテナに結合されていることを意味します

にきび(およびその配列アクセス)は実際にはそれを解決しないので、私はあなたの質問に直接答えていませんが、それがより明確になることを願っています。


補足:「理想的な」方法は何ですか?依存性注入。

アプリケーションのルート(コントローラーの作成など)を除いて、コンテナーを使用したり呼び出したりしないでください。コンテナ全体を注入するのではなく、常に必要なオブジェクト(依存関係)を注入します。

于 2014-05-19T07:42:58.440 に答える
1

Pimpleは、配列のようにアクセスできるように設計されています(ArrayAccessインターフェイスを実装します)。代わりにメソッドのようなインターフェースが必要な場合は、Pimpleを拡張して、__call()魔法のメソッドを使用してください。

class Zit extends Pimple
{
    public function __call($method, array $args)
    {
        $prefix = substr($method, 0, 3);
        $suffix = isset($method[3])
                ? substr($method, 3)
                : NULL;

        if ($prefix === 'get') {
            return $this[$suffix];
        } elseif ($prefix === 'set') {
            $this[$suffix] = isset($args[0])
                           ? $args[0]
                           : NULL;
        }
    }
}

使用法:

$zit = new Zit();

// equivalent to $zit['Foo'] = ...
$zit->setFoo(function() {
    return new Foo();
});

// equivalent to ... = $zit['Foo']
$foo = $zit->getFoo();

Pimpleにこの機能がすぐに付いてこない理由については、私にはわかりません。おそらくそれをできるだけ単純に保つためだけです。


編集:

IDEオートコンプリートに関しては、このような魔法のメソッドでは使用できません。@property一部のエディターでは、 andを使用して、これを補うためのdoc-blockヒントを提供できると@method思います。

于 2013-02-12T23:46:30.413 に答える
1

高性能で構成可能性を維持したいので、唯一のオプションはDIコンテナーコードを生成することです。

簡単なオプションは、必要なメソッドを準備し、ジェネレーターを作成することです。このようなもの(テストされていないコード、インスピレーションのみ):

$config_file = 'config.ini';
$di_file = 'var/di.php';
if (mtime($config_file) > mtime($di_file) // check if config changed
    || mtime(__FILE__) > mtime($di_file)  // check if generator changed
{ 
    $config = parse_ini_file($config_file, true); // get DI configuration
    ob_start(); // or use fopen($di_file) instead
    echo "<", "?php\n",
        "class DIContainer {\n";
    foreach ($config_file as $service_name => $service) {
        // generate methods you want, use configuration in $service as much as possible
        echo "function create", $service_name, "() {\n",
             "  return new ", $service['class'], "();\n\n";
    }
    echo "}\n";
    file_put_contents($di_file, ob_get_contents());
    ob_end_clean();
}

require($di_file);
$dic = new DIContainer();

使用法:

$service = $dic->createSomeService();
// Now you have instance of FooBar when example config is used

設定ファイルの例:

[SomeService]
class = "FooBar"

[OtherService]
class = "Dummy"
于 2013-02-13T00:18:29.043 に答える
0

最終的には、2つのアプローチのハイブリッドになりました。

dic Pimpleの内部で物事を管理し、外部のオブジェクトはゲッターを介して取得されます。

例えば

abstract class DicAbstract {

    /**
     * @var \Pimple
     */
    protected $_dic;

    /**
     * Initialise the pimple container
     */
    public function __construct()
    {
        $this->_dic = new \Pimple();

        $this->defineContainer();
    }

    /**
     * Define dependency items
     */
    protected abstract function defineContainer();
}

class Dic extends DicAbstract {

    /**
     * @return \Component\Error\Manager
     */
    public function errorManager()
    {
        return $this->_dic['errorManager'];
    }

    /**
     * @return SomethingElse
     */
    public function somethingElse()
    {
        return $this->_dic['somethingElse'];
    }

    /**
     * Define the container
     */
    protected function defineContainer()
    {
        $this->_dic['errorTypesGeneral'] = function() {
            return new \Component\Error\Type\General();
        };

        $this->_dic['errorTypesSecurity'] = function() {
            return new \Component\Error\Type\Security();
        };

        $this->_dic['errorManager'] = function($dic) {
            $errorManager = new \Component\Error\Manager();

            $errorManager->registerMessages($dic['errorTypesGeneral']);
            $errorManager->registerMessages($dic['errorTypesSecurity']);

            return $errorManager;
        };
    }

}
于 2013-02-26T15:02:36.777 に答える