溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android中怎么實現(xiàn)訪問者模式

發(fā)布時間:2021-06-29 15:42:03 來源:億速云 閱讀:147 作者:Leah 欄目:移動開發(fā)

本篇文章給大家分享的是有關Android中怎么實現(xiàn)訪問者模式,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

一、介紹

訪問者模式是一種將數(shù)據(jù)操作與數(shù)據(jù)結構分離的設計模式,它是《設計模式》中23種設計模式中最復雜的一個,但它的使用頻率并不高,正如《設計模式》的作者GOF對訪問者模式的描述:大多數(shù)情況下,你不需要使用訪問者模式,但是當你一旦需要使用它時,那你就是真的需要它了。

訪問者模式的基本想法是,軟件系統(tǒng)中擁有一個由許多對象構成的、比較穩(wěn)定的對象結構,這些對象的類都擁有一個accept方法用來接受訪問者對象的訪問。訪問者是一個接口,它擁有一個visit方法,這個方法對訪問到的對象結構中不同類型的元素作出不同的處理。在對象結構的一次訪問過程中,我們遍歷整個對象結構,對每一個元素都實施accept方法,在每一個元素的accept方法中會調(diào)用訪問者的visit方法,從而使訪問者得以處理對象結構的每一個元素,我們可以針對對象結構設計不同的訪問者類來完成不同的操作,達到區(qū)別對待的效果。

二、定義

封裝一些作用于某種數(shù)據(jù)結構中的各元素的操作,它可以在不改變這個數(shù)據(jù)結構的前提下定義作用于這些元素的新的操作。

三、使用場景

對象結構比較穩(wěn)定,但經(jīng)常需要在此對象結構上定義新的操作。

需要對一個對象結構中的對象進行很多不同的并且不相關的操作,而需要避免這些操作”污染“這些對象的類,也不希望在增加新操作時修改這些類。

四、訪問者模式的UML類圖

UML類圖:

Android中怎么實現(xiàn)訪問者模式

角色介紹:

Visitor:接口或抽象類,定義了對每一個元素的訪問行為,參數(shù)就是可訪問的元素,方法個數(shù)理論上是個元素個數(shù)一樣的。因此,訪問者模式要求被訪問的對象結構要穩(wěn)定,如果經(jīng)常增刪元素,必然會導致頻繁修改Visitor接口,就不適合用訪問者模式了。

ConcreteVisitor:具體的訪問者,定義具體的對每一個元素的具體訪問行為。

Element:抽象的元素接口或抽象類,定義了一個接待訪問者的方法,讓每個元素都可以被訪問者訪問。

ElementA,ElementB:具體的元素類,提供接收訪問方法的具體實現(xiàn)。這個具體實現(xiàn)通常是調(diào)用訪問者提供的訪問該元素的方法。

ObjectStructure:定義對象結構,里面維護了一個元素的集合,并且迭代這些元素供訪問者訪問。

五、簡單示例

情景:年終了,公司會給員工進行業(yè)績考核。但是,不同領域的管理人員對于員工的評定標準不一樣。現(xiàn)在員工有工程師和經(jīng)理,評定者有CEO和CTO,我們假定CTO只關注工程師的代碼量、經(jīng)理的新產(chǎn)品數(shù)量,而CEO關注的是工程師的KPI和經(jīng)理的KPI以及新產(chǎn)品數(shù)量。

員工基類:

/**
 * 員工基類(Element) 
 */
public abstract class Staff {
  //員工姓名
  public String name;
  //員工KPI
  public int kpi;
  public Staff(String name) {
    super();
    this.name = name;
    this.kpi = new Random().nextInt(10);
  }
  //接受Visitor的訪問
  public abstract void accept(Visitor visitor);
}

工程師:

/**
 * 工程師 
 */
public class Engineer extends Staff{
  private int codeLines;//代碼數(shù)量
  public Engineer(String name) {
    super(name);
    codeLines = new Random().nextInt(10 * 10000);
  }
  @Override
  public void accept(Visitor visitor) {
    visitor.visit(this);
  }
  //工程師這一年寫的代碼數(shù)量
  public int getCodeLines(){
    return codeLines;
  }
}

