zoukankan      html  css  js  c++  java
  • (原创)无废话C#设计模式之十一:Composite

     

    无废话C#设计模式之十一:Composite

     

    意图

     

           将对象组合成树形结构以表示“部分-整体”的层次结构。Composite模式使得用户对单个对象和组合对象的使用具有一致性。

     

    场景

     

    我们知道,一个网络游戏通常会有多个游戏大区。每一个游戏大区会有很多游戏服务器(一个游戏大区就是一组游戏服务器)。每一个游戏服务器上会有不同的服务(可以是多个服务)。这是一个明显的部分-整体关系,假设我们现在需要制作一个服务器管理工具,用于显示所有大区、服务器以及服务的信息,并且能开启这些服务(可以是单独开启一个服务,也可以是开启整个服务器上的所有服务,也可以是开启整个大区的所有服务)。

    可以看到,游戏服务器和游戏大区都是一个组合对象,而游戏服务是最底层的节点。客户端在开启一个游戏大区服务的时候,必须和游戏服务器以及游戏服务进行依赖,而在开启游戏服务器上所有服务的时候,必须和游戏服务进行依赖。试想一下,如果一个公司的总裁在管理上不但需要和各总监以及经理进行沟通,还有和底层的员工沟通,那么总裁是不是会太忙碌了一点?由此,我们引入组合模式,使组合对象和单个对象具有一样的表现形式。

     

    示例代码

     

    using System;

    using System.Collections.Generic;

    using System.Text;

     

    namespace CompositeExample

    {

        class Program

        {

            static void Main(string[] args)

            {

                Element server1 = new GameServer("GS1", "192.168.0.1");

                server1.Add(new GameService("Lobby1", 1, "S5Lobby1"));

                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("Lobby2", 1, "S5Lobby2"));

                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.Display();

                area.Start();

                area.Stop();

            }

        }

     

        abstract class Element

        {

            protected string name;

     

            public Element(string name)

            {

                this.name = name;

            }

     

            public abstract void Add(Element element);

            public abstract void Remove(Element element);

            public abstract void Display();

            public abstract void Start();

            public abstract void Stop();

        }

     

        class GameService : Element, IComparable<GameService>

        {

            private int serviceType;

            private string serviceName;

     

            public GameService(string name, int serviceType, string serviceName)

                : base (name)

            {

                this.serviceName = serviceName;

                this.serviceType = serviceType;

            }

     

            public override void Add(Element element)

            {

                throw new ApplicationException("xxx");

            }

     

            public override void Remove(Element element)

            {

                throw new ApplicationException("xxx");

            }

     

            public override void Display()

            {

                Console.WriteLine(string.Format("name:{0},serviceType:{1},serviceName:{2}", name, serviceType, serviceName));

            }

     

            public override void Start()

            {

                Console.WriteLine(string.Format("{0} started", name));

            }

     

            public override void Stop()

            {

                Console.WriteLine(string.Format("{0} stopped", name));

            }

     

            public int CompareTo(GameService other)

            {

                return other.serviceType.CompareTo(serviceType);

            }

        }

     

        class GameServer : Element

        {

            private string serverIP;

            private List<GameService> serviceList = new List<GameService>();

     

            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 Display()

            {

                Console.WriteLine(string.Format("{0}{1}({2}){3}", new string('+', 10), name, serverIP, new string('+', 10)));

                foreach (Element element in serviceList)

                {

                    element.Display();

                }

            }

     

            public override void Start()

            {

                serviceList.Sort();

                Console.WriteLine("=============Starting the whole " + name + "=============");

                for (int i = 0; i < serviceList.Count; i++ )

                {

                    serviceList[i].Start();

                }

                Console.WriteLine("=============The whole " + name + " started=============");

            }

     

            public override void Stop()

            {

                Console.WriteLine("=============Stopping the whole " + name + "=============");

                for (int i = serviceList.Count -1; i >= 0; i--)

                {

                    serviceList[i].Stop();

                }

                Console.WriteLine("=============The whole " + name + " stopped=============");

            }

        }

     

        class GameArea : Element

        {

            private List<GameServer> serverList = new List<GameServer>();

     

            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 Display()

            {

                Console.WriteLine(new string('=',20));

                Console.WriteLine("       " + name);

                Console.WriteLine(new string('=', 20));

                foreach (Element element in serverList)

                {

                    element.Display();

                }

            }

     

            public override void Start()

            {

                Console.WriteLine("=============Starting the whole " + name + "=============");

                foreach (Element element in serverList)

                {

                    element.Start();

                }

                Console.WriteLine("=============The whole " + name + " started=============");

            }

     

            public override void Stop()

            {

                Console.WriteLine("=============Stopping the whole " + name + "=============");

                foreach (Element element in serverList)

                {

                    element.Stop();

                }

                Console.WriteLine("=============The whole " + name + " stopped=============");

            }

        }

    }

    代码执行结果如下图:

     

     

    代码说明

     

    l         Element类型就是抽象构件的角色,它给组合对象以及单个对象提供了一个一致的接口,使得它们都能有一致的行为。

    l         这里就出现一个问题,组合对象需要通过AddRemove方法来为其添加子节点,而最底层的树叶构件下并没有任何子节点,在接口中定义这些操作子节点方法的方式叫做透明方式的合成模式,缺点就是不够安全,容易在运行时出现异常。如果把这些操作子节点的方法定义从抽象构件中删除,由各树枝构件来实现的话,就是安全方式的合成模式,缺点也就是不够透明。具体怎么做还要根据自己的需求。

    l         GameService当然就是树叶构件,如果调用它的Add以及Remove方法会抛出一个异常。当然,你也可以以其它方式来记录这种逻辑错误。

    l         GameServer是一个树枝构件。一个游戏服务器上会有多个游戏服务。这里注意到一点,在开启服务器上所有服务的时候,我们对服务进行了排序,排序是按照服务的类型进行的。然后,我们按照服务类型从大到小的次序开启了服务。一般一个网络游戏的服务会有很多种,而这些服务的开启是有先后次序的,先开记录日志的服务、排名服务、再开数据交换的服务、最后才是大厅服务等。我们不可能让用户进入大厅的时候没有地方写数据和日志吧。开启服务是按照次序的,那么关闭服务也就是按照相反的次序了。

    l         从上面这点,我们就可以看到组合模式的好处了,树枝构件在管理树叶构件的时候通常还会有一些逻辑,不会是简单的增加和删除操作。如果这些工作交给客户端去做的话,就太不合理了。

    l         最上层的树枝构件就是GameArea类型。它并没有什么特殊的地方。

     

    何时采用

     

    l         从代码角度来说,如果类型之间组成了层次结构,你希望使用统一的接口来管理每一个层次的类型的时候。

    l         从应用角度来说,如果你希望把一对多的关系转化为一对一的关系的时候。

     

    实现要点

     

    l         使用透明模式还是安全模式根据自己的需要定。

    l         在某些情况下,树叶构件可以访问树枝构件获取一些信息。

    l         如果树叶构件数量比较多,树枝构件频繁遍历子节点的话可以考虑进行缓存。

    l         既然所有对象有了统一的接口,客户端应该针对抽象构件进行编程。

     

    注意事项

     

    l        

    欢迎大家阅读我的极客时间专栏《Java业务开发常见错误100例》【全面避坑+最佳实践=健壮代码】
  • 相关阅读:
    Android SwitchButton(滑动开关)
    创建您自己的Maven模板
    Bag标签成一条线的代码来实现中国字
    rabbitmq的java简单的实现
    【七】注入框架RoboGuice使用:(Your First Custom Binding)
    Sqlmap渗透测试是常用语句
    Android NOtification 使用(震动 闪屏 铃声)
    Android loader 详解
    Android实现获取本机中所有图片
    Android保存图片到系统图库
  • 原文地址:https://www.cnblogs.com/lovecherry/p/920360.html
Copyright © 2011-2022 走看看