从王者荣耀看设计模式(二十三.访问者模式)

从王者荣耀看设计模式从王者荣耀看设计模式(二十三.访问者模式)

一.简介

在游戏中,根据场上状况给英雄配备合适的装备是赢的比赛的重要环节。对于刚开始玩王者荣耀(低端玩家like me),在选择装备的时候只会关注装备能给英雄带来多少伤害的增幅(装备属性),而大神级别玩家会仔细研究装备的详细描述

二.模式动机

在有些集合对象中可能存在多种不同类型的元素,而且不同的调用者在使用这些元素时也有所区别,这些调用者称为访问者。此时,可以使用访问者模式来进行系统设计。访问者模式为多个访问者访问集合对象中的多种元素提供了一种解决方案。在本实例中,集合中存储有装备"冰霜法杖"和"破魔刀"的属性和详细描述。低端玩家关注点(想要访问的对象)为集合中的属性。高端玩家关注点为集合中的详细描述。

三.访问者模式

访问者模式(Visitor Pattern):表示一个作用于某对象结构中的各元素的操作,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。访问者模式是一种对象行为型模式

访问者模式的应用场景
在以下情况下可以使用访问者模式
■ 一个对象结构包含很多类型的对象,希望对这些对象实施一些依赖其具体类型的操作。在访问者中针对每一种具体的类型都提供了一个访问操作,不同类型的对象可以有不同的访问操作
■ 需要对一个对象结构中的对象进行很多不同的并且不相关的操作
■ 对象结构中对象相应的类很少改变,但经常需要在此对象结构上定义新的操作

访问者模式的通用类图
从王者荣耀看设计模式(二十三.访问者模式)

访问者模式涉及的角色
⑴.Vistor(抽象访问者)
抽象访问者为对象结构类中每一个具体元素类ConcreteElement声明了一个访问操作。从这些操作的名称或参数类型可以清楚知道需要访问的具体元素的类型,具体访问者需要实现这些操作方法,定义对这些元素的访问操作
⑵.ConcreteVisitor(具体访问类)
具体访问类实现了每个由抽象访问者声明的操作,每一个操作用于访问对象结构中一种类型的对象
⑶.Element(抽象元素)
抽象元素一般是抽象类或者接口,它定义了一个accept()方法,该方法以一个抽象访问类作为参数
⑷.ConcreteElement(具体元素)
具体元素实现了accept()方法,在其accpet()中调用访问者的访问方法以便完成对一个元素的操作
⑸.ObjectStructure(对象结构)
对象结构时一个元素的集合,它用于存放元素对象,并且提供了遍历其内部元素的方法。

访问者模式优点:
⑴.使得增加新的访问操作变得容易
⑵.将有关元素对象的访问行为集中到一个访问对象对象中。类的职责更加清晰,有利于对象结构中元素对象的复用。相同的对象结构可以供多个不同的访问者访问
⑶.可以跨过类的等级结构访问属于不同的等级结构元素类

访问者模式缺点:
⑴.增加新的元素类很困难
⑵.破坏封装。访问者模式要求访问者对象访问并调用每一个元素对象的操作,这意味着元素对象有时候必须暴露一些自己的内部操作和内部状态,否则无法供访问者访问。

四.结构图

从王者荣耀看设计模式(二十三.访问者模式)

五.设计类图

从王者荣耀看设计模式(二十三.访问者模式)

六.代码实现

创建抽象访问者类(Player类)

package com.practice.Visitor;

import com.practice.elements.MagicKnife;
import com.practice.elements.TheStaffOfFrost;
/*
 * 创建抽象访问者类
 */
public interface Player {
    //查看装备冰霜法杖
    void view(TheStaffOfFrost tsof);
    //查看装备破魔刀
    void view(MagicKnife mkf);
}

Player类作为抽象访问者,在此是一个抽象类,定义了业务方法view(TheStaffOfFrost tsof)和view(MagicKnife mkf),用于访问两种不同类型的元素
创建具体访问者(LowPlayer类)

package com.practice.Visitor;

import com.practice.elements.MagicKnife;
import com.practice.elements.TheStaffOfFrost;
/*
 * 创建具体访问者
 */
public class LowPlayer implements Player {
    private String playerAttenEquip;//玩家关注的装备名称
    private String playerAttenValue;//玩家关注的装备属性
    
    @Override
    public void view(TheStaffOfFrost tsof) {
        playerAttenEquip = tsof.getEquipName();
        playerAttenValue= tsof.getDamageValue();
    }

    @Override
    public void view(MagicKnife mkf) {
        playerAttenEquip = mkf.getEquipName();
        playerAttenValue = mkf.getDamageValue();
    }
    
    public void getTSOFF() {
        System.out.println("低端玩家:装备的名称:【" + playerAttenEquip 
                + "】,关注点:【" + playerAttenValue + "】");  
    }
    
    public void getMKF() {
        System.out.println("低端玩家:装备的名称:【" + playerAttenEquip
                + "】,关注点:【" + playerAttenValue + "】");
    }
}

lowPlayer类是具体访问者类,它实现了在抽象访问者类中声明的抽象访问方法,对于两种类型的元素对象,实现方法有所不同
创建具体访问者(highPlayer)

package com.practice.Visitor;

import com.practice.elements.MagicKnife;
import com.practice.elements.TheStaffOfFrost;
/*
 * 创建具体访问类(HighPlayer)
 */
