34. 访问者模式

作者: Next_吴思成 | 来源:发表于2018-07-23 21:07 被阅读17次

    定义

    访问者模式(Visitor Pattern):提供一个作用于某对象结构中的各元素的操作表示,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

    通俗理解

    当我们去超市买东西的时候,总是会遇到这样的场景,不同的商品有不同的折扣方案,例如吃的打5折,而一些生活用品不打折;生活用品可以用纸袋装,但是如果带水的,那就只能使用塑料袋来装。

    收银的地方,只有一个人,那么他首先需要用不同的袋子去装不同的商品,装完之后,又给不同的商品一定的折扣去计算价格。弄完之后,客户才愉快地拿着要买的东西离开超市。

    可是,这真的是很愉快吗?看来未必,对于收银员来讲,他们只是个收钱的,为什么还需要去给客户装袋?那如果我们还需要添加一个,查看客户是否是会员,享受折上折的待遇,那么收银员还得去兼顾这个工作,收银员内心是崩溃的。对于客户来讲,我为什么要看收银员在装袋,而且还这么慢?客户也是崩溃的。

    怎么办?专业的人做专业的事情,我们要不计成本,请一个装袋员,让装袋员专门装袋,而收银员能够愉快收银,那么就能很好地解决这个问题了。

    访问者模式就是这样的一个过程。我们并不在一个类(收银员)里面,判断对象的集合(超市商品),享受什么折扣、包装、折上折之类的方法,而是把他们独立开来,每一个方法都有一个相关的类去实现。简单来讲,就是把一个类里面的方法拆到类当中去。

    示例

    渣渣程序

    货物类以及具体的实现类

    public class Good {
        private String name;
        private int price;
        private String packing;
        // 构造器、getter、setter、toString方法省略
    }
    public class FoodGood extends Good {
        public FoodGood(String name) {
            super(name);
        }
    }
    //CommonGood 类似
    

    收银员

    public class Cashier {
        public void cash(List<Good> goods) {
            for(Good good : goods) {
                if(good instanceof FoodGood) {
                    good.setPrice(2);
                    good.setPacking("塑料袋装");
                    System.out.println(good);
                } else if(good instanceof CommonGood) {
                    good.setPrice(3);
                    good.setPacking("纸袋装");
                    System.out.println(good);
                } else {
                    System.out.println("===找不到该商品===");
                }
            }
        }
    }
    

    程序入口

    public class Main {
        public static void main(String[] args) {
            List<Good> goods = new ArrayList<>();
            goods.add(new FoodGood("鱼"));
            goods.add(new CommonGood("塑料袋"));
    
            Cashier cashier = new Cashier();
            cashier.cash(goods);
        }
    }
    //Good{name='鱼', price=2, packing='塑料袋装'}
    //Good{name='塑料袋', price=3, packing='纸袋装'}
    

    现在好了,收营员需要定价格,也需要装袋,忙si了,如果还加上一个折上折,那么需要修改cash方法,明显违背开闭原则。

    优化

    类图

    image

    程序

    货物与实现

    public abstract class Good {
        private String name;
        private int price;
        private String packing;
        // 构造器、getter、setter、toString方法省略
        // 访问者类访问的入口
        public abstract void accept(IVistor vistor);
    }
    public class FoodGood extends Good {
        public FoodGood(String name) {
            super(name);
        }
        public void accept(IVistor vistor) {
            vistor.visit(this);
        }
    }
    // CommonGood类似
    

    访问者入口与实现

    public interface IVistor {
        void visit(Good good);
    }
    public class PackingVistor implements IVistor {
        public void visit(Good good) {
            if(good instanceof FoodGood) {
                good.setPacking("塑料袋装");
            } else if(good instanceof CommonGood) {
                good.setPacking("纸袋装");
            } else {
                System.out.println("===找不到该商品===");
            }
        }
    }
    

    收银员

    public class Cashier {
        IVistor packingVistor = new PackingVistor();
        IVistor collectMoneyVistor = new CollectMoneyVistor();
        public void cash(List<Good> goods) {
            for(Good good : goods) {
                good.accept(packingVistor);
                good.accept(collectMoneyVistor);
                System.out.println(good);
            }
        }
    }
    

    程序入口

    public class Main {
        public static void main(String[] args) {
            List<Good> goods = new ArrayList<>();
            goods.add(new FoodGood("鱼"));
            goods.add(new CommonGood("塑料袋"));
    
            Cashier cashier = new Cashier();
            cashier.cash(goods);
        }
    }
    //Good{name='鱼', price=2, packing='塑料袋装'}
    //Good{name='塑料袋', price=3, packing='纸袋装'}
    

    现在收银员有两个帮手,可以帮他装袋与收钱,如果还需要折上折的方法,那么只需要再请帮手就可以,大大减少了收银员的工作量。

    优点

    1. 增加新的访问者简单,符合开闭原则;
    2. 有关对象的访问的行为都规定好,例如这里的访问者都得实现IVistor接口;
    3. 访问者都集中在某个类中,例如这里的访问方式都集中在收银员的类方法上。

    缺点

    1. 增加新的节点困难,例如这里的货物类型如果需要加新的,需要修改IVistor的实现类;
    2. 破坏封装,通过其他方法来修改对象内部的状态。

    应用场景

    访问类比较固定的,就是商品类型基本不变的情况下。

    程序

    e34_visitor_pattern

    吐槽

    packingVistor.visit(good);也是能用,Good当中引用了IVistorIVistor中也引用了Good,造成了不管怎么调用,都是使用。这是双重分派。

    https://www.jianshu.com/p/f21cf48422de

    相关文章

      网友评论

        本文标题:34. 访问者模式

        本文链接:https://www.haomeiwen.com/subject/ioqoyftx.html