国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Android編程設(shè)計(jì)模式之訪問者模式詳解

2019-10-22 18:18:23
字體:
供稿:網(wǎng)友

本文實(shí)例講述了Android編程設(shè)計(jì)模式之訪問者模式。分享給大家供大家參考,具體如下:

一、介紹

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

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

二、定義

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

三、使用場景

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

需要對一個(gè)對象結(jié)構(gòu)中的對象進(jìn)行很多不同的并且不相關(guān)的操作,而需要避免這些操作”污染“這些對象的類,也不希望在增加新操作時(shí)修改這些類。

四、訪問者模式的UML類圖

UML類圖:

Android,設(shè)計(jì)模式,訪問者模式

角色介紹:

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

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

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

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

ObjectStructure:定義對象結(jié)構(gòu),里面維護(hù)了一個(gè)元素的集合,并且迭代這些元素供訪問者訪問。

五、簡單示例

情景:年終了,公司會給員工進(jìn)行業(yè)績考核。但是,不同領(lǐng)域的管理人員對于員工的評定標(biāo)準(zhǔn)不一樣?,F(xiàn)在員工有工程師和經(jīng)理,評定者有CEO和CTO,我們假定CTO只關(guān)注工程師的代碼量、經(jīng)理的新產(chǎn)品數(shù)量,而CEO關(guān)注的是工程師的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());  }}

員工報(bào)表:

//員工業(yè)務(wù)報(bào)表類(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"));  }  /**   * 為訪問者展示報(bào)表    * @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) {    //構(gòu)建報(bào)表    BusinessReport report = new BusinessReport();    System.out.println("===== 給CEO看報(bào)表 =====");    //設(shè)置訪問者CEO    report.showReport(new CEOVisitor());    System.out.println("===== 給CTO看報(bào)表 =====");    //設(shè)置訪問者CTO    report.showReport(new CTOVisitor());  }}

結(jié)果:

===== 給CEO看報(bào)表 =====經(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看報(bào)表 =====經(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

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

六、Android中的訪問者模式

安卓中的著名開源庫ButterKnife、Dagger、Retrofit都是基于APT(Annotation Processing Tools)實(shí)現(xiàn)。而編譯注解核心依賴APT。當(dāng)我們通過APT處理注解時(shí),最終會將獲取到的元素轉(zhuǎn)換為相應(yīng)的Element元素,以便獲取到它們對應(yīng)信息。那么元素基類的源碼如下:(路徑: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接口,每個(gè)接口處理一種元素類型,那么這就是典型的訪問者模式。

七、總結(jié)

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

優(yōu)點(diǎn):

各角色職責(zé)分離,符合單一職責(zé)原則。

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

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

靈活性。

缺點(diǎn):

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

具體元素變更時(shí)導(dǎo)致修改成本大。

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

希望本文所述對大家Android程序設(shè)計(jì)有所幫助。


注:相關(guān)教程知識閱讀請移步到Android開發(fā)頻道。
發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 治多县| 砀山县| 枣强县| 九龙县| 九龙县| 贵定县| 云浮市| 武山县| 乡宁县| 聊城市| 福清市| 靖宇县| 邵武市| 桓仁| 江口县| 芷江| 会昌县| 吴桥县| 绵阳市| 清水河县| 高淳县| 凌源市| 抚顺县| 监利县| 牟定县| 阿鲁科尔沁旗| 通许县| 卢氏县| 汉中市| 玉田县| 尚志市| 宝应县| 仪陇县| 上饶县| 彩票| 揭西县| 澳门| 托里县| 万山特区| 乌兰浩特市| 铅山县|