經(jīng)理:

/**
 * 經(jīng)理
 */
public class Manager extends Staff{
  private int products;//產(chǎn)品數(shù)量
  public Manager(String name) {
    super(name);
    products = new Random().nextInt(10);
  }
  @Override
  public void accept(Visitor visitor) {
    visitor.visit(this);
  }
  //一年內(nèi)做的產(chǎn)品數(shù)量
  public int getProducts(){
    return products;
  }
}

Visitor類:

public interface Visitor {
  /**
   * 訪問工程師類型
   */
  public void visit(Engineer engineer);
  /**
   * 訪問經(jīng)理類型
   */
  public void visit(Manager manager);
}

CEO訪問者:

public class CEOVisitor implements Visitor {
  @Override
  public void visit(Engineer engineer) {
    System.out.println("工程師:" + engineer.name + ", KPI:" + engineer.kpi);
  }
  @Override
  public void visit(Manager manager) {
    System.out.println("經(jīng)理:" + manager.name + ", KPI:" + manager.kpi
        + ", 新產(chǎn)品數(shù)量 :" + manager.getProducts());
  }
}

CTO訪問者:

public class CTOVisitor implements Visitor {
  @Override
  public void visit(Engineer engineer) {
    System.out.println("工程師:" + engineer.name + ", 代碼數(shù)量:" + engineer.getCodeLines());
  }
  @Override
  public void visit(Manager manager) {
    System.out.println("經(jīng)理:" + manager.name +", 產(chǎn)品數(shù)量 :" + manager.getProducts());
  }
}

員工報表:

//員工業(yè)務報表類(ObjectStructure)
public class BusinessReport {
  List<Staff> mStaffs = new LinkedList<Staff>();
  public BusinessReport() {
    mStaffs.add(new Manager("王經(jīng)理"));
    mStaffs.add(new Engineer("工程師-A"));
    mStaffs.add(new Engineer("工程師-B"));
    mStaffs.add(new Manager("李經(jīng)理"));
    mStaffs.add(new Engineer("工程師-C"));
  }
  /**
   * 為訪問者展示報表 
   * @param visitor 如CEO、CTO
   */
  public void showReport(Visitor visitor){
    for(Staff staff : mStaffs){
      staff.accept(visitor);
    }
  }
}

Client訪問:

public class Client {
  public static void main(String[] args) {
    //構建報表
    BusinessReport report = new BusinessReport();
    System.out.println("===== 給CEO看報表 =====");
    //設置訪問者CEO
    report.showReport(new CEOVisitor());
    System.out.println("===== 給CTO看報表 =====");
    //設置訪問者CTO
    report.showReport(new CTOVisitor());
  }
}

結果:

===== 給CEO看報表 =====
經(jīng)理:王經(jīng)理, KPI:2, 新產(chǎn)品數(shù)量 :5
工程師:工程師-A, KPI:5
工程師:工程師-B, KPI:7
經(jīng)理:李經(jīng)理, KPI:9, 新產(chǎn)品數(shù)量 :8
工程師:工程師-C, KPI:1
===== 給CTO看報表 =====
經(jīng)理:王經(jīng)理, 產(chǎn)品數(shù)量 :5
工程師:工程師-A, 代碼數(shù)量:26238
工程師:工程師-B, 代碼數(shù)量:8282
經(jīng)理:李經(jīng)理, 產(chǎn)品數(shù)量 :8
工程師:工程師-C, 代碼數(shù)量:47927

從上面代碼中可以看出,如果要增加一個訪問者,你新創(chuàng)建一個實現(xiàn)了Visitor接口的類,然后實現(xiàn)兩個visit方法來對不同的元素進行不同的操作,從而達到數(shù)據(jù)對象與數(shù)據(jù)操作相分離的效果。如果不使用訪問者模式,而又想對不同元素進行不同的操作,那么必定會使用if-else和類型轉換,這使得代碼難以升級維護。

六、Android中的訪問者模式

