温故而知新:设计模式之组合模式(Composite)

简介: 场景: 如果想模拟windows的开始菜单,分析一下会发现里面的菜单项:有些有子菜单,有些则没有;因此大体可以将菜单类分为二类,设计代码如下:   /// /// 菜单的显示接口 /// public interface IMenu { ...

场景:

如果想模拟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 《设计模式》]

 

类图:

img_bf940da2ca9cbe2458afc7e8cc0fb338.png

目录
相关文章
|
4月前
|
设计模式
二十三种设计模式全面解析-组合模式与迭代器模式的结合应用:构建灵活可扩展的对象结构
二十三种设计模式全面解析-组合模式与迭代器模式的结合应用:构建灵活可扩展的对象结构
|
5天前
|
设计模式 Java 容器
【设计模式系列笔记】组合模式
组合模式(Composite Pattern)是一种结构型设计模式,它允许将对象组合成树状结构以表示部分-整体的层次结构。组合模式使得客户端可以统一处理单个对象和对象组合,而无需区分它们的类型。
38 12
|
14天前
|
设计模式 Java
小谈设计模式(20)—组合模式
小谈设计模式(20)—组合模式
|
1月前
|
设计模式 JavaScript uml
设计模式之组合模式
设计模式之组合模式
设计模式之组合模式
|
1月前
|
设计模式 存储 安全
【设计模式】组合模式
【设计模式】组合模式
|
2月前
|
设计模式 Java
浅谈设计模式 - 组合模式(十二)
浅谈设计模式 - 组合模式(十二)
51 0
|
3月前
|
设计模式 存储 安全
聊聊Java设计模式-组合模式
组合(Composite)模式,又叫做树形模式,主要用来处理树形结构数据。是将一组对象组织成树形结构,以表示一种“部分-整体”的层次结构。让客户端可以统一单个对象和组合对象的处理逻辑
29 1
聊聊Java设计模式-组合模式
|
3月前
|
设计模式 存储
设计模式 | 组合模式
设计模式 | 组合模式
12 0
|
3月前
|
设计模式 Go 开发工具
Golang设计模式——17组合模式
Golang设计模式——17组合模式
13 1
|
3月前
|
设计模式 存储 前端开发
【设计模式】之组合模式
组合模式是一种非常有用的设计模式,在前端开发中经常用于处理复杂的层次结构和嵌套关系。它通过将对象组合成树形结构来表示“部分-整体”的关系,并提供了一种优雅而灵活的方式来处理复杂性。通过使用组合模式,我们可以构建可扩展和易于维护的应用程序。然而,需要根据具体情况权衡使用组合模式所带来的优缺点。
39 0