23種類の設計モード(16)java訪問者モード


23種類のデザインモード第16編:java訪問者モード
定義:パッケージはある種のデータ構造における各要素の動作に作用し、データ構造を変化させない前提でこれらの要素に作用する新しい動作を定義することができる。
タイプ:行動パターン
クラス図:

訪問者モードは行動パターンの中で一番複雑なパターンかもしれませんが、それを把握しない理由にはなりません。
まず簡単な例を見てみます。コードは以下の通りです。

class A { 
    public void method1(){ 
      System.out.println("  A"); 
    } 
     
    public void method2(B b){ 
      b.showA(this); 
    } 
  } 
   
class B { 
  public void showA(A a){ 
    a.method1(); 
   } 
}
主にクラスAで方法method 1と方法method 2の違いはどこですか?方法method 1は簡単です。つまり、「私はAです。」メソッドmethod 2はやや複雑で、クラスBをパラメータとして使用し、クラスBのshowAメソッドを呼び出します。
また、クラスBのshowA方法を見てください。showA方法はクラスAをパラメータとして使用して、クラスAのmethod 1方法を呼び出すと、method 2メソッドが回り道していることが分かります。自分のmethod 1メソッドを呼び出しただけです。その結果も「私はAです。」と分析した後、この二つの方法を実行してみます。

public class Test { 
    public static void main(String[] args){ 
      A a = new A(); 
      a.method1(); 
      a.method2(new B()); 
    } 
  }
実行結果は:
私はAです
私はAです
       この例を読めば、訪問者パターンの90%が理解できます。例では、クラスAにとって、クラスBは訪問者です。しかし、この例は訪問者モードの全部ではなく、直観的ですが、その拡張性は比較的に悪いです。以下では、訪問者モードの共通の実現について説明します。
抽象的な訪問者:抽象的なクラスまたはインターフェースは、訪問者がどのような要素にアクセスできるかを声明しています。具体的には、プログラムにvisit方法におけるパラメータ定義は、どのようなオブジェクトがアクセスできるかを示しています。
訪問者:抽象的な訪問者の声明を実現する方法は、訪問者が一つのクラスに訪問した後、何をすべきかに影響を及ぼします。
抽象元素類:インターフェースまたは抽象類、どのタイプの訪問者のアクセスを受け入れるかを宣言します。プログラムはaccept方法のパラメータで定義されます。抽象的な要素は普通は2種類の方法があって、一部は自分の業務の論理で、その他にどの種類の訪問者を受け入れることができますか?
元素類:抽象元素類が声明しているaccept方法を実現するには、一般的にはvisitor.visitであり、基本的にはもう一つの定型が形成されています。
構造オブジェクト:1つの要素の容器は、一般的に、List、Set、Mapなど、複数の異なるインターフェースを格納する容器を含み、プロジェクトでは、この役割を抽象化することが少ない。
訪問者モードの共通コードの実現

abstract class Element { 
    public abstract void accept(IVisitor visitor); 
    public abstract void doSomething(); 
  } 
   
  interface IVisitor { 
    public void visit(ConcreteElement1 el1); 
    public void visit(ConcreteElement2 el2); 
  } 
   
  class ConcreteElement1 extends Element { 
    public void doSomething(){ 
      System.out.println("    1"); 
    } 
     
    public void accept(IVisitor visitor) { 
      visitor.visit(this); 
    } 
  } 
   
  class ConcreteElement2 extends Element { 
    public void doSomething(){ 
      System.out.println("    2"); 
    } 
     
    public void accept(IVisitor visitor) { 
      visitor.visit(this); 
    } 
  } 
  class Visitor implements IVisitor { 
   
    public void visit(ConcreteElement1 el1) { 
      el1.doSomething(); 
    } 
     
    public void visit(ConcreteElement2 el2) { 
      el2.doSomething(); 
    } 
  } 
   
  class ObjectStruture { 
    public static List<Element> getList(){ 
      List<Element> list = new ArrayList<Element>(); 
      Random ran = new Random(); 
      for(int i=0; i<10; i++){ 
        int a = ran.nextInt(100); 
        if(a>50){ 
          list.add(new ConcreteElement1()); 
        }else{ 
          list.add(new ConcreteElement2()); 
        } 
      } 
      return list; 
    } 
  } 
   
  public class Client { 
    public static void main(String[] args){ 
      List<Element> list = ObjectStruture.getList(); 
      for(Element e: list){ 
        e.accept(new Visitor()); 
      } 
    } 
  }
訪問者モードのメリット
単一の職責原則に適合する:すべての訪問者モードが適用される場面では、要素類の中に封入される必要がある操作は、要素類そのものとは大きくなく、変化しやすい操作であることが間違いない。訪問者モードを使用することは、単一の職責原則に合致する一方、パッケージされた操作のために、通常は変わりやすいものである。元素類そのものを変えない前提で、変化部分に対する拡張を実現することができます。
拡張性は良好です。元素類は異なる訪問者を受け入れることによって、異なる操作に対する拡張を実現できます。
訪問者モードの適用シーン
       一つのオブジェクトの中に本オブジェクトと関係のない(または関係が弱い)操作が存在する場合、これらの操作が汚染されないように訪問者モードを使用してこれらの操作を訪問者にカプセル化することができる。
       同じような動作がある場合は、大量の重複コードが発生しないように、これらの重複した操作を訪問者にカプセル化することもできる。
       しかし、訪問者モードはそんなに完璧ではなく、致命的な欠陥もあります。新しい元素類を増やすのは難しいです。訪問者モードのコードによって見られますが、訪問者クラスでは、各要素クラスに対応する処理方法があります。つまり、要素クラスを増やすごとに、訪問者クラス(訪問者クラスのサブクラスや実現クラスも含む)を修正する必要があります。つまり、要素クラスの数が不確定な場合は、訪問者モードを慎重に利用するべきである。したがって、訪問者モードは既存の機能の再構成に適しています。例えば、一つのプロジェクトの基本機能が確定しました。元素類のデータは基本的に決まっています。これらの要素の中の関連操作だけになります。この時、訪問者モードを使って既存のコードを再構築することができます。各元素類を修正しないで、元の機能を修正できます。
締め括りをつける
       「デザインモード」の著者GoFが訪問者モードについて説明しているように、多くの場合、訪問者モードを使用する必要がありますが、いざ使用すると、それが本当に必要になります。もちろんこれは本当の大牛に対してだけです。現実的には(少なくとも私が置かれている環境の中で)多くの人がデザインモードに夢中になっています。彼らはデザインモードを使う時に、このような場面に合うかどうか真剣に考えないでください。プログラミングする時にこのような心理があって、よく設計のモードを濫用する情況が発生します。ですから、デザインモードを学ぶときは、モードの適用性を理解する必要があります。一つのパターンを使用しなければならないのは、その長所を知るためであり、一つのパターンを使用しないのは、その欠点を理解するためである。一つのパターンを使うのではなく、その弊害を知らないので、一つのパターンを使わないのは、その長所を知らないからです。
以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。