zoukankan      html  css  js  c++  java
  • 温故而知新:设计模式之组合模式(Composite)

    场景:

    如果想模拟windows的开始菜单,分析一下会发现里面的菜单项:有些有子菜单,有些则没有;因此大体可以将菜单类分为二类,设计代码如下:

    /// <summary>
        /// 菜单的显示接口
        /// </summary>
        public interface IMenu
        {
            void Show();
        }
    
        /// <summary>
        /// 菜单基类
        /// </summary>
        public class MenuBase
        {
            public string MenuName { set; get; }
        }
    
        /// <summary>
        /// 有子菜单的菜单类
        /// </summary>
        public class Menu : MenuBase, IMenu
        {
            public void Show()
            {
                Console.WriteLine(MenuName);
            }
    
            private IList<IMenu> _children;
    
            public IList<IMenu> Children
            {
                get
                {
                    if (_children == null)
                    {
                        _children = new List<IMenu>();
                    }
                    return _children;
                }
            }
        }
    
        /// <summary>
        /// 无子菜单的菜单类(即最底级菜单)
        /// </summary>
        public class SingleMenu : MenuBase, IMenu
        {
            public void Show()
            {
                Console.WriteLine( MenuName);
            }
        }
    


    客户端示例调用如下:

    class Program
        {
            //客户程序
            static void Main(string[] args)
            {
                Menu menuContainer = new Menu() { MenuName = "开始" };
                SingleMenu menuShutdown = new SingleMenu() { MenuName = "\t关机" };
                Menu menuProgram = new Menu() { MenuName = "\t程序" };
                SingleMenu menuStartUp = new SingleMenu() { MenuName = "\t\t启动" };
    
                menuProgram.Children.Add(menuStartUp);
                menuContainer.Children.Add(menuProgram);
                menuContainer.Children.Add(menuShutdown);
    
                menuContainer.Show();
                foreach (IMenu item in menuContainer.Children)
                {
                    if (item is SingleMenu)
                    {
                        item.Show();
                    }
                    else if (item is Menu)
                    {
                        ShowAllSubMenu(item);
                    }
                }
                Console.ReadLine();
            }
    
    
            /// <summary>
            /// 客户端调用的递归Show程序
            /// </summary>
            /// <param name="menu"></param>
            static void ShowAllSubMenu(IMenu menu)
            {
                if (menu is SingleMenu)
                {
                    menu.Show();
                }
                else if (menu is Menu)
                {
                    menu.Show();
                    IList<IMenu> children = (menu as Menu).Children;
                    foreach (IMenu i in children)
                    {
                        ShowAllSubMenu(i);
                    }
                    
                }
            }
        }
    

    从功能正确性上讲,上面的示意代码并无大错,但是如果从客户程序上考虑,却发现这样并非最佳实践:客户程序依赖了太多的Menu类细节,客户程序在树型菜单创建完成后,最关心的莫过于如何把菜单完整的显示出来,但上面的代码中为了达到这个目的,却不得不知道子菜单的内部实现(通过Children和类型判断),如果以后菜单类升级,修改了内部构造(比如将Children改成GetChildren),客户程序将被迫重新修改,这时候组合(Composite)模式就派上用场了。

    using System;
    using System.Collections.Generic;
    
    namespace composite
    {
        class Program
        {
            //客户程序
            static void Main(string[] args)
            {
                IMenu menuContainer = new Menu() { MenuName = "开始" };
                IMenu menuShutdown = new SingleMenu() { MenuName = "\t关机" };
                IMenu menuProgram = new Menu() { MenuName = "\t程序" };
                IMenu menuStartUp = new SingleMenu() { MenuName = "\t\t启动" };
    
                menuProgram.Add(menuStartUp);
                menuContainer.Add(menuProgram);
                menuContainer.Add(menuShutdown);
    
                menuContainer.Show();           
                
                Console.ReadLine();
            }
           
        }
    
        /// <summary>
        /// 菜单的显示接口
        /// </summary>
        public interface IMenu 
        {
            void Show();
            void Add(IMenu menu);
            void Remove(IMenu menu);
        }
    
        /// <summary>
        /// 菜单基类
        /// </summary>
        public abstract class MenuBase 
        {
            public string MenuName { set; get; }
            protected IList<IMenu> Children;   
        }
    
        /// <summary>
        /// 有子菜单的菜单类
        /// </summary>
        public class Menu : MenuBase,IMenu
        {        
    
            public void Show()        
            {            
                ShowAllSubMenu(this);
            }
    
            public void ShowAllSubMenu(IMenu menu)
            {
                if (menu is SingleMenu)
                {
                    menu.Show();
                }
                else if (menu is Menu)
                {
                    Console.WriteLine((menu as Menu).MenuName);
                    IList<IMenu> children = (menu as Menu).Children;
                    foreach (IMenu i in children)
                    {
                        ShowAllSubMenu(i);
                    }                
                }
            }
            
    
            public void Add(IMenu menu)
            {
                if (Children == null) { Children = new List<IMenu>(); }
    
                Children.Add(menu);
            }
    
            public void Remove(IMenu menu)
            {
                if (Children == null) { Children = new List<IMenu>(); }
    
                Children.Add(menu);
            }
           
        }
    
        /// <summary>
        /// 无子菜单的菜单类(即最底级菜单)
        /// </summary>
        public class SingleMenu :MenuBase, IMenu 
        {
            public void Show()
            {
                Console.WriteLine( MenuName);           
            }
    
            public void Add(IMenu menu)
            {
                throw new Exception("最底层菜单无法Add子元素!");
            }
    
            public void Remove(IMenu menu) 
            {
                throw new Exception("最底层菜单无法Remove子元素!");
            }
        }
    }
    

    最后再来理解Composite的意图可能就更清楚了(摘自terrylee的“.NET设计模式(11):组合模式(Composite Pattern)"):

    概述

    组合模式有时候又叫做部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解 耦。

    意图

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

    类图:

    作者:菩提树下的杨过
    出处:http://yjmyzz.cnblogs.com
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    也谈谈关于WEB的感想
    spring boot,https,双向ssl认证
    Spring Cloud Gateway(二)
    Spring Cloud Gateway(一)
    .Net Web Service 自定义返回值命名
    随便记一下,C#并行环境操作Winform的代码段
    随便记一下,解决Windows Server 2012无法远程登录的方法
    记录C#控件DataGridView绑定BindingList无法排序问题(转)
    记录SQL Server 2019链接Oracle 11g R2的过程
    Json CPP 中文支持与入门示例
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/1668787.html
Copyright © 2011-2022 走看看