4

私は xml ドキュメント (google docs api) を解析し、個々のドキュメントをオブジェクトに入れるのに忙しいです。

ドキュメントにはさまざまな種類があります (ドキュメント、スプレッドシート、プレゼンテーション)。これらのドキュメントに関するほとんどの情報は同じですが、一部が異なります。

アイデアは、特定のドキュメント タイプごとにサブクラスを使用しながら、すべての共有情報を保持するベース ドキュメント クラスを作成することでした。

問題は、さまざまなタイプに適切なクラスを作成することです。ドキュメントのタイプを区別するには、2 つの方法があります。各エントリには、タイプを見つけることができるカテゴリ要素があります。使用される別の方法は、resourceId によるもので、形式はtype:id.

最も単純なオプションは、エントリのタイプをチェックする if ステートメント (または switch ステートメント) を作成し、それに対応するオブジェクトを作成することです。ただし、新しいタイプが追加される場合は、コードを編集する必要があります。

これを解決する別の方法があるかどうかはよくわからないので、ここで質問しています。適切なタイプのオブジェクトの作成をファクトリ メソッドにカプセル化できるので、必要な変更量は最小限ですみます。

今、私はこのようなものを持っています:

public static function factory(SimpleXMLElement $element)
{
    $element->registerXPathNamespace("d", "http://www.w3.org/2005/Atom");
    $category = $element->xpath("d:category[@scheme='http://schemas.google.com/g/2005#kind']");

    if($category[0]['label'] == "spreadsheet")
    {
        return new Model_Google_Spreadsheet($element);
    }
    else
    {
        return new Model_Google_Base($element);
    }
}

私の質問は、この状況を処理するために私が見ていない別の方法はありますか?

編集: サンプルコードを追加

4

3 に答える 3

4

コード例で回答を更新

これがあなたの新しい工場です:

public static function factory(SimpleXMLElement $element)
{
    $element->registerXPathNamespace("d", "http://www.w3.org/2005/Atom");
    $category = $element->xpath("d:category[@scheme='http://schemas.google.com/g/2005#kind']");
    $className = 'Model_Google_ '.$category[0]['label'];
    if (class_exists($className)){
       return new $className($element);
    } else {
        throw new Exception('Cannot handle '.$category[0]['label']);
    }
}

私はあなたの要点を正確に理解しているかどうかわかりません...質問を言い換えると、「クライアントコードで選択をハードコーディングせずに適切なオブジェクトを作成するにはどうすればよいか」を理解しました

オートロードあり

それでは、ベースクライアントコードから始めましょう

class BaseFactory
{
    public function createForType($pInformations)
    {
       switch ($pInformations['TypeOrWhatsoEver']) {
           case 'Type1': return $this->_createType1($pInformations);
           case 'Type2': return $this->_createType2($pInformations);
           default : throw new Exception('Cannot handle this !');
       }
    }
}

ここで、if / switch ステートメントを回避するためにこれを変更できるかどうかを見てみましょう (常に必要というわけではありませんが、必要な場合もあります)。

ここでは、PHP Autoload 機能を使用します。

まず、自動ロードが行われていると考えてください。ここに新しい工場があります

class BaseFactory
{
    public function createForType($pInformations)
    {
       $handlerClassName = 'GoogleDocHandler'.$pInformations['TypeOrWhatsoEver'];
       if (class_exists($handlerClassName)){
           //class_exists will trigger the _autoload
           $handler = new $handlerClassName();
           if ($handler instanceof InterfaceForHandlers){
               $handler->configure($pInformations);
               return $handler;
           } else {
               throw new Exception('Handlers should implements InterfaceForHandlers');
           }
       }  else {
           throw new Exception('No Handlers for '.$pInformations['TypeOrWhatsoEver']);
       }
   }
}

ここで、オートロード機能を追加する必要があります

class BaseFactory
{
    public static function autoload($className)
    {
        $path = self::BASEPATH.
                $className.'.php'; 

        if (file_exists($path){
            include($path); 
        }
    }
}

そして、オートローダーを次のように登録するだけです

spl_autoload_register(array('BaseFactory', 'autoload'));

これで、タイプの新しいハンドラーを作成する必要があるたびに、自動的に追加されます。

責任の連鎖で

複数のタイプを処理するサブクラスを使用して、ファクトリでより「動的」なものを書きたくない場合があります。

例えば

class BaseClass
{
    public function handles($type);
}
class TypeAClass extends BaseClass
{
    public function handles($type){
        return $type === 'Type1';
    }
}
//....

BaseFactory コードでは、すべてのハンドラーをロードして、次のようにすることができます。

class BaseFactory
{ 
    public function create($pInformations)
    {
        $directories = new \RegexIterator(
            new \RecursiveIteratorIterator(
                new \RecursiveDirectoryIterator(self::BasePath)
            ), '/^.*\.php$/i'
        );

        foreach ($directories as $file){
            require_once($fileName->getPathName());
            $handler = $this->_createHandler($file);//gets the classname and create it
            if ($handler->handles($pInformations['type'])){
                return $handler;
            }
        }
        throw new Exception('No Handlers for '.$pInformations['TypeOrWhatsoEver']);
    }
}
于 2011-06-09T09:05:44.887 に答える
1

私は Oktopus に同意します。通常、ハードコーディングを使用しない方法は 2 つあります。最初の方法は、文字列を動的に追加してクラス名を見つけ、クラスに適切な名前が付けられていることを確認することです.2番目の方法は、すべてのハンドラークラスをロードし、そのタイプを処理できると言うものを使用することです. 私は最初に行きます。サンプルコードでは、これは次のようになります。

<?php
public static function factory(SimpleXMLElement $element)
{
    $element->registerXPathNamespace("d", "http://www.w3.org/2005/Atom");
    $category = $element->xpath("d:category[@scheme='http://schemas.google.com/g/2005#kind']");

    $classname = sprintf( 'Model_Google_%s', ucfirst( strtolower( (string) $category[0]['label'] ) ) );

    if( class_exists( $classname, true /* true means, do autoloading here */ ) ) {
        return new $classname( $element );
    }
    return new Model_Google_Base($element);
}

責任の連鎖について: これは記述方法の美しい例ですが、責任の連鎖とは、可能なすべてのハンドラーをロードして、その型を処理できるかどうかを確認する必要があることを意味します。結果として得られるコードは、私の観点からはよりクリーンで明確ですが、パフォーマンスが低下し、複雑さが増します。そのため、動的なクラス名解決を使用します。

于 2011-06-09T09:33:31.037 に答える
0

おそらく、入力ファイルに xsl-transformation を適用できます (ファクトリ メソッドで)。変換の結果は、使用するクラスに関する入力を提供する均一な xml ファイルである必要があります。

これは多くの よりも優れているとは言えませんifが、この方法では、少なくともコードの書き直しを回避できます。

于 2011-06-09T08:55:38.443 に答える