美文网首页
访问者模式

访问者模式

作者: Whyn | 来源:发表于2018-11-05 20:50 被阅读12次

    简介

    Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.
    封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。

    访问者模式(Visitor Pattern)是一种将数据结构与数据操作分离的设计模式。其基本思想是:对于系统中拥有固定类型数的对象结构(元素),可以通过在其内提供一个 accept 方法用来接受访问者对象的访问。不同的访问者对同一元素的访问内容不同,使得相同的元素集合可以产生不同的数据结果。解耦了数据结构与操作,且数据操作不会改变元素状态。

    访问者模式 解耦数据结构与数据操作原理:元素内部提供一个 accept 方法,该方法可以接收不同的访问者对象,然后在内部将自己(元素)转发到接收到的访问者对象的 visit 方法内。访问者内部对应类型的 visit 方法就会得到回调执行,对元素进行操作。也就是通过两次动态分发(第一次是对访问者的分发accept,第二次是对元素的分发visit),才最终将一个具体的元素传递到一个具体的访问者。

    访问者模式 核心:结构数据结构与数据操作,使得对元素的操作具备优秀的扩展性。可以通过扩展不同的数据操作类型(访问者)实现对相同元素集的不同的操作。

    主要解决

    当系统中存在类型数目稳定(固定)的一类数据结构时,可以通过 访问者模式 方便地实现对该类型所有数据结构的不同操作,而又不会数据产生任何副作用(脏数据)。

    简单理解:想对集合中的不同类型数据(类型数量稳定)进行多种操作,则使用 访问者模式

    优缺点

    优点

    • 解耦了数据结构与数据操作,使得操作集合可以独立变化;
    • 扩展性好:可以通过扩展访问者角色,实现对数据集的不同操作;
    • 元素类型无需一致,访问者均可操作;
    • 各角色职责分离,符合 单一职责原则

    缺点

    • 无法增加元素类型:若系统数据结构对象易于变化,经常有新的数据对象增加进来,则访问者类必须增加对应元素类型的操作,违背了 开闭原则
    • 具体元素变更困难:具体元素增加属性,删除属性等操作会导致对应的访问者类需要进行相应的修改,尤其当有大量访问者类时,修改范围太大;
    • 违背 依赖倒置原则:为了达到”区别对待“,访问者依赖的是具体元素类型,而不是抽象;

    使用场景

    • 数据结构稳定,作用于数据结构的操作经常变化的场景;
    • 需要数据结构与数据操作分离的场景;
    • 需要对不同数据类型(元素)进行操作,而不使用分支判断具体类型的场景;

    模式讲解

    首先来看下 访问者模式 的通用 UML 类图:

    访问者模式

    从 UML 类图中,我们可以看到,访问者模式 主要包含五种角色:

    • 抽象访问者(Visitor):接口或抽象类,该类地冠以了对每一个具体元素(Element)的访问行为visit,其参数就是具体的元素(Element)对象。理论上来说,Visitor 的方法个数与元素(Element)个数是相等的。如果元素(Element)个数经常变动,会导致 Visitor 的方法也要进行变动,此时,该情形并不适用 访问者模式
    • 具体访问者(ConcreteVisitor):实现对具体元素的操作;
    • 抽象元素(Element):接口或抽象类,定义了一个接受访问者访问的方法accept,表示所有元素类型都支持被访问者访问;
    • 具体元素(Concrete Element):具体元素类型,提供接受访问者的具体实现。通常的实现都为:visitor.visit(this)
    • 结构对象(ObjectStruture):该类内部维护了元素集合,并提供方法接受访问者对该集合所有元素进行操作;

    以下是 访问者模式 的通用代码:

    class Client {
        public static void main(String[] args) {
            ObjectStructure collection = new ObjectStructure();
            System.out.println("ConcreteVisitorA handle elements:");
            IVisitor visitorA = new ConcreteVisitorA();
            collection.accept(visitorA);
            System.out.println("------------------------------------");
            System.out.println("ConcreteVisitorB handle elements:");
            IVisitor visitorB = new ConcreteVisitorB();
            collection.accept(visitorB);
    
        }
    
        // 抽象元素
        interface IElement {
            void accept(IVisitor visitor);
        }
    
        // 具体元素
        static class ConcreteElementA implements IElement {
    
            @Override
            public void accept(IVisitor visitor) {
                visitor.visit(this);
            }
    
            public String operationA() {
                return this.getClass().getSimpleName();
            }
        }
    
        // 具体元素
        static class ConcreteElementB implements IElement {
    
            @Override
            public void accept(IVisitor visitor) {
                visitor.visit(this);
            }
    
            public int operationB() {
                return new Random().nextInt(100);
            }
        }
    
        // 抽象访问者
        interface IVisitor {
            void visit(ConcreteElementA element);
    
            void visit(ConcreteElementB element);
        }
    
        // 具体访问者
        static class ConcreteVisitorA implements IVisitor {
            @Override
            public void visit(ConcreteElementA element) {
                String result = element.operationA();
                System.out.println(String.format("result from %s: %s", element.getClass().getSimpleName(), result));
            }
    
            @Override
            public void visit(ConcreteElementB element) {
                int result = element.operationB();
                System.out.println(String.format("result from %s: %s", element.getClass().getSimpleName(), result));
            }
        }
    
        // 具体访问者
        static class ConcreteVisitorB implements IVisitor {
            @Override
            public void visit(ConcreteElementA element) {
                String result = element.operationA();
                System.out.println(String.format("result from %s: %s", element.getClass().getSimpleName(), result));
            }
    
            @Override
            public void visit(ConcreteElementB element) {
                int result = element.operationB();
                System.out.println(String.format("result from %s: %s", element.getClass().getSimpleName(), result));
            }
        }
    
        // 结构对象
        static class ObjectStructure {
            private List<IElement> mList = new ArrayList<>();
    
            {
                this.mList.add(new ConcreteElementA());
                this.mList.add(new ConcreteElementB());
            }
    
            public void accept(IVisitor visitor) {
                for (IElement element : this.mList) {
                    element.accept(visitor);
                }
            }
        }
    }
    

    运行结果如下:

    ConcreteVisitorA handle element:
    result from ConcreteElementA: ConcreteElementA
    result from ConcreteElementB: 58
    ------------------------------------
    ConcreteVisitorB handle element:
    result from ConcreteElementA: ConcreteElementA
    result from ConcreteElementB: 83
    

    举个例子

    例子:假设现 NBA 某球队近期将对各个球员能力进行统计,统计表同时提供给教练和球队老板查看。假设教练只对球员身体素质,场均得分感兴趣,而老板对球员身价,粉丝数量感兴趣。请用代码进行实现。

    分析:为了简单,我们假设支队球员 A 和球员 B 进行能力统计。则题目相当于是统计 A 和 B 的相关 数据 提交给教练和老板 查看。也就是对数据和对数据的操作,非常适合使用 访问者模式 进行实现。球员A 和 B 相当于元素(Element),教练和老板相当于访问者(Visitor),球队经理相当于结构对象,负责统计数据并提交。

    具体代码如下:

    class Client {
        public static void main(String[] args) {
            Manager manager = new Manager();
    
            System.out.println("Coach look up:");
            IViewer coach = new Coach();
            manager.show(coach);
    
            System.out.println("--------------------");
            System.out.println("Boss look up:");
            IViewer boss = new Boss();
            manager.show(boss);
    
        }
    
        static abstract class Player {
            // 姓名
            private String mName;
            // 身体素质
            private String mFitness;
            // 场均得分
            private int mScorePerGame;
            // 粉丝数量
            private int mFans;
            // 身价
            private int mSalary;
    
            public Player(String name, int salary, String fitness, int scorePerGame, int fans) {
                this.mName = name;
                this.mSalary = salary;
                this.mFitness = fitness;
                this.mScorePerGame = scorePerGame;
                this.mFans = fans;
            }
    
            public String getName() {
                return this.mName;
            }
    
            public String getFitness() {
                return this.mFitness;
            }
    
            public int getScorePerGame() {
                return this.mScorePerGame;
            }
    
            public int getFans() {
                return this.mFans;
            }
    
            public int getSalary() {
                return this.mSalary;
            }
    
            abstract void accept(IViewer viewer);
        }
    
        static class PlayerA extends Player {
    
            public PlayerA(String name, int salary, String fitness, int scorePerGame, int fans) {
                super(name, salary, fitness, scorePerGame, fans);
            }
    
            @Override
            public void accept(IViewer viewer) {
                viewer.show(this);
            }
        }
    
        static class PlayerB extends Player {
    
            public PlayerB(String name, int salary, String fitness, int scorePerGame, int fans) {
                super(name, salary, fitness, scorePerGame, fans);
            }
    
            @Override
            public void accept(IViewer viewer) {
                viewer.show(this);
            }
        }
    
        interface IViewer {
            void show(PlayerA player);
    
            void show(PlayerB player);
        }
    
        static class Coach implements IViewer {
    
            @Override
            public void show(PlayerA player) {
                this.getInterested(player);
            }
    
            @Override
            public void show(PlayerB player) {
                this.getInterested(player);
            }
    
            private void getInterested(Player player) {
                String name = player.getName();
                String fitness = player.getFitness();
                int scorePerGame = player.getScorePerGame();
                int fans = player.getFans();
                System.out.println(String.format("%s [body: %s,scorePerGame: %d]", name, fitness, scorePerGame));
            }
        }
    
        static class Boss implements IViewer {
    
            @Override
            public void show(PlayerA player) {
                this.getInterested(player);
            }
    
            @Override
            public void show(PlayerB player) {
                this.getInterested(player);
            }
    
            private void getInterested(Player player) {
                String name = player.getName();
                int salary = player.getSalary();
                int fans = player.getFans();
                System.out.println(String.format("%s [salary: $%d ,fans: %d]", name, salary, fans));
            }
        }
    
        static class Manager {
            private List<? extends Player> mPlayers = Arrays.asList(
                new PlayerA("John", 2000000, "good", 20, 500000),
                 new PlayerA("Bill", 20000000, "excellent", 30, 15000000));
    
            public void show(IViewer viewer) {
                for (Player player : this.mPlayers) {
                    player.accept(viewer);
                }
            }
        }
    }
    

    运行结果如下:

    Coach look up:
    John [body: good,scorePerGame: 20]
    Bill [body: excellent,scorePerGame: 30]
    --------------------
    Boss look up:
    John [salary: $2000000 ,fans: 500000]
    Bill [salary: $20000000 ,fans: 15000000]
    

    相关文章

      网友评论

          本文标题:访问者模式

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