访问者模式

2018-11-05  本文已影响12人  Whyn

简介

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 类图中,我们可以看到,访问者模式 主要包含五种角色:

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

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]
上一篇 下一篇

猜你喜欢

热点阅读