“设计模式系列-观察者模式,访问者模式”


观察者模式

s

Subject被观察者:能够动态地增加、取消观察者,职责是管理观察者并通知观察者。
Observer观察者: 接收到消息后,进行相应的操作。
ConcreteSubject:具体的被观察者
ConcreteObserver:具体的观察者

    //抽象被观察者
    public abstract class Subject {
         //定义一个观察者数组
         private Vector obsVector = new Vector();
         //增加一个观察者
         public void addObserver(Observer o){
                 this.obsVector.add(o);
         }
         //删除一个观察者
         public void delObserver(Observer o){
                 this.obsVector.remove(o);
         }
         //通知所有观察者
         public void notifyObservers(){
                 for(Observer o:this.obsVector){
                         o.update();
                 }
         }
    }

    //具体被观察者
    public class ConcreteSubject extends Subject {
         //具体的业务
         public void doSomething(){
                 //something
                 super.notifyObservers();
         }
    }

    //观察者接口
    public interface Observer {
         //更新方法
         public void update();
    }

    //具体观察者
    public class ConcreteObserver implements Observer {
         //实现更新方法
         public void update() {
                 System.out.println("接收到信息,并进行处理!");
         }
    }

    public class Client {
         public static void main(String[] args) {
                 //创建一个被观察者
                 ConcreteSubject subject = new ConcreteSubject();
                 //定义一个观察者
                 Observer obs= new ConcreteObserver();
                 //观察者观察被观察者
                 subject.addObserver(obs);
                 //观察者开始活动了
                 subject.doSomething();
         }
    }

观察者模式的使用场景:
建立一套触发机制。

观察者模式的缺点在:
观察者模式需要考虑一下开发效率和运行效率问题

访问者模式

数据操作与数据结构分离的设计模式。是23中设计模式中比较复杂的一个,使用频率不高但是一旦需要使用,就是迫切的需要。

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

s

角色:
Visitor——抽象访问者:程序中就是visit方法的参数定义哪些对象是可以被访问的
ConcreteVisitor——具体访问者:它影响访问者访问到一个类后该怎么干,要做什么事情(CEOVisitor,CTOVisitor)
Element——抽象元素:声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的(Staff)
ConcreteElement——具体元素:实现accept方法,通常是visitor.visit(this),基本上都形成了一种模式了(Engineer,Manager)
ObjectStruture——结构对象(可选):元素产生者,一般容纳在多个不同类、不同接口的容器,如List、Set、Map等,在项目中,一般很少抽象出这个角色。(Report)

    //员工基类
    public abstract class Staff {
        public String name;
        public int kpi;
        public Staff(String name){
            this.name = name;
            kpi = new Random().nextInt(10);
        }

        public abstract void accept(Visitor visitor);
    }

    //产品经理
    public class Manager extends Staff{
        public Manager(String name) {
            super(name);
        }

        //一年内的产品数量
        public int getProducts(){
            return 100;
        }

        @Override
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }
    }

    //工程师
    public class Engineer extends Staff{
        public Engineer(String name) {
            super(name);
        }

        //一年的代码行数
        public int getCodeLine(){
            return 10000;
        }

        @Override
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }
    }

    //访问者接口
    public interface Visitor {
        void visit(Engineer engineer);
        void visit(Manager manager);
    }

    public class Report {
        List<Staff> list = new ArrayList<>();
        public Report(){
            list.add(new Manager("zhang3"));
            list.add(new Engineer("wang5"));
            list.add(new Engineer("li4"));
            list.add(new Engineer("liuermazi"));
        }

        public void showReport(Visitor visitor){
            for (Staff staff : list){
                staff.accept(visitor);
            }
        }
    }

    public class CEOVisitor implements Visitor{
        @Override
        public void visit(Engineer engineer) {
            System.out.print("kpi: " +engineer.kpi);
        }

        @Override
        public void visit(Manager manager) {
            System.out.print("kpi: " + manager.kpi);
        }
    }

    public class CTOVisitor implements Visitor {
        @Override
        public void visit(Engineer engineer) {
            System.out.print("code: " + engineer.getCodeLine());
        }

        @Override
        public void visit(Manager manager) {
            System.out.print("product: "+ manager.getProducts());
        }
    }

    public class Client {
        public static void main(String[] args) {
            Report report = new Report();
            //CEO访问者,给CEO看的报表
            report.showReport(new CEOVisitor());
            //CTO访问者,给CTO看的报表
            report.showReport(new CTOVisitor());
        }
    }

思考:
抽象Staff中使用了Visitor,Visitor依赖于Engineer和Manager(具体的Staff),这样间接导致依赖于具体的Staff,这样设计好不好?
如果这样写呢?

    public abstract class Staff {
        public String name;
        public int kpi;
        public Staff(String name){
            this.name = name;
            kpi = new Random().nextInt(10);
        }

        public void accept(Visitor visitor){
            visitor.visit(this)
        }
    }

    //访问者接口
    public interface Visitor {
        void visit(Staff staff);
    }

    public class CEOVisitor implements Visitor{
        @Override
        public void visit(Staff staff) {
            if(staff instanceof Manager){
                 Manager manager = (Manager)staff;
                 System.out.print("产品经理kpi: " +manager.kpi);
            }else if(staff instanceof Engineer){
                 Engineer engineer = (Engineer)staff;
                 System.out.print("工程师kpi: " +engineer.kpi);
            }
        }
    }

这样写就不依赖于具体的元素了,也减少了每次调用visit方法。但是增加的是if-else的嵌套以及类型的强制转换,当类型较多时也会变得比较难以维护。各有利弊吧。



本作品由 Tony Zhang 创作,采用 CC BY-NC-SA 3.0 许可协议 进行许可。

Comments