组合模式 (Composite Pattern)-结构型模式第二篇

静悄悄中时间飞逝,感叹一句最近TMD真忙真累,好在天生坚强性格阳光,不吹不黑,下面开始讲结构型模式(Structural patterns)的第2篇,Composite Pattern,中文称之为组合或复合模式。

定义:

Composite模式,能够灵活地实现循环、递归、部分-整体类的结构,使外部世界能够以一致的方式处理单体或集合。

解决的问题:

  1. 就像定义中所提及的那样,如何使客户端能够以一致的方式去访问某个单体或者集合,以使结构更加简洁
  2. 如何优雅的表达一个树形结构

怎么去解决(包含叶子Leaf的实现方式):

  1. 通过定义一个接口代表集合与个体所拥有的相同属性与行为,如IComponent
  2. 通过定义一个类Composite,它继承自IComponent,同时通过组合的方式它拥有一个名称为Components的属性,它代表着一个IComponent集合
  3. Composite有能力将一个实现了IComponent接口的对象加入到Components中
  4. 对于Composite的任何请求,都会被重定向到属性Components中的每个个体分别去执行
  5. 通过定义一个类Leaf,它继承自IComponent,代表着不可再分割的个体

举个栗子,在一个树状结构中,树杆、树枝、树叉皆由Composite来表示,而Leaf代表的是末端的每片树叶。

怎么去解决(进一步抽象):

通过分析我们能够发现在上面的解决方式中,如果有能力分辨一个IComponent是树杆还是树叶,便可以将Composite和Leaf合并为一个类型,于是我们为IComponent引入一个新的属性HasChild,并且给它换一个更贴切的名字INode,如果HasChild为True,则代表它是Composite,反之则代表它是Leaf

  1. 通过定义一个接口代表集合与个体所拥有的相同属性与行为,如INode,该接口拥有一个称为HasChild的布尔属性
  2. 通过定义一个类Node,它继承自INode,同时通过组合的方式它拥有一个名称为Nodes的属性,它代表着一个INode集合
  3. Node有能力添加一个实现了INode接口的对象到Nodes中
  4. 对于Node的任何请求,如果HasChild为True,那么请求会被重定向到属性Nodes的每一个个体分别去执行,如果HasChild为False,那么请求将由自身进行处理

UML:

实例:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

namespace CompositePattern
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var orginazation = new Node() { Name = "Microsoft" };
            var ceoOffice = new Node() { Name = "CEO Office" };
            var ceo = new Node() { Name = "CEO" };
            var cloudDepartment = new Node() { Name = "Cloud Department" };
            var departmentManager = new Node() { Name = "Department Manager" };
            var projectGroup = new Node() { Name = "Project Group" };
            var projectManager = new Node() { Name = "Project Manager" };
            var foobar = new Node() { Name = "Foobar" };
            var you = new Node { Name = "You" };
            orginazation.Add(ceoOffice);
            ceoOffice.Add(ceo);
            ceoOffice.Add(cloudDepartment);
            cloudDepartment.Add(departmentManager);
            cloudDepartment.Add(projectGroup);
            projectGroup.Add(projectManager);
            projectGroup.Add(foobar);
            projectGroup.Add(you);
            Console.WriteLine(orginazation.GetHierarchy());
            Console.ReadKey();
        }
    }

    /// <summary>
    /// Node
    /// </summary>
    public interface INode
    {
        string Name { get; set; }

        /// <summary>
        /// Adds the specified node.
        /// </summary>
        /// <param name="node">The node.</param>
        void Add(INode node);

        /// <summary>
        /// Gets the hierarchy.
        /// </summary>
        /// <returns></returns>
        string GetHierarchy();

        /// <summary>
        /// Gets a value indicating whether this instance has child node.
        /// </summary>
        /// <value>
        ///   <c>true</c> if this instance has child; otherwise, <c>false</c>.
        /// </value>
        bool HasChild { get; }

        /// <summary>
        /// Gets the child nodes.
        /// </summary>
        /// <value>
        /// The nodes.
        /// </value>
        ICollection<INode> Nodes { get; }

        /// <summary>
        /// Gets or sets the parent.
        /// </summary>
        /// <value>
        /// The parent.
        /// </value>
        INode Parent { get; set; }
    }

    public class Node : INode
    {
        public bool HasChild { get { return Nodes.Any(); } }
        public string Name { get; set; }

        public ICollection<INode> Nodes { get; } = new Collection<INode>();

        public INode _parent;
        public INode Parent { get { return _parent; } set { if (_parent != null) throw new Exception("The parent only can be set up once."); _parent = value; } }

        public void Add(INode node)
        {
            Nodes.Add(node);
            node.Parent = this;
        }

        private int GetFloorNumber(INode node, int floors = 0)
        {
            if (node != null)
            {
                floors++;
                return GetFloorNumber(node.Parent, floors);
            }
            return floors;
        }

        public string GetHierarchy()
        {
            var indents = string.Empty;
            var floorNumber = GetFloorNumber(this);
            for (int i = 0; i < floorNumber; i++)
            {
                indents = $">{indents}";
            }
            var result = $"{indents} {Name}";
            if (HasChild)
            {
                foreach (var item in Nodes)
                {
                    var childName = item.GetHierarchy();
                    result = $"{result}{Environment.NewLine}{childName}";
                }
            }
            return $"{result}";
        }
    }
}

注释:

输出:

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.