无废话C#设计模式之二十一:Visitor
意图
实现通过统一的接口访问不同类型元素的操作,并且通过这个接口可以增加新的操作而不改变元素的类。
场景
想不出什么好例子,我们在组合模式的那个例子上进行修改吧。我们知道,无论是游戏大区、游戏服务器还是游戏的服务都是一个元素,只不过它们的层次不一样。对于这样的层次结构,我们使用了组合模式来统一各层的接口,这样对游戏大区的操作和对游戏服务器的操作对调用方来说没有什么两样。在现实中,组合模式的运用往往没有这么顺利:
l 如果元素需要增加新的操作,那么势必需要在抽象元素中增加接口,这个时候的改动就非常大了,几乎每一个具体元素都需要修改。
l 如果元素并没有统一的接口,并且树枝角色中有多种树叶角色,那么树枝角色势必需要根据树叶类型来调用不同的方法。
l 如果树叶角色的接口经常发生变动,那么一旦发生变动操作树叶的树枝角色也需要发生修改。
访问者模式可以解决这些问题。
示例代码
using System; using System.Collections; using System.Collections.Generic; using System.Text;
namespace VisitorExample { class Program { static void Main(string[] args) { Element server1 = new GameServer("GS1", "192.168.0.1"); server1.Add(new GameService("Lobby1", 1, "S5Lobby1", 100)); server1.Add(new GameService("Lobby2", 1, "S5Lobby2", 200)); server1.Add(new GameService("Gate1", 2, "S5Gate1")); server1.Add(new GameService("DataExchange1", 3, "S5DataExchange1")); server1.Add(new GameService("Rank1", 4, "S5Rank1")); server1.Add(new GameService("Log1", 5, "S5Log1")); Element server2 = new GameServer("GS2", "192.168.0.2"); server2.Add(new GameService("Lobby3", 1, "S5Lobby3", 150)); server2.Add(new GameService("Lobby4", 1, "S5Lobby4", 250)); server2.Add(new GameService("Gate2", 2, "S5Gate2")); server2.Add(new GameService("DataExchange2", 3, "S5DataExchange1")); server2.Add(new GameService("Rank2", 4, "S5Rank2")); server2.Add(new GameService("Log2", 5, "S5Log2")); Element area = new GameArea("电信区"); area.Add(server1); area.Add(server2); area.Accept(new StartGameVisitor()); //A1 area.Accept(new StopGameVisitor()); //B1 server1.Accept(new QueryPlayerCountVisitor()); server2.Accept(new QueryPlayerCountVisitor()); area.Accept(new QueryPlayerCountVisitor()); } }
interface IVisitor { }
interface IGameServiceVisitor { void Visit(GameService gameService); }
interface IGameServerVisitor { void Visit(GameServer gameServer); }
interface IGameAreaVisitor { void Visit(GameArea gameArea); }
class StartGameVisitor : IVisitor, IGameServiceVisitor, IGameServerVisitor, IGameAreaVisitor { public void Visit(GameService gameService) { //A9 gameService.StartGameService(this); }
public void Visit(GameServer gameServer) { //A6 gameServer.StartGameServer(this); }
public void Visit(GameArea gameArea) { //A3 gameArea.StartGameArea(this); }
}
class StopGameVisitor : IVisitor, IGameServiceVisitor, IGameServerVisitor, IGameAreaVisitor { public void Visit(GameService gameService) { //B7 Console.WriteLine(string.Format("{0} stopped", gameService.Name)); }
public void Visit(GameServer gameServer) { //B5 Console.WriteLine("=============Stopping the whole " + gameServer.Name + "============="); for (int i = gameServer.ServiceList.Count - 1; i >= 0; i--) { gameServer.ServiceList[i].Accept(this); } Console.WriteLine("=============The whole " + gameServer.Name + " stopped============="); }
public void Visit(GameArea gameArea) { //B3 Console.WriteLine("=============Stopping the whole " + gameArea.Name + "============="); foreach (GameServer element in gameArea.ServerList) { element.Accept(this); } Console.WriteLine("=============The whole " + gameArea.Name + " stopped============="); } }
class QueryPlayerCountVisitor : IVisitor, IGameServerVisitor, IGameAreaVisitor { public void Visit(GameServer gameServer) { int playerCount = 0; foreach (GameService gameService in gameServer) { if (gameService.ServiceType == 1) playerCount += gameService.PlayerCount; } Console.WriteLine("=============Player Count on " + gameServer.Name + " is:" + playerCount); }
public void Visit(GameArea gameArea) { int playerCount = 0; foreach ( |