c#设计模式系列之Visitor模式
设计模式总纲:
1、"开-闭"原则——模块应对扩展开放,而对修改关闭。
2、里氏代换原则——如果调用的是父类的话,那么换成子类也完全可以运行。里氏代换原则是继承复用的一个基础。
3、合成复用原则——要少用继承,多用合成关系来实现。
4、依赖倒转原则——抽象不应该依赖与细节,细节应当依赖与抽象。
要针对接口编程,而不是针对实现编程。
5、接口隔离原则——每一个接口应该是一种角色,不多不少,不干不该干的事,该干的事都要干。
6、抽象类
7、迪米特法则——最少知识原则。不要和陌生人说话。
c#设计模式系列之Visitor模式
目的:
通过统一的接口来实现不同类型元素的操作,并且可以增加新的操作而不修改元素的类
实现方式:
在类的基础上添加访问者,使我们的类可以通过访问者来实现操作的访问。
实例场景:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace VisitorExample
{
l IVisitor是一个空接口,目的是为了抽象所有的访问者,抽象层次相当于Element。
interface IVisitor { }
l IGameServerVisitor、IGameServiceVisitor以及IGameAreaVisitor定义了访问者的访问操作(操作接口)。在这里,我们并没有把它们合并为一个接口,因为访问者可能仅针对一部分元素,或一个层次的元素,不一定都针对所有元素。
interface IGameServiceVisitor
{
void Visit(GameService gameService);
}
interface IGameServerVisitor
{
void Visit(GameServer gameServer);
}
interface IGameAreaVisitor
{
void Visit(GameArea gameArea);
}
StartGameVisitor、StopGameVisitor以及QueryPlayCountVisitor是具体的访问者。它们根据自己的需求实现不同的操作接口,虽然都是访问者但是它们的目的不太一样,见下。
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);
}
l Element类型就是抽象构件(抽象元素),它给组合对象以及单个对象提供了一个一致的接口,使得它们都能有一致的行为。唯一和组合模式不同的是,在这里定义了一个接受访问者的接口。
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);
}
l GameService、GameServer以及GameArea都是具体的元素。从组合角度来看,GameServer和GameArea是树枝,GameService是树叶。看一下Accept()方法,对于具体元素来说,它应该明确接受特定层次的访问者,如果类型转换正确的话,那么调用访问者的访问方法。
{
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=============");
}
}
}
}
调用方式
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());
}
}
}
通过上述的实例我们可能发现,访问者模式在某种程度上类似适配器、装饰模式,同时访问者模式结构复杂,逻辑性强,所以在应用上有一定的难度,需要认真把握!
访问者模式适用下面的情况:
l 对象结构中包含很多类型,这些类型没有统一的接口,而我们又希望使得对象的操作进行统一。
l 希望为对象结构中的类型新增操作,并且不希望改变原有的代码。
l 对象结构中的一些类型之间发生耦合,而它们的实现又经常会发生变动。
l 针对结构中同一层次的不同类型甚至是不同层次的类型进行迭代。
从这里可以看出,访问者模式的主要适用还是针对对象结构(往往有层次关系),并且需要在设计的时候实现为各类型预留接受访问者的接口。

浙公网安备 33010602011771号