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);

    }

 

 

     StartGameVisitorStopGameVisitor以及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         GameServiceGameServer以及GameArea都是具体的元素。从组合角度来看,GameServerGameArea是树枝,GameService是树叶。看一下Accept()方法,对于具体元素来说,它应该明确接受特定层次的访问者,如果类型转换正确的话,那么调用访问者的访问方法。

 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=============");

            }

        }

    }

}

 

调用方式

 

    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         针对结构中同一层次的不同类型甚至是不同层次的类型进行迭代。

从这里可以看出,访问者模式的主要适用还是针对对象结构(往往有层次关系),并且需要在设计的时候实现为各类型预留接受访问者的接口。

 

posted @ 2009-06-09 13:44  张国涛  阅读(428)  评论(0)    收藏  举报