組合せサブルーチン設計方法への再構築について


ioc containerでcoを説明するのはいい考えではないような気がします.
このcontainerの例を挙げると、明らかに意見を出した人はその簡単なロゴの例よりずっと少ない.
結局picoがどういうことなのか、どのように使うのか、多くの人はまだ見られない.picoの使い方はinのfancy factoryであることは言うまでもない.珠を抜く.
でも、始まった以上、私には終わりがあるでしょう.
この章では、coのrefactorを見てみましょう.
実は、多くの人がcoの中の基本的な組み合わせの度をどのように把握するかと聞いています.どのような組み合わせが基本ですか.どのように直交するか.どれだけの基本的な組み合わせが足りるか.どうしてこの組み合わせが使われるかなど知っていますか.
実は、答えはすべて再構築から来ています.
だれも急に正しいことをする人はいない.coはooよりもデザイン的に過剰なデザインを避けやすいと思います.
どうして?
ooを設計する時、あなたは需要を分析して、各モジュールの通信インタフェースを設計して、この過程、同じように経験が必要で、同じように模索しなければならなくて、同じように1つの近道がありません.
しかし,oo設計の際には過度を避ける必要があり,インタフェースを介して柔軟性を残したり,変化しやすい部分を抽出したり,できるだけ簡単な間に衝突したりすることがある.難しい推測と選択が必要です
いったん選択が行われると、後で物事が望ましくないことに気づいたら、インタフェースを変える代価はかなり大きい.
coを使うと、簡単な組み合わせを設計するときに、非常に漸進的な方法で発見されます.ああ、元の組み合わせのデザインは直交していません.この場所で抽出することができます.はい、抽出して、波及したいくつかの組み合わせのデザインを修正します.
組み合わせが非常に簡単なため、この変化の波及範囲は一般的にかなり小さい.
はい、空論は少なく、具体的な例を見てみましょう.
次に、withArgument、withPropertyのほかに、パラメータをより柔軟に設定したいことを発見しました.例えば、
[リスト]コンポーネントXの各パラメータ、タイプAに対して、「a 1」で識別されるコンポーネントをパラメータ値として選択し、その他はデフォルトで選択する.
コンポーネントYの各パラメータについて、タイプAのものは、「a 2」で識別されるコンポーネントをパラメータ値として選択し、その他はデフォルトで選択する.[/list:u]
このニーズにはいくつかの点があります.
[list]1.keyでコンポーネントを直接指定できる必要があります.「ref」に相当します.
2.パラメータの配置には、パラメータの位置に加えて、より柔軟な構成(パラメータのタイプなど)が必要です.[/list:u]
第一に、以下の組み合わせを作成して対応します.(ほら、私たちは需要に応じて私たちの基本的な組み合わせの集合を豊かにすることができますが)
コンテナから別のキーで識別されたコンポーネントを取得し、すべての動作をdelegateすることができるUseKeyコンビネーションを期待しています.
class UseKey extends Component{
  private final Object key;
  public Object create(Dependency dep);{
    //?????????
  }
  ....
}

しかし、最初にコードを書くと、このコードは書けないことに気づきました!この容器を手に入れる必要があります.この容器からdelegateのコンポーネントを手に入れることができます.しかし、このかわいい容器の対象はどこですか?
よく分析してみると、仕方がない.唯一の方法は、Dependencyインタフェースを修正し、パラメータとpropertyの解析を支援するほか、現在のコンテナの情報を提供することです.
Dependencyインタフェースは次のようになります.
interface Dependency{
  Object getArgument(int i, Class type);;
  Object getProperty(Object key, Class type);;
  Container getContainer();;
}

Wow!インタフェースを変更します!実は、この点も怖くない.どうして?
coにはもう一つの利点があります.詳細なパッケージについては言及していません.このパッケージは一般的なOOの意味でのパッケージではなく、実装するインタフェースの詳細をパッケージ化し、ooのようにユーザーにこのインタフェースを実装させるのではなく、事前に定義された組み合わせで顧客を拡張させるということです.
実際、ユーザーがすべてComponentオブジェクトを使用している場合、Componentオブジェクトを作成するには、次の手順に従います.
Container.getInstance(Object key);;

この方法では、Dependencyというインタフェースは実際に私たちの内部実装の詳細になっています.ユーザーは、このようなインタフェースが存在することを知る必要はありません.
実際,我々の組合せ子が十分に豊富になると,Dependencyインタフェースをパケット内部に完全に隠すことができ,このインタフェースを徹底的にユーザに遮蔽することができる.
このように、お客様の拡張は、Componentインタフェースを実装し、Dependencyインタフェースを呼び出すのではなく、Componentオブジェクトを組み合わせることによって完全に行われます.
このDependencyインタフェースがどのように設計され、どのように変化しても、ユーザーに影響を与えることなく、変化をパッケージ内に隔離することができます.
いいですよ.Dependencyインタフェースを変更したと仮定すると、UseKeyは次のように書くことができます.
class UseKey extends Component{
  private final Object key;
  public Object create(Dependency dep);{
     final Component c = dep.getContainer();.getComponent(key);;
     if(c==null);throw new ComponentNotFoundException(...);;
     return c.create(dep);;
  }
  ....
}

次に、より柔軟なパラメータ構成を行います.これに対してbind操作を参考にして,パラメータに対するbindを作ることができる.
interface ParameterBinder{
  Component bind(int i, Class type);;
}


