访问者模式
访问者模式是对象行为型模式,将某中数据类型中的元素分离封装成类,在不改变原本的数据结构的情况下可以添加作用于这些元素的新的操作,为数据结构的每个元素提供多个访问方式。它在不改变原数据结构的情况下,为类扩展了方法。
访问者模式将新的类的行为,放入了一个名叫访问者的类中,而不是去添加到原有的类中。这样的交互方式不仅没有对原有的类造成污染,还实现了我们需要添加的方法。
结构
说明
- 抽象访问者(Visitor)- (支持重载的情况下)声明一系类以具体元素为参数的相同标签的访问方法。如果需要其他私有变量,也可通过参数形式传递(但这样会破坏数据的封装性)
- 具体访问者(Concrete Visitor)- 为不同的元素类型实现相同行为的扩展方法。
- 抽象元素(Element)- 声明一个可以接收访问者的方法。该方法必须要又一个访问者接口。
- 具体元素(Concrete Element)- 实现访问方法。该方法需以自身作为参数调用。这样才可以实现重定向到对应元素。(这里实现了双分派)
- 对象结构(ObjectStructure,可选)- 包含元素对象的容器,提供常见的集合操作,以及元素遍历操作。(可以于迭代器模式一起使用)
实现
抽象访问者
public interface IVisitor
{
void VisitorPlayer(Player player);
void VisitorEnemy(Enemy enemy);
}
具体访问者
public class NameVisitor : IVisitor
{
public void VisitorPlayer(Player player)
{
Debug.Log("Player 的名字: " + player.Name);
}
public void VisitorEnemy(Enemy enemy)
{
Debug.Log("Enemy 的名字: " + enemy.Name);
}
}
抽象元素
public interface ICharacter
{
void Display(IVisitor visitor);
}
具体元素
public class Player : ICharacter
{
public string Name { get; set; }
public void Display(IVisitor visitor)
{
visitor.VisitorPlayer(this);
}
}
public class Enemy : ICharacter
{
public string Name { get; set; }
public void Display(IVisitor visitor)
{
visitor.VisitorEnemy(this);
}
}
对象结构
public class CharacterList
{
private IList<ICharacter> _characters;
public CharacterList()
{
_characters = new List<ICharacter>();
}
public void Add(ICharacter character) => _characters.Add(character);
public void Remove(ICharacter character) => _characters.Remove(character);
public void ForEach(IVisitor visitor)
{
foreach (var character in _characters)
{
character.Display(visitor);
}
}
}
调用端
public class VisitorExample : MonoBehaviour
{
private void Start()
{
//创建容器
CharacterList list = new CharacterList();
//创建元素
ICharacter player = new Player
{
Name = "Player",
};
ICharacter enemy = new Enemy
{
Name = "Enemy",
};
list.Add(player);
list.Add(enemy);
//创建访问者
IVisitor visitor = new NameVisitor();
list.ForEach(visitor);
}
}
双分派 - 静态分派 与 动态分派
- 静态分派
即编译器中确定的方法调用,根据声明的类型来判断访问的方法- 动态分派
即无法在编译器中确定的方法调用,需要在虚拟机的运行时确定。多见于抽象方法(纯虚方法),虚方法等。
访问者模式通过两次分派,一次动态分派和一次静态分派,来实现了伪动态分派的效果。
应用场景
- 当你需要对复杂数据类型结构进行所以元素操作时,可以使用访问者模式
- 对象结构在不变的情况下,提供多种不同操作,但却于对象不相关的操作时,可以避免这些不相关的操作影响到对象。
优缺点
优点
- 扩展数据类型方法,只需添加访问者对象即可,满足开闭原则
- 将每个对象方法拆分,满足单一职责原则
- 扩展的方法,不影响原有类型
缺点
- 很难对其类型添加新的类型,这样会导致这个访问者都需修改
- 类型的扩展方法如果需要元素的私有变量,会改变元素的封装性
与其他模式的关系
- 访问者模式可以与迭代器模式一起使用,来遍历元素的扩展方法
- 访问者模式可以与组合模式一起使用,来对树进行操作
访问者模式在开发中并不常用,第一就是太复杂,第二就是需要一个相对稳定的数据类型。而在实际开发中数据类型很少不进行修改和添加。
C# 中的扩展方法 与 访问者模式的区别
C# 中的扩展方法是在语法层面为类型添加方法,虽然没有修改原数据类型,但却改变了调用时方法的选择。且无法做到统一,只能针对一个类型进行扩展。
访问者模式是在类层面为类型添加方法,是对原有数据类不更改的情况下,进行方法扩展,且不会影响原有的类。但其自身实现向复杂,且需要稳定的数据类型。