安卓中的著名開源庫ButterKnife、Dagger、Retrofit都是基于APT(Annotation Processing Tools)實現(xiàn)。而編譯注解核心依賴APT。當我們通過APT處理注解時,最終會將獲取到的元素轉換為相應的Element元素,以便獲取到它們對應信息。那么元素基類的源碼如下:(路徑:javax.lang.model.element.Element)

public interface Element extends javax.lang.model.AnnotatedConstruct {
  /**
   * Returns the {@code kind} of this element.
   *
   * @return the kind of this element
   */
  ElementKind getKind();//獲取元素類型
  //代碼省略
  /**
   * Applies a visitor to this element.
   *
   * @param <R> the return type of the visitor's methods
   * @param <P> the type of the additional parameter to the visitor's methods
   * @param v  the visitor operating on this element
   * @param p  additional parameter to the visitor
   * @return a visitor-specified result
   */
  <R, P> R accept(ElementVisitor<R, P> v, P p);//接受訪問者的訪問
}

ElementVisitor就是訪問者類型,ElementVisitor源碼如下:

public interface ElementVisitor<R, P> {
  /**
   * Visits an element.
   * @param e the element to visit
   * @param p a visitor-specified parameter
   * @return a visitor-specified result
   */
  R visit(Element e, P p);
  /**
   * A convenience method equivalent to {@code v.visit(e, null)}.
   * @param e the element to visit
   * @return a visitor-specified result
   */
  R visit(Element e);
  /**
   * Visits a package element.
   * @param e the element to visit
   * @param p a visitor-specified parameter
   * @return a visitor-specified result
   */
  R visitPackage(PackageElement e, P p);
  /**
   * Visits a type element.
   * @param e the element to visit
   * @param p a visitor-specified parameter
   * @return a visitor-specified result
   */
  R visitType(TypeElement e, P p);
  /**
   * Visits a variable element.
   * @param e the element to visit
   * @param p a visitor-specified parameter
   * @return a visitor-specified result
   */
  R visitVariable(VariableElement e, P p);
  /**
   * Visits an executable element.
   * @param e the element to visit
   * @param p a visitor-specified parameter
   * @return a visitor-specified result
   */
  R visitExecutable(ExecutableElement e, P p);
  /**
   * Visits a type parameter element.
   * @param e the element to visit
   * @param p a visitor-specified parameter
   * @return a visitor-specified result
   */
  R visitTypeParameter(TypeParameterElement e, P p);
  /**
   * Visits an unknown kind of element.
   * This can occur if the language evolves and new kinds
   * of elements are added to the {@code Element} hierarchy.
   *
   * @param e the element to visit
   * @param p a visitor-specified parameter
   * @return a visitor-specified result
   * @throws UnknownElementException
   * a visitor implementation may optionally throw this exception
   */
  R visitUnknown(Element e, P p);
}

在ElementVisitor中定義了多種visit接口,每個接口處理一種元素類型,那么這就是典型的訪問者模式。

七、總結

正如本節(jié)開頭引用GOF的話所說:大多數(shù)情況下,你不需要使用訪問者模式,但是,當你一旦需要使用它時,那你就是真的需要它了。在現(xiàn)實情況下,我們要根據(jù)具體的情況來評估是否適合使用訪問者模式,例如,我們的對象結構是否足夠穩(wěn)定,使用訪問者模式是否能夠優(yōu)化我們的代碼,而不是使我們的代碼變得更復雜。在使用一個模式之前,我們應該明確它的使用場景、它能解決什么問題等,以此來避免濫用設計模式的現(xiàn)象。

優(yōu)點:

各角色職責分離,符合單一職責原則。

具有優(yōu)秀的擴展性。

使得數(shù)據(jù)結構和作用于結構上的操作解耦,使得操作集合可以獨立變化。

靈活性。

缺點:

具體元素對訪問者公布細節(jié),違反了迪米特原則。

具體元素變更時導致修改成本大。

違反了依賴倒置原則,為了達到“區(qū)別對待”而依賴了具體類,沒有依賴抽象。

以上就是Android中怎么實現(xiàn)訪問者模式,小編相信有部分知識點可能是我們?nèi)粘9ぷ鲿姷交蛴玫降?。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI