目的
定義能逐一施行於物件結構裡各個元素的操作,讓你不必修改作用對象的類別介面,就能定義新的操作;將操作集中到物件結構上,以讓它們能獨立變化,但仍以多型方式進行。
Centralize operations on an object structure so that they can vary independently but still behave polymorphically.
適用
- 物件結構含有許多介面各異的類別,希望能依據物件的實體類別來執行對應的動作時。
- 需要對整個物件結構體系執行各種互不相干的操作,不會造成干擾。
- 物件結構的類別不常變動,但施於其上的操作卻常有增減時。
結構及成員
Collaborations:
Client先建立一個ConcreteVisitor物件,再於巡訪物件結構的過程中,將Visitor物件餵給每一個遇到的Element。
Element被巡訪到時,會呼叫Visitor相對應的操作,如有必要,也會把自己列入參數傳送過去讓Visitor存取。
影響結果
好處
壞處
實作
雙重分派(Double Dispatch)?
- 在單一分派程式語言,呼叫哪個Overload方法,由方法呼叫者和方法參數共同在執行期間依靜態類別決定。
如:以下程式碼所有的ride()出來都會是「騎馬」,因為他們的靜態類別都是Horse。
public class Mozi {
public void ride(Horse h){ System.out.println("騎馬"); }
public void ride(WhiteHorse wh){ System.out.println("騎白馬"); }
public void ride(BlackHorse bh){ System.out.println("騎黑馬"); }
public static void main(String[] args) {
Horse wh = new WhiteHorse();
Horse bh = new BlackHorse();
Mozi mozi = new Mozi();
mozi.ride(wh);
mozi.ride(bh);
}
}
但JAVA可以透過Override實作動態分派
public class Horse {
public void ride(){ System.out.println("騎馬"); }
}
public class WhiteHorse extends Horse {
public void ride(){ System.out.println("騎白馬"); }
}
public class BlackHorse extends Horse {
public void ride(){ System.out.println("騎黑馬"); }
}
public class Mozi {
public static void main(String[] args) {
Horse wh = new WhiteHorse();
Horse bh = new BlackHorse();
wh.ride();
bh.ride();
}
}
- Visitor模式將真正會啟用的操作,依Visitor的型別和作用對象的型別決定,不用把靜態操作繫結在Element介面裡。
誰負責巡訪物件結構?
有三種角色可以幫Visitor一一拜訪物件結構裡的每一個元素:物件結構、Visitor物件、Iterator物件。
Example: Liquor
//Interface: Visitable
package liquor;
interface Visitable {
public double accept(Visitor visitor);
}
//Class: Liquor
package liquor;
class Liquor implements Visitable {
private double price;
Liquor(double item) {
price = item;
}
public double accept(Visitor visitor) {
return visitor.visit(this);
}
public double getPrice() {
return price;
}
}
//Class: Necessity
package liquor;
class Necessity implements Visitable {
private double price;
Necessity(double item) {
price = item;
}
public double accept(Visitor visitor) {
return visitor.visit(this);
}
public double getPrice() {
return price;
}
}
//Class: Tobaco
package liquor;
class Tobacco implements Visitable {
private double price;
Tobacco(double item) {
price = item;
}
public double accept(Visitor visitor) {
return visitor.visit(this);
}
public double getPrice() {
return price;
}
}
//Interface: Visitor
package liquor;
interface Visitor {
public double visit(Liquor liquorItem);
public double visit(Tobacco tobaccoItem);
public double visit(Necessity necessityItem);
}
//Class: TaxVisitor
package liquor;
import java.text.DecimalFormat;
class TaxVisitor implements Visitor {
DecimalFormat df = new DecimalFormat("#.##");
public TaxVisitor() {
}
public double visit(Liquor liquorItem) {
System.out.println("Liquor Item: Price with Tax");
return Double.parseDouble(df.format((liquorItem.getPrice() * .18) + liquorItem.getPrice()));
}
public double visit(Tobacco tobaccoItem) {
System.out.println("Tobacco Item: Price with Tax");
return Double.parseDouble(df.format((tobaccoItem.getPrice() * .32) + tobaccoItem.getPrice()));
}
public double visit(Necessity necessityItem) {
System.out.println("Necessity Item: Price with Tax");
return Double.parseDouble(df.format(necessityItem.getPrice()));
}
}
//Class: TaxHolidayVisitor
package liquor;
import java.text.DecimalFormat;
class TaxHolidayVisitor implements Visitor {
DecimalFormat df = new DecimalFormat("#.##");
public TaxHolidayVisitor() {
}
public double visit(Liquor liquorItem) {
System.out.println("Liquor Item: Price with Tax");
return Double.parseDouble(df.format((liquorItem.getPrice() * .10) + liquorItem.getPrice()));
}
public double visit(Tobacco tobaccoItem) {
System.out.println("Tobacco Item: Price with Tax");
return Double.parseDouble(df.format((tobaccoItem.getPrice() * .30) + tobaccoItem.getPrice()));
}
public double visit(Necessity necessityItem) {
System.out.println("Necessity Item: Price with Tax");
return Double.parseDouble(df.format(necessityItem.getPrice()));
}
}
//Class: VisitorTest
package liquor;
public class VisitorTest {
public static void main(String[] args) {
TaxVisitor taxCalc = new TaxVisitor();
TaxHolidayVisitor taxHolidayCalc = new TaxHolidayVisitor();
Necessity milk = new Necessity(3.47);
Liquor vodka = new Liquor(11.99);
Tobacco cigars = new Tobacco(19.99);
System.out.println(milk.accept(taxCalc) + "\n");
System.out.println(vodka.accept(taxCalc) + "\n");
System.out.println(cigars.accept(taxCalc) + "\n");
System.out.println("TAX HOLIDAY PRICES\n");
System.out.println(milk.accept(taxHolidayCalc) + "\n");
System.out.println(vodka.accept(taxHolidayCalc) + "\n");
System.out.println(cigars.accept(taxHolidayCalc) + "\n");
}
}
Result:
Necessity Item: Price with Tax
3.47
Liquor Item: Price with Tax
14.15
Tobacco Item: Price with Tax
26.39
TAX HOLIDAY PRICES
Necessity Item: Price with Tax
3.47
Liquor Item: Price with Tax
13.19
Tobacco Item: Price with Tax
25.99