设计模式----访问者模式

一,简介

        访问者模式在实际的开发中使用非常少,因为它比较难以实现,该模式可能导致代码的可读性变差,可维护性变差,在没有特别必要的情况下,不建议使用。 

        访问者模式是行为型设计模式的一种,它的原始定义是:允许在运行时将一个或多个操作应用于一组对象,将操作与对象结构分离。

        访问者模式主要解决的是数据与算法的藕合问题,尤其是在数据结构比较稳定,而算法多变的情况下,为了不污染数据本身,访问者会将多算法进行独立归档,并在访问数据时根据数据类型自动切换到对应的算法,实现数据的自动响应机制,并确保算法的自由扩展。 

二、结构

设计模式----访问者模式_第1张图片

访问者模式由以下几个角色组成

①抽象访问者:定义了一系列的操作方法,用来处理数据元素,通常都是同名方法,重载形式出现,以具体传入的数据元素类型进行区分,选择对应的重载方法调用。

②抽象元素接口:被访问的数据元素接口,定义了一个接收访问者的方法。这么做的目的是要每一个元素都可以被访问者访问。

③具体的元素角色:对抽象元素接口进行实现,通常情况下是使用访问者提供的的访问该元素的方法。

④具体的访问者:实现抽象访问者,重写对应的方法。

三、实现

场景:我们以超市购物为例,假如超市中有三类商品:水果,糖果,酒水进行售卖,我们可以忽略每种商品的计价方法,因为最终结账时由收银员统一集中处理,在商品类中增加计价的方法是不合理的设计。

1)产品抽象类

/**
 * 抽象产品类
 */
public abstract class Product {
    private String name;  //产品名称
    private LocalDate previousDate;  //生产日期

    private double price;  //单价

    private int count;   //数量

    public Product(String name, LocalDate previousDate, double price, int count) {
        this.name = name;
        this.previousDate = previousDate;
        this.price = price;
        this.count = count;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public LocalDate getPreviousDate() {
        return previousDate;
    }

    public void setPreviousDate(LocalDate previousDate) {
        this.previousDate = previousDate;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }
}

2)具体的产品

/**
 * 糖果类
 */
public class Candy extends Product{

    public Candy(String name, LocalDate previousDate, double price, int count) {
        super(name, previousDate, price, count);
    }

}

/**
 * 水果类
 */
public class Fruit extends Product {
    private double width; //重量

    public double getWidth() {
        return width;
    }

    public void setWidth(double width) {
        this.width = width;
    }

    public Fruit(String name, LocalDate previousDate, double price, int count, double width) {
        super(name, previousDate, price, count);
        this.width = width;
    }

  
}



/**
 * 酒水类
 */
public class Wine extends Product{
    public Wine(String name, LocalDate previousDate, double price, int count) {
        super(name, previousDate, price, count);
    }

   
}

3)抽象访问者类:提供多个重载的方法

/**
 * 抽象访问者
 */
public interface  Viector {
    //多个重载方法
    void viecte(Candy candy);
    void viecte(Wine wine);
    void viecte(Fruit fruit);

}

4)具体的访问者类

public class DiscountViector implements Viector {



    @Override
    public void viecte(Candy candy) {
        System.out.println("糖果打八折:" + candy.getName()+"价格:"+candy.getCount()*candy.getPrice()*0.8);
    }


    @Override
    public void viecte(Wine wine) {
        System.out.println("酒水打6折:" + wine.getName()+"价格:"+wine.getCount()*wine.getPrice()*0.6);
    }

    @Override
    public void viecte(Fruit fruit) {
        System.out.println("水果原价出售:" + fruit.getName()+"价格:"+fruit.getCount()*fruit.getPrice()*fruit.getWidth());
    }
}

通过上述代码测试发现,我们并不能动态的执行访问者中的方法,因此我们引入抽象元素接口,改造产品类,由产品类去实现它。

5)抽象元素接口

/**
 *抽象元素接口
 */
public interface Adeapter {
    void accept(Viector viector);
}

改造的产品

/**
 * 糖果类
 */
public class Candy extends Product implements  Adeapter{

    public Candy(String name, LocalDate previousDate, double price, int count) {
        super(name, previousDate, price, count);
    }

    @Override
    public void accept(Viector viector) {
        viector.viecte(this);
    }
}


...............

6) 客户端

public class Client {
    public static void main(String[] args) {
        //创建折扣访问者
        DiscountViector discountViector = new DiscountViector();
        //创建产品
        List products = Arrays.asList(
                new Candy("巧克力", LocalDate.MIN, 12, 3),
                new Wine("九粮液", LocalDate.MIN, 200, 2),
                new Fruit("橘子", LocalDate.MIN, 12, 4, 1.5));
        //我们直接通过产品类的集合并不能一次计算出所有数据,因此我们引入抽象元素接口,提供一个方法,使产品类实现它,并返回自己
        for (Adeapter product : products) {
            product.accept(discountViector);
        }

        /**
         * 糖果打八折:巧克力价格:28.8
         * 酒水打6折:九粮液价格:240.0
         * 水果原价出售:橘子价格:72.0
         */

    }
}

四、总结

优点

①扩展性好:在不修改对象结构中元素的情况下,为对象结构中的元素添加新的功能。

②复用性好:通过访问者来定义整个对象结构的通用功能,从而提高复用程度

③分离无关行为:通过访问者来分离无关行为,把相关的行为封装在一起,构成一个访问者,这样每一个访问者的功能都比较单一。

缺点

①对象结构变化很困难:在访问者模式中,每增加一个新的元素,都要在每一个具体的访问者类中增加具体的操作,违背了开闭原则。

②违反了依赖倒转原则:访问者模式具体类,而没有依赖抽象类。

使用场景

①当对象的数据结构相对稳定,而操作经常变化的时候。

②需要将数据结构与不常用的操作进行分离的时候。

③需要再运行时动态决定使用那些对象和方法的时候。

你可能感兴趣的:(设计模式,设计模式,访问者模式)