• Java设计模式(三) Visitor(訪问者)模式及多分派场景应用


    基本概念

    Visitor

    • 封装一些作用于数据结构中的各元素的操作,不同的操作能够借助新的visitor实现。减少了操作间的耦合性
    • 訪问者能够将数据结构对数据的操作解耦,使得添加对数据结构的操作不须要取改动数据结构,也不必去改动原有的操作,而运行时再定义新的Visitor时闲着即可了(在操作加入上易拓展)

    模式中角色分工

    • Visitor:抽象訪问者,在重载的visit函数中声明訪问者能够訪问的对象
    • Concrete Visitor:实现一个訪问者对于一个详细的元素的操作
    • Element:抽象元素,声明具有訪问该类型元素权限的訪问者的类型(通常是抽象类型)。提供重载的accept函数赋予权限
    • Concrete Element:实现accept方法,基本上是模板化的visitor.visit(this)
    • Object Structure:容纳多种类型也许不同。接口或者不同的元素的集合。

    例讲Visitor的实现

    先是一个简单的样例,展现一个最主要的简陋的Visitor

    既然在春招季。我们举个简历筛选的样例,投简历的都是写本科生、专科生。还有硕士生、高职啊…为了简单就先取前两者。求职者的简历作为Element实现例如以下:

    abstract class Student {
       //提供对于数据域基本操作的函数
        private String name;
        private String university;
        private String rating;
        //让指定的visitor获得操作该对象的权限
        public abstract void accept(Visitor visitor);
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getUniversity() {
            return university;
        }
    
        public void setUniversity(String university) {
            this.university = university;
        }
    
        public String getRating() {
            return rating;
        }
    
        public void setRating(String rating) {
            this.rating = rating;
        }
    }
    
    class Bachelor extends Student{
    
        @Override
        public void accept( Visitor visitor ) {
            visitor.visit( this );
        }
    }
    
    class College extends Student{
    
        @Override
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }
    }

    由于我们仅仅定义了两种学生,所以接口提供了对于两种Element訪问

    interface Visitor{
        public void visit ( Bachelor bachelor );
        public void visit ( College college );
    }

    首先筛选简历我们看一下大家的简历都什么样子,那么须要一个ShowVisitor:

    class ShowVisitor implements Visitor {
    
        @Override
        public void visit(Bachelor bachelor) {
            System.out.println("A bachelor
    ");
            //TODO 可能会有一些特异的操作,我们为了简单就省略了
            this.printMessage( bachelor );
        }
    
        @Override
        public void visit(College college) {
            System.out.println(" a college student!
    ");
            //TODO 同上
            this.printMessage( college );
        }
    
        public void printMessage ( Student student ){
            System.out.println( "Name : " + student.getName()+"
    "
                    + "University : " + student.getUniversity()+"
    "
                    + "Rating : " + student.getRating() + "
    "
            );
        }
    }

    要进行測试,我们首先要构造一个数据集合。也就是角色中相应的ObjectStructure,为了简单我们直接用ArrayList了

    public class VisitorEg {
        public static void main ( String [] args ){
            ArrayList<Student> list = new ArrayList<Student>();
            Bachelor bachelor = new Bachelor();
            bachelor.setName("llin");
            bachelor.setRating("100");
            bachelor.setUniversity("Tianjin University");
    
            College college = new College();
            college.setUniversity("Tianjin college");
            college.setRating("1");
            college.setName("dalinge");
    
            list.add ( bachelor );
            list.add ( college );
    
            Visitor visitor = new ShowVisitor();
            for ( Student student: list ){
                student.accept( visitor );
            }
    
        }
    }

    那么好像看不出訪问者模式有什么优势啊!!!并且好费事啊,可是由于你将数据结构和对数据的操作分离了(解耦),所以当我想加入新的操作时,不须要改动原有的类,仅仅须要又一次实现一个visitor就能够了。


    所以,我们回到这个样例。这么多人报名,那么究竟有多少本科生呢(假设人数够了,可能直接偷懒仅仅面试本科生了),-_-万恶的这样的HR,所以我们须要一个统计的Visitor:

    class SumVisitor implements Visitor{
    
        private int totalBachelor;
    
        SumVisitor(){
            super();
            totalBachelor = 0;
        }
    
        @Override
        public void visit(Bachelor bachelor) {
            totalBachelor++;
        }
    
        @Override
        public void visit(College college) {
        }
    
        public int getTotal_bachelor() {
            return totalBachelor;
        }
    }
    
    public class VisitorEg {
        public static void main ( String [] args ){
            ArrayList<Student> list = new ArrayList<Student>();
            Bachelor bachelor = new Bachelor();
            bachelor.setName("llin");
            bachelor.setRating("100");
            bachelor.setUniversity("Tianjin University");
    
            College college = new College();
            college.setUniversity("Tianjin college");
            college.setRating("1");
            college.setName("dalinge");
    
            list.add ( bachelor );
            list.add ( college );
    
            Visitor visitor = new ShowVisitor();
            Visitor visitor1 = new SumVisitor();
            for ( Student student: list ){
                student.accept( visitor );
                student.accept( visitor1);
            }
            System.out.println( "The total sum of bachelors : "+ ((SumVisitor)visitor1).getTotal_bachelor() );
        }
    }

    达到了要求。却没有改动一行代码,开心!

    Visitor应用场景

    一定会有的疑问:visitor和iterator的差别:

    • visitor能够訪问不同的对象(仅仅须要在Element定义相应的accept),可是Iterator仅仅能訪问同样的对象。最起码要有同样的接口
    • iterator是不依赖详细实现的,而visitor是依赖详细实现的,由于Visitor会依据訪问的详细的对象来採取相应的操作,而iterator最多仅仅是基于同样的接口的泛化实现。
    • iterator訪问的数据结构的操作和数据并未分离。所以拓展功能起来须要改动,违反了开闭原则单一职责原则

      可是由于訪问者依赖详细实现,而不是依赖抽象。所以违反了依赖倒置原则

    优缺点决定的应用场景

    • 符合单一职责原则。功能上具有良好的拓展性,可是由于依赖详细实现违背了详细实现,所以为类的改动带了麻烦。
    • 具有优良的拓展性。仅仅须要实现新的Visitor来满足新的訪问要求。

      由于数据和操作的分离,防止了加入新的操作污染原来的数据结构。

    综上

    訪问者是一种集中规整模式,特别适合用于大规模重构的项目。在这一个阶段的需求已经非常清晰,原系统的功能点也已经明白。通过訪问者模式能够非常easy把一些功能进行梳理,达到终于目的功能集中化

    双分派

    首先介绍下面单分派

    单分派:一个操作是依据请求者的名称和接收到的參数决定的,在Java有静态绑定动态绑定,各自是通过重载覆写实现的。


    双分派:双分派意味着得到运行的操作决定于请求的种类接收者的类型

    正相应于訪问者模式

    Javac在构建、优化、解析语法树的时候就是採用的是Visitor模式(语法、语义分析阶段)

  • 相关阅读:
    etcd数据单机部署
    PostgreSQL INSERT ON CONFLICT不存在则插入,存在则更新
    ERROR 1709 (HY000): Index column size too large. The maximum column size is 767 bytes.
    Hbase 0.92.1集群数据迁移到新集群
    PostgreSQL创建只读账户
    Kafka技术内幕 读书笔记之(六) 存储层——服务端处理读写请求、分区与副本
    Kafka技术内幕 读书笔记之(六) 存储层——日志的读写
    Kafka技术内幕 读书笔记之(五) 协调者——消费组状态机
    Kafka技术内幕 读书笔记之(五) 协调者——延迟的加入组操作
    Kafka技术内幕 读书笔记之(五) 协调者——协调者处理请求
  • 原文地址:https://www.cnblogs.com/tlnshuju/p/7347785.html
Copyright © 2020-2023  润新知