存取者模式

2023-04-05 18:01:25

begin 2023年04月02日15:56:19

引子

悲觀者往往正確,樂觀者往往成功

定義

Represent an operation to be performed on the elements of an object structure.
Visitor lets you define a new operation without changing the classes of the elements
on which it operates.

表示在物件結構的元素上執行的操作。存取者模式(Visitor)允許您定義一種新操作,而無需更改它所作用的元素的類。——《設計模式:可複用物件導向軟體的基礎》

存取者模式是一種行為型設計模式。

使用場景

  • an object structure contains many classes of objects with differing
    interfaces, and you want to perform operations on these objects that depend
    on their concrete classes.
  • many distinct and unrelated operations need to be performed on objects in
    an object structure, and you want to avoid "polluting" their classes with
    these operations. Visitor lets you keep related operations together by
    defining them in one class. When the object structure is shared by many
    applications, use Visitor to put operations in just those applications that
    need them.
  • the classes defining the object structure rarely change, but you often want
    to define new operations over the structure. Changing the object structure
    classes requires redefining the interface to all visitors,which is
    potentially costly. If the object structure classes change often, then it's
    probably better to define the operations in those classes.
  • 物件機構包含了許多具有不同介面的物件類,您希望對這些物件執行依賴於其具體類的操作。
  • 需要對物件結構中的物件執行許多不同且不相關的操作,並且您希望避免這些操作「汙染」它們的類。Visitor允許將相關操作定義在一個類中,從而將它們放在一起。當物件結構被許多應用程式共用時,使用Visitor將操作放在需要它們的應用程式中。
  • 定義物件結構的類很少改變,但是您經常希望在類上定義新的操作。更改物件結構類需要為所有存取者重新定義介面,這可能成本很高。如果物件結構類經常變化,那麼最好在這些類中定義操作。

圖示

存取者模式結構圖:

角色

抽象存取者角色(Visitor):

  • 為物件結構中的每個 ConcreteElement 類宣告一個 visit 操作。
  • 操作的名稱和簽名(visitConcreteElementA、visitConcreteElementB)標識向存取者(ConcreteVisitorA、ConcreteVisitor)傳送 visit 請求的類,讓存取者可以確定所存取元素的具體類(ConcreteElementA、ConcreteElementB)。
  • 然後存取者可以通過元素特定的介面直接存取該元素。

具體存取者角色(ConcreteVisitorA、ConcreteVisitorB):

  • 實現存取者宣告的每個操作。
  • 每個操作實現為物件結構中對應的物件類定義的演演算法片段(針對具體元素所執行的操作,如獲取ConcreteElementA的名字等)
  • ConcreteVisitor 為演演算法提供了上下文,並儲存其本地狀態。該狀態通常在遍歷物件結構時積累結果。

物件結構角色(ObjectStructure):

  • 可以列舉其元素。
  • 可以提供一個高層介面,以允許存取者存取其元素。
  • 可以是一個組合(參見組合模式)或者一個集合,如列表或集合

抽象元素角色(Element):

  • 定義一個接受存取者作為引數的 Accept 操作。

具體元素角色(ConcreteElementA、ConcreteElementB):

  • 實現一個接受存取者作為引數的 Accept 操作。

程式碼範例

生命主要分為過去、現在、未來,樂觀者看到不念過去,不畏將來,立足現在努力,悲觀者看到悔恨過去,迷茫未來,或不知所措的現在,同樣的生命不同的人看到不同的世界。

程式碼範例類圖:

程式碼範例:

抽象存取者角色:

// 人類
public interface Man {
    void visitPast(Past past);
    void visitPresent(Present present);
    void visitFuture(Future future);
}

具體存取者角色:

// 樂觀主義者
public class Optimist implements Man {
    @Override
    public void visitPast(Past past) {
        System.out.println("不念" + past.getName());
    }

    @Override
    public void visitPresent(Present present) {
        System.out.println("享受" + present.getName());
    }

    @Override
    public void visitFuture(Future future) {
        System.out.println("不畏" + future.getName());
    }
}
// 悲觀主義者
public class Pessimist implements Man {
    @Override
    public void visitPast(Past past) {
        System.out.println("悔恨" + past.getName());
    }

    @Override
    public void visitPresent(Present present) {
        System.out.println("焦慮" + present.getName());
    }

    @Override
    public void visitFuture(Future future) {
        System.out.println("迷茫" + future.getName());
    }
}

物件結構角色:

// 生命
public class Life {
    private List<Time> timeList = new ArrayList<>();

    public Life() {
        timeList.add(new Past());
        timeList.add(new Present());
        timeList.add(new Future());
    }

    public void visitTime(Man man) {
        for (Time time : timeList) {
            if (time instanceof Past) {
                man.visitPast((Past) time);
            } else if (time instanceof Present) {
                man.visitPresent((Present) time);
            } else if (time instanceof Future) {
                man.visitFuture((Future) time);
            }
        }
    }
}

抽象元素角色:

// 時間
public interface Time {
    void accept(Man man);
    String getName();
}

具體元素角色:

// 過去
public class Past implements Time {

    private String name = "過去";

    @Override
    public void accept(Man man) {
        man.visitPast(this);
    }

    public String getName() {
        return name;
    }
}
// 現在
public class Present implements Time {

    private String name = "現在";

    @Override
    public void accept(Man man) {
        man.visitPresent(this);
    }
    public String getName() {
        return name;
    }
}
// 未來
public class Future implements Time {

    private String name = "未來";

    @Override
    public void accept(Man man) {
        man.visitFuture(this);
    }

    public String getName() {
        return name;
    }
}

使用者端:


public class Client {
    public static void main(String[] args) {
        Optimist optimist = new Optimist();
        Pessimist pessimist = new Pessimist();

        Life life = new Life();
        System.out.println("樂觀者:");
        life.visitTime(optimist);
        System.out.println("悲觀者:");
        life.visitTime(pessimist);
    }
}

結果:

樂觀者:
不念過去
享受現在
不畏未來
悲觀者:
悔恨過去
焦慮現在
迷茫未來

優點

  • 操作和結構分開,易於新增操作

缺點

  • 修改結構需要修改所有操作類,成本極大

總結

存取者莫斯允許您定義一種新操作,而無需更改它所作用的元素的類。存取者模式適用於結構固定,但是操作不固定的物件,它把物件結構和作用於結構的操作耦合解開,方便增加新的操作。

end 2023年04月05日15:22:07