详解Java设计模式编程中的访问者模式

网友投稿 171 2023-07-21


详解Java设计模式编程中的访问者模式

定义:封装某些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。

类型:行为类模式

类图:

例子:

例如,思考一下添加不同类型商品的购物车,当点击结算的时候,它计算出所有不同商品需付的费用。现在,计算逻辑即为计算这些不同类型商品的价格。或者说通过访问者模式我们把此逻辑转移到了另外一个类上面。让我们实现这个访问者模式的例子。

为了实现访问者模式,最先需要做的是创建能够被添加到购物车中代表不同类型商品(itecbROhHmElement)的类。

ItemElement.java

package com.journaldev.design.visitor;

public interface ItemElement {

public int accept(ShoppingCarthttp://Visitor visitor);

}

注意,accept方法接受访问者作为参数。当然这儿还有其他的一些方法来指定详细的商品,但为了简化,此处没用过多的考虑细节,只关注访问者模式。

现在创建一些不同商品的实体类。

Book.java

package com.journaldev.design.visitor;

public class Book implements ItemElement {

private int price;

private String isbnNumber;

public Book(int cost, String isbn){

this.price=cost;

this.isbnNumber=isbn;

}

public int getPrice() {

return price;

}

public String getIsbnNumber() {

return isbnNumber;

}

@Override

public int accept(ShoppingCartVisitor visitor) {

return visitor.visit(this);

}

}

Fruit.java

package com.journaldev.design.visitor;

public class Fruit implements ItemElement {

private int pricePerKg;

private int weight;

private String name;

public Fruit(int priceKg, int wt, String nm){

this.pricePerKg=priceKg;

this.weight=wt;

this.name = nm;

}

public int getPricePerKg() {

return pricePerKg;

}

public int getWeight() {

return weight;

}

public String getName(){

return this.name;

}

@Override

public int accept(ShoppingCartVisitor visitor) {

return visitor.visit(this);

}

}

注意,accept()方法的实现是在实体类中,它调用访问者的visit()方法传递当前类对象作为自己的参数。

此处针对不同类型的商品所使用的visit()方法将会在访问者接口的实体类中被实现。

ShoppingCartVisitor.java

package com.journaldev.design.visitor;

public interface ShoppingCartVisitor {

int visit(Book book);

int visit(Fruit fruit);

}

现在将实现访问者接口以及每种商品自己计算自己费用的逻辑。

ShoppingCartVisitorImpl.java

package com.journaldev.design.visitor;

public class ShoppingCartVisitorImpl implements ShoppingCartVisitor {

@Override

public int visit(Book book) {

int cost=0;

//apply 5$ discount if book price is greater than 50

if(book.getPrice() > 50){

cost = book.getPrice()-5;

}else cost = book.getPrice();

System.out.println("Book ISBN::"+book.getIsbnNumber() + " cost ="+cost);

return cost;

}

@Override

public int visit(Fruit fruit) {

int cost = fruit.getPricePerKg()*fruit.getWeight();

System.out.println(fruit.getName() + " cost = "+cost);

return cost;

}

}

现在看一看在程序中如何使用它。

ShoppingCartClient.java

package com.journaldev.design.visitor;

public class ShoppingCartClient {

public static void main(String[] args) {

ItemElement[] items = new ItemElement[]{new Book(20, "1234"),new Book(100, "5678"),

new Fruit(10, 2, "Banana"), new Fruit(5, 5, "Apple")};

int total = calculatePrice(items);

System.out.println("Total Cost = "+total);

}

private static int calculatePrice(ItemElement[] items) {

ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl();

int sum=0;

for(ItemElement item : items){

sum = sum + item.accept(visitor);

}

return sum;

}

}

当运行上述程序是,我们得到如下输出。

Book ISBN::1234 cost =20

Book ISBN::5678 cost =95

Banana cost = 20

Apple cost = 25

Total Cost = 160

请注意,此处的实现,好像accept()方法对于所有商品是相同的,但是他也可以不同。例如,如果商品为空它能进行逻辑检查并不再调用visit()方法。

访问者模式的优点:

符合单一职责原则:凡是适用访问者模式的场景中,元素类中需要封装在访问者中的操作必定是与元素类本身关系不大且是易变的操作,使用访问者模式一方面符合单一职责原则,另一方面,因为被封装的操作通常来说都是易变的,所以当发生变化时,就可以在不改变元素类本身的前提下,实现对变化部分的扩展。

扩展性良好:元素类可以通过接受不同的访问者来实现对不同操作的扩展。

 访问者模式的适用场景:

       假如一个对象中存在着一些与本对象不相干(或者关系较弱)的操作,为了避免这些操作污染这个对象,则可以使用访问者模式来把这些操作封装到访问者中去。

       假如一组对象中,存在着相似的操作,为了避免出现大量重复的代码,也可以将这些重复的操作封装到访问者中去。

       但是,访问者模式并不是那么完美,它也有着致命的缺陷:增加新的元素类比较困难。通过访问者模式的代码可以看到,在访问者类中,每一个元素类都有它对应的处理方法,也就是说,每增加一个元素类都需要修改访问者类(也包括访问者类的子类或者实现类),修改起来相当麻烦。也就是说,在元素类数目不确定的情况下,应该慎用访问者模式。所以,访问者模式比较适用于对已有功能的重构,比如说,一个项目的基本功能已经确定下来,元素类的数据已经基本确定下来不会变了,会变的只是这些元素内的相关操作,这时候,我们可以使用访问者模式对原有的代码进行重构一遍,这样一来,就可以在不修改各个元素类的情况下,对原有功能进行修改。

 

总结:


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:谷歌showModalDialog()方法不兼容出现对话窗口的解决办法
下一篇:详解Java中native关键字
相关文章

 发表评论

暂时没有评论,来抢沙发吧~