定義:特定のデータ構造の各要素に作用する特定の操作をカプセル化します。データ構造を変更せずに、これらの要素に作用する新しい操作を定義できます。
タイプ:行動パターン
クラス図:
例:
たとえば、さまざまな種類の商品をカートに追加することを検討し、チェックアウトをクリックすると、すべての異なる商品に対して支払われる料金を計算します。現在、計算ロジックは、これらのさまざまな種類の商品の価格を計算することです。または、言い換えれば、このロジックを訪問者モードを介して別のクラスに転送します。訪問者パターンのこの例を実装しましょう。
訪問者パターンを実装するために、最初に行うべきことは、さまざまな種類のアイテム(アイテムエレメント)を表すためにショッピングカートに追加できるクラスを作成することです。
IteamElement.javapackage com.journaldev.design.visitor; public interface itemelement {public int(ShoppingCartVisitor Visitor);}Acceptメソッドは、訪問者をパラメーターとして受け入れることに注意してください。もちろん、ここで詳細な製品を指定する他の方法もありますが、簡単にするために、ここではあまりにも多くの詳細を検討する必要はなく、訪問者モードに焦点を当てているだけです。
ここで、さまざまな製品のエンティティクラスを作成します。
book.java
パッケージcom.journaldev.design.visitor; public class bookを実装します{private int price;プライベート文字列isbnnumber;パブリックブック(int cost、string isbn){this.price = cost; this.isbnnumber = isbn; } public int getPrice(){return price; } public string getisbnnumber(){return isbnnumber; } @Override public int Accept(ShoppingCartVisitor Visitor){return visitor.visit(this); }}fruit.java
パッケージcom.journaldev.design.visitor; public class Fruit Implements ItemElement {private int priceperkg;プライベートINT重量;プライベート文字列名;パブリックフルーツ(int pricekg、int wt、string nm){this.priceperkg = pricekg; this.weight = wt; this.name = nm; } public int getPricePerkg(){return priceperkg; } public int getWeight(){return weight; } public string getname(){return this.name; } @Override public int Accept(ShoppingCartVisitor Visitor){return visitor.visit(this); }} Accept()メソッドの実装はエンティティクラスにあることに注意してください。エンティティクラスは、訪問者のVisit()メソッドを呼び出して、現在のクラスオブジェクトを独自のパラメーターとして渡すことです。
ここでは、さまざまな種類の商品に使用されるVisit()メソッドが、Visitor Interfaceのエンティティクラスに実装されます。
Shoppingcartvisitor.java
パッケージcom.journaldev.design.visitor; public interface shoppingcartvisitor {int visit(book book); int visit(フルーツフルーツ);}Visitor Interfaceが実装され、各製品の独自の費用を計算する論理が実装されます。
Shoppingcartvisitorimpl.java
パッケージcom.journaldev.design.visitor; public class shoppingcartvisitorimpl Shoppingcartvisitor {@override public int visit(book book){int cost = 0; // book価格が50を超える場合、5ドルの割引を適用しますif(book.getprice()> 50){cost = book.getprice()-5; } else cost = book.getPrice(); system.out.println( "book isbn ::"+book.getisbnnumber()+"cost ="+cost);返品コスト。 } @Override public int Visit(Fruit Fruit){int Cost = Fruit.getPricePerkg()*Fruit.getWeight(); System.out.println(fruit.getName() + "cost =" + cost);返品コスト。 }}それでは、プログラムで使用する方法を見てみましょう。
ShoppingCartClient.java
パッケージcom.journaldev.design.visitor; public class shoppingcartclient {public static void main(string [] args){itemelement [] items = new book(20、 "1234")、new Book(100、 "5678")、new(10、2、 "Banana")、new Fruit(5、5、 "Apple") int total = calculateprice(items); System.out.println( "合計コスト="+合計); } private static int calculatePrice(itemelement [] items){ShoppingCartVisitor Visitor = new ShoppingCartVisitorImpl(); int sum = 0; for(itemelement item:item){sum = sum + item.accept(visitor); } return sum; }}上記のプログラムを実行するとき、次の出力を取得します。
本ISBN :: 1234コスト= 20book ISBN :: 5678コスト= 95バナナコスト= 20Appleコスト= 25トーダルコスト= 160
ここの実装は、すべての製品のAccept()メソッドと同じであるように思われますが、異なる場合もあります。たとえば、製品が空の場合、論理チェックを実行でき、visit()メソッドを呼び出さなくなります。
訪問者モードの利点:
単一の責任の原則に従う:訪問者モードが適用されるシナリオでは、要素クラスの訪問者にカプセル化する必要がある操作は、要素クラス自体とはほとんど関係がなく、揮発性のある操作でなければなりません。一方では、訪問者モードの使用は単一の責任の原則に準拠しており、他方では、カプセル化された操作が通常揮発性であるため、変更が発生すると、要素クラス自体を変更せずに変更部品の拡張を達成できます。
優れたスケーラビリティ:要素クラスは、さまざまな訪問者を受け入れることで異なる操作を拡張できます。
訪問者モードに適用されるシナリオ:
オブジェクトにオブジェクトに関連していない(または弱く関連する)操作があり、これらの操作がオブジェクトを汚染するのを避けるために、訪問者モードを使用してこれらの操作を訪問者にカプセル化することができます。
オブジェクトのグループに同様の操作がある場合、多数の重複コードを回避するために、これらの重複操作を訪問者にカプセル化することもできます。
ただし、ビジターモードはそれほど完璧ではなく、致命的な欠陥もあります。新しい要素クラスを追加することはより困難です。訪問者パターンのコードを通じて、訪問者クラスでは、各要素クラスに対応する処理方法があることがわかります。つまり、各要素クラスを追加するには、訪問者クラス(訪問者クラスのサブクラスまたは実装クラスも含まれます)を変更する必要があります。つまり、要素クラスの数が不確かな場合、訪問者モードに注意して使用する必要があります。したがって、訪問者モードは、既存の機能のリファクタリングにより適しています。たとえば、プロジェクトの基本的な機能が決定されている場合、要素クラスのデータは基本的に決定されており、変更されません。変更されるのは、これらの要素内の関連する操作だけです。この時点で、Visitorモードを使用して元のコードをリファクタリングして、各要素クラスを変更せずに元の機能を変更できるようにすることができます。
要約:
デザインパターンの著者であるGOFは、訪問者モードについて説明しています。ほとんどの場合、訪問者モードを使用する必要がありますが、必要になると、本当に必要です。もちろん、これは本当の大物のためだけです。現実には(少なくとも私がいる環境では)、多くの人々はしばしば設計パターンに夢中になっています。デザインパターンを使用する場合、使用しているパターンがこのシナリオに適しているかどうかを真剣に検討することはありませんが、オブジェクト指向のデザインを制御する能力を示したいだけです。プログラミング時にこのメンタリティがある場合は、デザインパターンを悪用することがよくあります。したがって、設計パターンを学習する場合、パターンの適用性を理解する必要があります。パターンを使用することは、その利点を理解しているため、パターンを使用する必要があります。パターンを使用しないでください。パターンを使用するのではなく、その欠点を理解していないため、その利点を理解していないためパターンを使用しないようにします。