访客设计模式示例
本文是我們名為“ Java設(shè)計(jì)模式 ”的學(xué)院課程的一部分。
在本課程中,您將深入研究大量的設(shè)計(jì)模式,并了解如何在Java中實(shí)現(xiàn)和利用它們。 您將了解模式如此重要的原因,并了解何時(shí)以及如何應(yīng)用模式中的每一個(gè)。 在這里查看 !
目錄
1.簡介 2.什么是訪客設(shè)計(jì)模式 3.實(shí)施訪客設(shè)計(jì)模式 4.何時(shí)使用訪客設(shè)計(jì)模式 5. JDK中的訪問者設(shè)計(jì)模式 6.下載源代碼1引言
要了解訪客設(shè)計(jì)模式,讓我們重新訪問“ 復(fù)合設(shè)計(jì)模式” 。 組合模式使您可以將對象組合成樹形結(jié)構(gòu),以表示部分整體層次結(jié)構(gòu)。
在Composite Pattern示例中,我們創(chuàng)建了一個(gè)由不同類型的對象組成的html結(jié)構(gòu)。 現(xiàn)在假設(shè)我們需要在html標(biāo)簽中添加一個(gè)css類。 一種方法是在使用setStartTag方法添加開始標(biāo)記時(shí)添加類。 但是,這種硬編碼設(shè)置會(huì)給我們的代碼造成僵化。
另一種方法是在父抽象HtmlTag類中添加新方法,例如addClass 。 所有子類都將重寫此方法,并將提供css類。 這種方法的一個(gè)主要缺點(diǎn)是,如果有很多子類(將在大型html頁面中顯示),則在所有子類中實(shí)現(xiàn)此方法將變得非常昂貴且忙碌。 并假設(shè),稍后我們需要在標(biāo)簽中添加另一個(gè)樣式元素,我們再次需要執(zhí)行相同的操作。
訪客設(shè)計(jì)模式為您提供了一種在不更改元素類的情況下,在對象上添加新操作的方法,尤其是當(dāng)操作經(jīng)常更改時(shí)。
2.什么是訪客設(shè)計(jì)模式
訪客設(shè)計(jì)模式的目的是表示要對對象結(jié)構(gòu)的元素執(zhí)行的操作。 訪問者可讓您定義新操作,而無需更改其所操作元素的類。
當(dāng)跨類層次結(jié)構(gòu)的對象的異構(gòu)集合設(shè)計(jì)操作時(shí),Visitor模式很有用。 訪客模式允許在不更改集合中任何對象的類的情況下定義操作。 為此,Visitor模式建議在一個(gè)單獨(dú)的類中定義該操作,該類稱為visiter類。 這將操作與其所操作的對象集合分開。 對于每個(gè)要定義的新操作,都會(huì)創(chuàng)建一個(gè)新的訪問者類。 由于該操作將在一組對象上執(zhí)行,因此訪問者需要一種訪問這些對象的公共成員的方法。 可以通過實(shí)施以下兩個(gè)設(shè)計(jì)思想來滿足此要求。
圖1 –訪客設(shè)計(jì)模式類圖
游客
- 為對象結(jié)構(gòu)中每類ConcreteElement聲明一個(gè)Visit操作。 操作的名稱和簽名標(biāo)識(shí)了將Visit請求發(fā)送給visitor 。 這使訪問者可以確定要訪問的元素的具體類別。 然后,訪問者可以直接通過其特定界面訪問該元素。
具體訪客
- 實(shí)現(xiàn)Visitor聲明的每個(gè)操作。 每個(gè)操作都會(huì)實(shí)現(xiàn)為結(jié)構(gòu)中相應(yīng)對象類別定義的算法片段。 ConcreteVisitor提供算法的上下文并存儲(chǔ)其本地狀態(tài)。 這種狀態(tài)通常會(huì)在遍歷結(jié)構(gòu)期間累積結(jié)果。
元件
- 定義一個(gè)Accept操作,將visitor作為參數(shù)。
ConcreteElement
- 實(shí)現(xiàn)將visitor作為參數(shù)的Accept操作。
對象結(jié)構(gòu)
- 可以枚舉其元素。
- 可以提供一個(gè)高級界面,以允許visitor訪問其元素。
- 可以是組合或集合,例如列表或集合。
3.實(shí)施訪客設(shè)計(jì)模式
為了實(shí)現(xiàn)Visitor設(shè)計(jì)模式,我們將使用相同的Composite Pattern代碼 ,并將為其引入一些新的接口,類和方法。
實(shí)現(xiàn)Visitor模式需要兩個(gè)重要的接口,一個(gè)Element接口,它將包含一個(gè)參數(shù)為Visitor類型的accept方法。 所有需要允許訪問者訪問的類都將實(shí)現(xiàn)此接口。 在我們的例子中, HtmlTag將實(shí)現(xiàn)Element接口,因?yàn)镠tmlTag是所有具體html類的父抽象類,因此具體類將繼承并覆蓋Element接口的accept方法。
另一個(gè)重要的界面是Visitor界面; 此接口將包含帶有實(shí)現(xiàn)Element接口的類的參數(shù)的visit方法。 還請注意,與Composite Design Pattern課程中顯示的示例相反,我們在HtmlTag類中添加了兩個(gè)新方法,即getStartTag和getEndTag 。
package com.javacodegeeks.patterns.visitorpattern;public interface Element {public void accept(Visitor visitor); }package com.javacodegeeks.patterns.visitorpattern;public interface Visitor {public void visit(HtmlElement element);public void visit(HtmlParentElement parentElement); }下面的代碼來自Composite Pattern示例,進(jìn)行了一些更改。
package com.javacodegeeks.patterns.visitorpattern;import java.util.List;public abstract class HtmlTag implements Element{public abstract String getTagName();public abstract void setStartTag(String tag);public abstract String getStartTag();public abstract String getEndTag();public abstract void setEndTag(String tag);public void setTagBody(String tagBody){throw new UnsupportedOperationException("Current operation is not support for this object");}public void addChildTag(HtmlTag htmlTag){throw new UnsupportedOperationException("Current operation is not support for this object");}public void removeChildTag(HtmlTag htmlTag){throw new UnsupportedOperationException("Current operation is not support for this object");}public List<HtmlTag>getChildren(){throw new UnsupportedOperationException("Current operation is not support for this object");}public abstract void generateHtml();}抽象的HtmlTag類實(shí)現(xiàn)Element接口。 下面的具體類將覆蓋Element接口的accept方法,并調(diào)用visit方法,并將this運(yùn)算符作為參數(shù)傳遞。 這將允許visitor方法獲取該對象的所有公共成員,并對其添加新的操作。
package com.javacodegeeks.patterns.visitorpattern;import java.util.ArrayList; import java.util.List;public class HtmlParentElement extends HtmlTag {private String tagName;private String startTag; private String endTag;private List<HtmlTag>childrenTag;public HtmlParentElement(String tagName){this.tagName = tagName;this.startTag = "";this.endTag = "";this.childrenTag = new ArrayList<>();}@Overridepublic String getTagName() {return tagName;}@Overridepublic void setStartTag(String tag) {this.startTag = tag;}@Overridepublic void setEndTag(String tag) {this.endTag = tag;}@Overridepublic String getStartTag() {return startTag;}@Overridepublic String getEndTag() {return endTag;}@Overridepublic void addChildTag(HtmlTag htmlTag){childrenTag.add(htmlTag);}@Overridepublic void removeChildTag(HtmlTag htmlTag){childrenTag.remove(htmlTag);}@Overridepublic List<HtmlTag>getChildren(){return childrenTag;}@Overridepublic void generateHtml() {System.out.println(startTag);for(HtmlTag tag : childrenTag){tag.generateHtml();}System.out.println(endTag);}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}}package com.javacodegeeks.patterns.visitorpattern;public class HtmlElement extends HtmlTag{private String tagName;private String startTag; private String endTag;private String tagBody;public HtmlElement(String tagName){this.tagName = tagName;this.tagBody = "";this.startTag = "";this.endTag = "";}@Overridepublic String getTagName() {return tagName;}@Overridepublic void setStartTag(String tag) {this.startTag = tag;}@Overridepublic void setEndTag(String tag) {this.endTag = tag;}@Overridepublic String getStartTag() {return startTag;}@Overridepublic String getEndTag() {return endTag;}@Overridepublic void setTagBody(String tagBody){this.tagBody = tagBody;}@Overridepublic void generateHtml() {System.out.println(startTag+""+tagBody+""+endTag);}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}}現(xiàn)在,具體的訪問者類:我們創(chuàng)建了兩個(gè)具體的類,一個(gè)將為所有html標(biāo)記添加一個(gè)css類visitor ,另一個(gè)將使用html標(biāo)記的style屬性更改標(biāo)記的寬度。
package com.javacodegeeks.patterns.visitorpattern;public class CssClassVisitor implements Visitor{@Overridepublic void visit(HtmlElement element) {element.setStartTag(element.getStartTag().replace(">", " class='visitor'>"));}@Overridepublic void visit(HtmlParentElement parentElement) {parentElement.setStartTag(parentElement.getStartTag().replace(">", " class='visitor'>"));}}package com.javacodegeeks.patterns.visitorpattern;public class StyleVisitor implements Visitor {@Overridepublic void visit(HtmlElement element) {element.setStartTag(element.getStartTag().replace(">", " style='width:46px;'>"));}@Overridepublic void visit(HtmlParentElement parentElement) {parentElement.setStartTag(parentElement.getStartTag().replace(">", " style='width:58px;'>"));}}現(xiàn)在,讓我們測試上面的示例。
package com.javacodegeeks.patterns.visitorpattern;public class TestVisitorPattern {public static void main(String[] args) {System.out.println("Before visitor......... \\n");HtmlTag parentTag = new HtmlParentElement("<html>");parentTag.setStartTag("<html>");parentTag.setEndTag("</html>");HtmlTag p1 = new HtmlParentElement("<body>");p1.setStartTag("<body>");p1.setEndTag("</body>");parentTag.addChildTag(p1);HtmlTag child1 = new HtmlElement("<p>");child1.setStartTag("<p>");child1.setEndTag("</p>");child1.setTagBody("Testing html tag library");p1.addChildTag(child1);child1 = new HtmlElement("<p>");child1.setStartTag("<p>");child1.setEndTag("</p>");child1.setTagBody("Paragraph 2");p1.addChildTag(child1);parentTag.generateHtml();System.out.println("\\nAfter visitor....... \\n");Visitor cssClass = new CssClassVisitor();Visitor style = new StyleVisitor();parentTag = new HtmlParentElement("<html>");parentTag.setStartTag("<html>");parentTag.setEndTag("</html>");parentTag.accept(style);parentTag.accept(cssClass);p1 = new HtmlParentElement("<body>");p1.setStartTag("<body>");p1.setEndTag("</body>");p1.accept(style);p1.accept(cssClass);parentTag.addChildTag(p1);child1 = new HtmlElement("<p>");child1.setStartTag("<p>");child1.setEndTag("</p>");child1.setTagBody("Testing html tag library");child1.accept(style);child1.accept(cssClass);p1.addChildTag(child1);child1 = new HtmlElement("<p>");child1.setStartTag("<p>");child1.setEndTag("</p>");child1.setTagBody("Paragraph 2");child1.accept(style);child1.accept(cssClass);p1.addChildTag(child1);parentTag.generateHtml();}}上面的代碼將導(dǎo)致以下輸出:
Before visitor......... <html> <body> <p>Testing html tag library</p> <p>Paragraph 2</p> </body> </html>After visitor....... <html style='width:58px;' class='visitor'> <body style='width:58px;' class='visitor'> <p style='width:46px;' class='visitor'>Testing html tag library</p> <p style='width:46px;' class='visitor'>Paragraph 2</p> </body> </html>“訪問者之前……”之后的輸出與“復(fù)合模式”課程中的輸出相同。 后來,我們創(chuàng)建了兩個(gè)具體的訪問者,然后使用accept方法將它們添加到具體的html對象中。 輸出“ After visitor…”顯示給您結(jié)果,其中將css類和樣式元素添加到html標(biāo)記中。
請注意,Visitor Pattern的優(yōu)點(diǎn)是我們可以在不更改其類的情況下向?qū)ο筇砑有虏僮鳌?例如,我們可以添加一些JavaScript函數(shù),例如onclick或一些angularjs ng標(biāo)簽,而無需修改類。
4.何時(shí)使用訪客設(shè)計(jì)模式
在以下情況下使用“訪問者”模式:
- 對象結(jié)構(gòu)包含許多具有不同接口的對象類,并且您希望根據(jù)這些對象的具體類對這些對象執(zhí)行操作。
- 需要對對象結(jié)構(gòu)中的對象執(zhí)行許多不同且不相關(guān)的操作,并且您要避免使用這些操作“污染”它們的類。 訪客可以通過在一個(gè)類中定義相關(guān)操作來將它們保持在一起。 當(dāng)許多應(yīng)用程序共享對象結(jié)構(gòu)時(shí),請使用Visitor將操作僅放在需要它們的那些應(yīng)用程序中。
- 定義對象結(jié)構(gòu)的類很少更改,但是您經(jīng)常想在該結(jié)構(gòu)上定義新的操作。 更改對象結(jié)構(gòu)類需要重新定義所有訪問者的接口,這可能會(huì)導(dǎo)致成本高昂。 如果對象結(jié)構(gòu)類經(jīng)常更改,則最好在這些類中定義操作。
5. JDK中的訪問者設(shè)計(jì)模式
- javax.lang.model.element.Element和javax.lang.model.element.ElementVisitor
- javax.lang.model.type.TypeMirror和javax.lang.model.type.TypeVisitor
6.下載源代碼
這是“訪客設(shè)計(jì)模式”的一課。 您可以在此處下載相關(guān)的源代碼: VisitorPattern-Project
翻譯自: https://www.javacodegeeks.com/2015/09/visitor-design-pattern.html
總結(jié)
- 上一篇: JavaFX 2.0和Scala,例如牛
- 下一篇: 适配器设计模式示例