意圖
實現通過統一的接口訪問不同類型元素的操作,並且通過這個接口可以增加新的操作而不改變元素的類。
場景
想不出什麼好例子,我們在組合模式的那個例子上進行修改吧。我們知道,無論是游戲大區、游戲服務器還是游戲的服務都是一個元素,只不過它們的層次不一樣。對於這樣的層次結構,我們使用了組合模式來統一各層的接口,這樣對游戲大區的操作和對游戲服務器的操作對調用方來說沒有什麼兩樣。在現實中,組合模式的運用往往沒有這麼順利:
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 (GameServer gameServer in gameArea)
{
foreach (GameService gameService in gameServer)
{
if (gameService.ServiceType == 1)
playerCount += gameService.PlayerCount;
}
}
Console.WriteLine("=============Player Count on " + gameArea.Name + " is:" + playerCount);
}
}
abstract class Element
{
protected string name;
public string Name
{
get { return name; }
}
public Element(string name)
{
this.name = name;
}
public abstract void Add(Element element);
public abstract void Remove(Element element);
public abstract void Accept(IVisitor visitor);
}
class GameService : Element, IComparable<GameService>
{
private int serviceType;
public int ServiceType
{
get { return serviceType; }
set { serviceType = value; }
}
private string serviceName;
private int playerCount;
public int PlayerCount
{
get { return playerCount; }
set { playerCount = value; }
}
public GameService(string name, int serviceType, string serviceName)
: base(name)
{
this.serviceName = serviceName;
this.serviceType = serviceType;
}
public GameService(string name, int serviceType, string serviceName, int playerCount)
: base(name)
{
this.serviceName = serviceName;
this.serviceType = serviceType;
this.playerCount = playerCount;
}
public override void Add(Element element)
{
throw new ApplicationException("xxx");
}
public override void Remove(Element element)
{
throw new ApplicationException("xxx");
}
public override void Accept(IVisitor visitor)
{
//A8,B6
IGameServiceVisitor gameServiceVisitor = visitor as IGameServiceVisitor;
if (gameServiceVisitor != null ) gameServiceVisitor.Visit(this);
}
public int CompareTo(GameService other)
{
return other.serviceType.CompareTo(serviceType);
}
public void StartGameService(IVisitor visitor)
{
//A10
Console.WriteLine(string.Format("{0} started", name));
}
}
class GameServer : Element
{
private string serverIP;
private List<GameService> serviceList = new List<GameService>();
public List<GameService> ServiceList
{
get { return serviceList; }
}
public GameServer(string name, string serverIP)
: base(name)
{
this.serverIP = serverIP;
}
public override void Add(Element element)
{
serviceList.Add((GameService)element);
}
public override void Remove(Element element)
{
serviceList.Remove((GameService)element);
}
public override void Accept(IVisitor visitor)
{
//A5,B5
IGameServerVisitor gameServerVisitor = visitor as IGameServerVisitor;
if (gameServerVisitor != null ) gameServerVisitor.Visit(this);
}
public IEnumerator<GameService> GetEnumerator()
{
foreach (GameService gameService in serviceList)
yIEld return gameService;
}
public void StartGameServer(IVisitor visitor)
{
//A7
IGameServerVisitor gameServerVisitor = visitor as IGameServerVisitor;
if (gameServerVisitor != null )
{
serviceList.Sort();
Console.WriteLine("=============Starting the whole " + name + "=============");
foreach (Element gameService in serviceList)
{
gameService.Accept(visitor);
}
Console.WriteLine("=============The whole " + name + " started=============");
}
}
}
class GameArea : Element
{
private List<GameServer> serverList = new List<GameServer>();
public List<GameServer> ServerList
{
get { return serverList; }
}
public GameArea(string name)
: base(name) { }
public override void Add(Element element)
{
serverList.Add((GameServer)element);
}
public override void Remove(Element element)
{
serverList.Remove((GameServer)element);
}
public override void Accept(IVisitor visitor)
{
//A2,B2
IGameAreaVisitor gameAreaVisitor = visitor as IGameAreaVisitor;
if (gameAreaVisitor != null ) gameAreaVisitor.Visit(this);
}
public IEnumerator<GameServer> GetEnumerator()
{
foreach (GameServer gameServer in serverList)
yIEld return gameServer;
}
public void StartGameArea(IVisitor visitor)
{
//A4
IGameAreaVisitor gameAreaVisitor = visitor as IGameAreaVisitor;
if (gameAreaVisitor != null )
{
Console.WriteLine("=============Starting the whole " + name + "=============");
foreach (Element gameServer in serverList)
{
gameServer.Accept(visitor);
}
Console.WriteLine("=============The whole " + name + " started=============");
}
}
}
}