【设计模式】访问者模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )

2023-03-29 15:27:42 浏览数 (1)

文章目录

  • 一、访问者模式简介
  • 二、访问者模式 适用场景
  • 三、访问者模式 优缺点
  • 四、访问者模式 与 迭代器模式
  • 五、代码示例
    • 1、Game 父类 ( 被访问者 )
    • 2、VipGame 收费游戏 ( 被访问者 )
    • 3、FreeGame 免费游戏 ( 被访问者 )
    • 4、IVisitor 访问者接口
    • 5、VipVisitor 付费玩家
    • 6、测试类

一、访问者模式简介


访问者模式 : 封装 作用于 某种 数据结构 的 各元素 操作 , 数据结构指的是 List , Set , Map 等 ;

在 不改变 元素类 的前提下 , 定义 作用于 元素 的操作 ;

访问者模式类型 : 行为型 ;

二、访问者模式 适用场景


访问者模式 适用场景 :

  • List , Set , Map 等 数据结构 中 , 包含 很多类型的对象 ;
  • 数据结构 与 数据操作 分离 ;

三、访问者模式 优缺点


访问者模式 优点 : 访问者模式 增加新的操作 很容易 , 只需要增加一个新的 访问者 即可 ; 将相关的行为 , 封装到一个 访问者 中 ;

访问者模式 缺点 :

  • 增加 新 数据结构 比较困难 ;
  • 元素变更 比较困难 ; 如 为 被访问 的对象 增加 / 减少 一些属性 , 相应的 访问者 也需要进行修改 ;

四、访问者模式 与 迭代器模式


访问者模式 与 迭代器模式 都是在 某种 数据结构 ( List / Set / Map ) 上进行处理 ;

访问者模式 主要用于 对 保存在 数据结构 中元素 , 进行某种特定处理 , 重点是 处理 ;

迭代器模式 主要作用就是 遍历 数据结构 中的元素 , 重点是 遍历 ;

五、代码示例


游戏 分为 免费游戏 和 收费游戏 , 玩家 分为 免费玩家 和 付费玩家 ;

不同的玩家 , 访问不同的游戏 , 各自有不同的效果 ;

被访问者 , 都继承自同一个父类 , 可以放在一个集合中 ;

访问者 , 都实现同一个接口 , 针对每个类型的被访问者 , 都有一个对应的重载方法 ;

如 : 玩家访问者 , 对免费游戏有一个方法 , 对收费游戏也有一个方法 ;

该设计模式不常用 , 如果遇到 数据加载 与 数据操作 分离的情况 , 可以考虑使用 访问者模式 ;

大部分情况下 , 用不到 , 但是一旦需要用到的时候 , 访问者模式是唯一的解决方案 ;

1、Game 父类 ( 被访问者 )

代码语言:javascript复制
package visitor;

/**
 * 游戏父类 , 所有的游戏都要继承该类
 *      VipGame 和 FreeGame 都继承 Game , 这两个都是 Game
 *
 */
public abstract class Game {
    /**
     * 游戏名称
     */
    private String mGameName;

    public String getGameName() {
        return mGameName;
    }

    public void setGameName(String gameName) {
        this.mGameName = gameName;
    }

    /**
     * 当有访问者要访问游戏时 , 将访问者传入该方法
     *      用于判定访问者是否有权限访问游戏
     * @param visitor
     */
    public abstract void accept(IVisitor visitor);
}

2、VipGame 收费游戏 ( 被访问者 )

代码语言:javascript复制
package visitor;

public class VipVisitor implements IVisitor {

    /**
     * 访问免费游戏 , 打印游戏名称
     * @param freeGame
     */
    @Override
    public void visit(FreeGame freeGame) {
        System.out.println("我是 VIP , 可以访问免费游戏 : "   freeGame.getGameName());
    }

    /**
     * 访问收费游戏 , 打印游戏名称和价格
     * @param vipGame
     */
    @Override
    public void visit(VipGame vipGame) {
        System.out.println("我是 VIP , 可以访问收费游戏 : "   vipGame.getGameName()   " , 价格 : "   vipGame.getmVipPrice());
    }
}

3、FreeGame 免费游戏 ( 被访问者 )

代码语言:javascript复制
package visitor;

/**
 * 免费游戏
 */
public class FreeGame extends Game {
    @Override
    public void accept(IVisitor visitor) {
        visitor.visit(this);
    }
}

4、IVisitor 访问者接口

代码语言:javascript复制
package visitor;

/**
 * 该接口中声明了两个重载方法
 *      访问 FreeGame 的 visit 方法
 *      访问 VipGame 的 visit 方法
 *
 * 在 Game 中调用 visit 方法 , 将自身传递到 visit 方法作为参数
 *      分别调用不同的重载方法
 *
 * 访问者可以扩展很多了 , 可以定义若干实现了 IVisitor 接口的访问者
 *      每个 Visistor 访问者 , 访问不同的游戏 , 有不同的表现 , 如 :
 *          白嫖访问者 , 不花钱 , 只能玩免费游戏 , 不能玩收费游戏
 *          付费访问者 , 购买 VIP , 可以同时玩免费和收费游戏
 *
 *  对于免费游戏来说 , 传入任何访问者都可以访问 ;
 *  对于收费游戏来说 , 传入付费访问者, 才可以访问 ;
 *
 *  相同的 Visitor 对不同的数据产生不同的行为
 *  不同的 Visitor 对相同的数据产生不同的行为
 */
public interface IVisitor {

    /**
     * 访问免费游戏
     * @param freeGame
     */
    void visit(FreeGame freeGame);

    /**
     * 访问收费游戏
     * @param vipGame
     */
    void visit(VipGame vipGame);
}

5、VipVisitor 付费玩家

代码语言:javascript复制
package visitor;

public class VipVisitor implements IVisitor {

    /**
     * 访问免费游戏 , 打印游戏名称
     * @param freeGame
     */
    @Override
    public void visit(FreeGame freeGame) {
        System.out.println("我是 VIP , 可以访问免费游戏 : "   freeGame.getGameName());
    }

    /**
     * 访问收费游戏 , 打印游戏名称和价格
     * @param vipGame
     */
    @Override
    public void visit(VipGame vipGame) {
        System.out.println("我是 VIP , 可以访问收费游戏 : "   vipGame.getGameName()   " , 价格 : "   vipGame.getmVipPrice());
    }
}

6、测试类

代码语言:javascript复制
package visitor;

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        // 创建存放 Game 的集合
        ArrayList<Game> games = new ArrayList<>();

        // 创建免费游戏
        FreeGame freeGame = new FreeGame();
        freeGame.setGameName("超级马里奥");
        games.add(freeGame);

        // 创建收费游戏
        VipGame vipGame = new VipGame();
        vipGame.setGameName("绝地求生");
        vipGame.setmVipPrice(88);
        games.add(vipGame);

        // Vip 访问者访问免费和收费游戏
        for (Game game : games) {
            game.accept(new VipVisitor());
        }
    }
}

运行结果 :

0 人点赞