public class HighPlayer implements Player {
    private String playerAttenEquip;//装备名称
    private String playerAttenIntro;//装备详细介绍

    @Override
    public void view(TheStaffOfFrost tsof) {
        playerAttenEquip = tsof.getEquipName();
        playerAttenIntro = tsof.getIntroduce();
    }

    @Override
    public void view(MagicKnife mkf) {
        playerAttenEquip = mkf.getEquipName();
        playerAttenIntro = mkf.getIntroduce();
    }

    public void getTSOFF() {
        System.out.println("高端玩家:装备的名称:【" + playerAttenEquip + "】,关注点:【" +  playerAttenIntro + "】");
    }

    public void getMKF() {
        System.out.println("高端玩家:装备的名称:【" + playerAttenEquip + "】,关注点:【" + playerAttenIntro + "】");
    }
}

HighPlayer类也是具体访问者类,实现了在抽象访问者中声明的抽象访问方法
抽象元素类(Equipment类)

package com.practice.elements;

import com.practice.Visitor.Player;
/*
 * 创建具体元素类
 */
public interface Equipment {
    public void accept(Player player);
}

Equipment是抽象元素类,它声明了一个抽象方法accept(Player player),用于接受访问者的访问
具体访问类(TheStaffOfFrost)

package com.practice.elements;

import com.practice.Visitor.Player;
/*
 * 创建具体访问类
 */
public class TheStaffOfFrost implements Equipment {
    private String introduce;//装备详细介绍
    private String equipName = "冰霜法杖";
    private String damageValue;//装备的属性
    
    public TheStaffOfFrost(String introduce,String damageValue) {
        super();
        this.introduce = introduce;
        this.damageValue = damageValue;
    }

    @Override
    public void accept(Player player) {
        player.view(this);
    }
    
    public String getIntroduce() {
        return introduce;
    }
    
    public String getEquipName() {
        return equipName;
    }
    
    public String getDamageValue() {
        return damageValue;
    }
}

TheStaffOfFrost类实现了Equipment接口,它是具体元素类之一。实现了在Equipment接口中声明的accept(Visitor visitor)方法,在实现过程中调用player对象的view()方法来实现对元素对象的访问。
具体元素类(MagicKnife)

package com.practice.elements;

import com.practice.Visitor.Player;
/*
 * 创建具体元素类
 */
public class MagicKnife implements Equipment {
    private String introduce;
    public String equipName = "破魔刀";
    private String damageValue;

    public MagicKnife(String introduce,String damageValue) {
        super();
        this.introduce = introduce;
        this.damageValue = damageValue;
    }
    
    @Override
    public void accept(Player player) {
        player.view(this);
    }
    
    public String getIntroduce() {
        return introduce;
    }
    
    public String getDamageValue() {
        return damageValue;
    }
    
    public String getEquipName() {
        return equipName;
    }
}

MagicKnife类也实现了EquiPment接口,它也是具体元素类之一
对象结构类(EquipmentRail)

package com.practie.ObjectStructure;

import java.util.ArrayList;
import java.util.List;

import com.practice.Visitor.Player;
import com.practice.elements.Equipment;
/*
 * 创建对象结构类
 */
//装备栏
public class EquipmentRail {
    //装备列表
    private List<Equipment> equipList = new ArrayList<Equipment>();
    //添加装备
    public void addEquip(Equipment equip) {
        equipList.add(equip);
    }
    
    //供查看者查看装备
    public void show(Player player) {
        for(Equipment equip:equipList) {
            equip.accept(player);
        }
    }
}

EquipmentRail类是存储元素对象的对象结构类,它定义了一个ArrayList类型的集合对象list,并提供了增加元素对象的方法addEquip()方法,此外它还提供了一个accept()方法,在该方法中循环调用集合中每一个元素对象的accept()方法,以实现对每一个元素对象的访问
客户测试类(Client)

package com.practice.client;

import com.practice.Visitor.HighPlayer;
import com.practice.Visitor.LowPlayer;
import com.practice.Visitor.Player;
import com.practice.elements.MagicKnife;
import com.practice.elements.TheStaffOfFrost;
import com.practie.ObjectStructure.EquipmentRail;

public class Client {
    public static void main(String[] args) {
        EquipmentRail er = new EquipmentRail();
        //添加装备
        er.addEquip(new TheStaffOfFrost("唯一被动—结霜:英雄技能造成伤害会附带20%的减速效果,持续2秒","+ 150法术攻击,+1050最大生命"));
        er.addEquip(new MagicKnife("唯一被动—破魔:增加等同于自身物理攻击40%的法术防御,最多增加300点","+ 100物理攻击;基础法术防御+50"));
        
        Player lowPlayer = new LowPlayer();
        Player highPlayer = new HighPlayer();
        
        //两类玩家分别访问装备栏
        er.show(lowPlayer);
        er.show(highPlayer);
        
        ((LowPlayer)lowPlayer).getMKF();
        ((HighPlayer)highPlayer).getMKF();
    }
}

运行结果:
从王者荣耀看设计模式(二十三.访问者模式)

七.源代码下载:

从王者荣耀看设计模式(二十三.访问者模式)

上一篇:A+B for Input-Output Practice (III)


下一篇:A+B for Input-Output Practice (I)