BinderインタフェースとParameterBinderインタフェースから何か見えますか?
1.Binder,ParameterBinderインタフェースはいずれもユーザに実現される.
2.この2つのインタフェースはいずれもComponentの詳細を暴露せず、それらのパラメータと戻り値はComponentのインタフェース署名に関与せず、お客様はこの2つのインタフェースを実現する際に、Dependencyインタフェースのような詳細に全く関心を持つ必要はありません.
3.戻り値はすべてComponentで、すべてのComponentコンビネーションサブが自由に使用できます.
実際,monad組合せ子は,このようにして高次論理の階層に下位の詳細を隠す.
class ParameterBoundDependency implements Dependency{
  private final Dependency dep;
  private final ParameterBinder binder;
  public Object getArgument(int i, Class type);{
    return binder.bind(i, type);.create(dep);;
  }
  ...
}
ParameterBoundComponent extends Component{
  private final Component c;
  private final ParameterBinder binder;
  public Object create(Dependency dep);{
    return c.create(new ParameterBoundDependency(dep, binder););;
  }
  ...
}

ParameterBinderを用いてDependencyのdecoratorを作り,問題は解決された.
次にParameterBoundComponentを用い,書きやすいようにComponentクラスにbind(ParameterBinder binder)という関数があると仮定する.さらに、Componentsクラスには、コンテナ内の別のコンポーネントを指すためのuseKey(Object key)関数があり、Componentオブジェクトを生成します.
そこで、上記のニーズは次のように実現されます.
Component x = ...;
Component x2 = x.bind(new ParameterBinder();{
  public Component bind(int i, Class type);{
    if(type.equals(A.class););{
      return Components.useKey("a1");;
    }
    else{
      //????  1
    }
  }
});;

このx 2コンポーネントは、「パラメータタイプがAの場合、a 1を使用し、そうでなければデフォルトを使用する」ことを実現するためです.
しかし、行1カ所で、再び障害に遭遇した.このいわゆる「デフォルト方式」は、どのように表しますか?
.
考えた結果,現在のDependencyオブジェクトの中から任意のパラメータを自分の値としてアクティブに選択できるuseArgument(int i,Class type)という組合せ子を実現することにした.これで、上の行1を書くことができます.
Component x = ...;
Component x2 = x.bind(new ParameterBinder();{
  public Component bind(int i, Class type);{
    if(type.equals(A.class););{
      return Components.useKey("a1");;
    }
    else{
      return Components.useArgument(i, type);;//  1
    }
  }
});;

次に、UseArgumentクラスを実装します.

class UseArgument extends Component{
  private final int i;
  private final Class type;
  public Object create(Dependency dep);{
    return dep.getArgument(i, type);;
  }
  .....
}

ああ.完璧です.すべては依然として掌握している.
customizerコンポーネントのパラメータとpropertyは、ほとんどの方法で使用できます.
実際、振り返ってみると、withArgument(int i,Class type)はbind(ParameterBinder)で書き換えることができます.

Component withArgument(Component c, final int i, final Component arg);{
  return c.bind(new ParameterBinder();{
    public Component bind(int k, Class type);{
      if(k==i); return arg;
      else return Components.useArgument(k, type);;
    }
  });;
}

元のWithArgument類もWithProperty類もゴミ箱に入れることができて嬉しいです.私たちはもっと簡単なParameterBinderインタフェースを実現するだけですべてを解決することができます.ああ.
また、これらの特定のWithArgument、ValueComponentクラスを非表示にし、静的ファクトリ関数withArgument()、value()で置き換えるメリットも見てほしい.
私たちは自由に再構築することができます.ある組合せ子自体が最も簡単ではなく、より簡単な組合せ子から推論できることを発見したとき、私たちはこれらの静的工場関数を変更するだけで、ユーザーに言う必要はありません:申し訳ありませんが、私の設計は変更して、WithArgumentクラスが欲しくなくて、あなたはあなたのnew WithArgument(...)を変更することができます.のコード?
coは,ある機能が直接実現されているのか,あるいは組み合わせられているのかにかかわらず,ユーザにインタフェースだけに注目させる.静的ファクトリ関数はこの詳細のカプセル化を提供する.
もう1つの一般的な要件は、コンポーネントのすべてのパラメータを1つの配列で一度に指定することです.たとえば、次のようなものです.
c.withArguments(new Component[]{c1, c2, c3});;

この機能はbindで非常によく実現されています.
Component withArguments(final Component[] args);{
  return bind(new ParameterBinder();{
    public Component bind(int i, Class type);{
      return args[i];
    }
  });;
}

もちろん、他のカスタムパラメータやpropertyの方法を挙げることもできます.
はい.今日はここまで.終了する前に、2つの新しいニーズを提案します.
1.Loggerオブジェクトを使用するクラスに対してLoggerインスタンスを注射することが望ましいが、このLoggerインスタンスは、毎回StackTraceを取得するために例外を構築することなく、Loggerオブジェクトを使用するクラスオブジェクトで作成する必要がある.
たとえば、
new ClassX(..., Loggers.instance(ClassX.class);, ...);;

このルールをコンテナレベルでグローバルに規定するにはどうすればいいですか?どのコンポーネントがLoggerを注射する必要があるのか、どのパラメータでLoggerオブジェクトを注射する必要があるのか分からない.
2.デフォルトのパラメータはどのように指定しますか?このように、パラメータの必要に応じてコンテナで解析できる場合は、この解析されたインスタンスを保持します.そうでない場合は、デフォルトのコンポーネントを使用します.
次の節では,coの再構成過程をこの2つの例で説明し続ける.