• 访问者模式实例分享


    访问者模式是一种行为设计模式。访问者模式被用在针对一组相同类型对象的操作。优点是,可以把针对此对象的操作逻辑转移到另外一个类上。

    适合场景:1)对象结构比较稳定,但经常需要在此对象结构上定义新的操作

         2)对一个对象结构中的对象进行很多不同的且不相关的操作,而需要避免这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类

    UML图:

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

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

    ItemElement.java

    1 package com.journaldev.design.visitor;
    2  
    3 public interface ItemElement {
    4  
    5     public int accept(ShoppingCartVisitor visitor);
    6 }

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

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

    Book.java

    01 package com.journaldev.design.visitor;
    02  
    03 public class Book implements ItemElement {
    04  
    05     private int price;
    06     private String isbnNumber;
    07  
    08     public Book(int cost, String isbn){
    09         this.price=cost;
    10         this.isbnNumber=isbn;
    11     }
    12  
    13     public int getPrice() {
    14         return price;
    15     }
    16  
    17     public String getIsbnNumber() {
    18         return isbnNumber;
    19     }
    20  
    21     @Override
    22     public int accept(ShoppingCartVisitor visitor) {
    23         return visitor.visit(this);
    24     }
    25  
    26 }

    Fruit.java

    01 package com.journaldev.design.visitor;
    02  
    03 public class Fruit implements ItemElement {
    04  
    05     private int pricePerKg;
    06     private int weight;
    07     private String name;
    08  
    09     public Fruit(int priceKg, int wt, String nm){
    10         this.pricePerKg=priceKg;
    11         this.weight=wt;
    12         this.name = nm;
    13     }
    14  
    15     public int getPricePerKg() {
    16         return pricePerKg;
    17     }
    18  
    19     public int getWeight() {
    20         return weight;
    21     }
    22  
    23     public String getName(){
    24         return this.name;
    25     }
    26  
    27     @Override
    28     public int accept(ShoppingCartVisitor visitor) {
    29         return visitor.visit(this);
    30     }
    31  
    32 }

    注意,accept()方法的实现是在实体类中,它调用访问者的visit()方法传递当前类对象作为自己的参数。
    此处针对不同类型的商品所使用的visit()方法将会在访问者接口的实体类中被实现。

    ShoppingCartVisitor.java

    1 package com.journaldev.design.visitor;
    2  
    3 public interface ShoppingCartVisitor {
    4  
    5     int visit(Book book);
    6     int visit(Fruit fruit);
    7 }

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

    ShoppingCartVisitorImpl.java

    01 package com.journaldev.design.visitor;
    02  
    03 public class ShoppingCartVisitorImpl implements ShoppingCartVisitor {
    04  
    05     @Override
    06     public int visit(Book book) {
    07         int cost=0;
    08         //apply 5$ discount if book price is greater than 50
    09         if(book.getPrice() > 50){
    10             cost = book.getPrice()-5;
    11         }else cost = book.getPrice();
    12         System.out.println("Book ISBN::"+book.getIsbnNumber() + " cost ="+cost);
    13         return cost;
    14     }
    15  
    16     @Override
    17     public int visit(Fruit fruit) {
    18         int cost = fruit.getPricePerKg()*fruit.getWeight();
    19         System.out.println(fruit.getName() + " cost = "+cost);
    20         return cost;
    21     }
    22  
    23 }

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

    ShoppingCartClient.java

    01 package com.journaldev.design.visitor;
    02  
    03 public class ShoppingCartClient {
    04  
    05     public static void main(String[] args) {
    06         ItemElement[] items = new ItemElement[]{new Book(20"1234"),new Book(100"5678"),
    07                 new Fruit(102"Banana"), new Fruit(55"Apple")};
    08  
    09         int total = calculatePrice(items);
    10         System.out.println("Total Cost = "+total);
    11     }
    12  
    13     private static int calculatePrice(ItemElement[] items) {
    14         ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl();
    15         int sum=0;
    16         for(ItemElement item : items){
    17             sum = sum + item.accept(visitor);
    18         }
    19         return sum;
    20     }
    21  
    22 }

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

    1 Book ISBN::1234 cost =20
    2 Book ISBN::5678 cost =95
    3 Banana cost = 20
    4 Apple cost = 25
    5 Total Cost = 160

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

    访问者模式的类图:

    此模式的优点就是,如果操作的逻辑改变,我们只需要改变访问者的实现就够了,而不用去修改其他所有的商品类。

    另一个好处是,添加新类别的商品到系统变得容易。只需要改变一下访问者接口以及其实现。已经存在的商品类别不会被干扰影响。

    当然,访问者模式的缺点也需要知道,visit()方法的返回值的类型在设计系统式就需要明确。不然,就需要修改访问者的接口以及所有接口实现。另外如果访问者接口的实现太多,系统的扩展性就会下降。

  • 相关阅读:
    让我们一起Go(二)
    通过Html5 Canvas画柱状图
    Node.js爬虫网页请求模块
    Javascript 面向对象编程
    菜单悬浮
    ie6支持的png格式图片的实现方法。
    兼容IE6 的:hover 鼠标经过效果
    VMware Workstation 8/9安装时出现脚本错误
    接上篇,Apache虚拟主机配置
    【Linux笔记】CentOS下找不到eth0设备的解决方法
  • 原文地址:https://www.cnblogs.com/hoobey/p/7774169.html
Copyright © 2020-2023  润新知