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

  1. 《设计模式》系列序——写给未来的自己
  2. 工厂方法模式(Factory Method)-创建型模式第一篇
  3. 抽象工厂模式(Abstract Factory Pattern)-创建型模式第二篇
  4. 单例模式(Singleton Pattern)-创建型模式第三篇
  5. 建造者模式(Builder Pattern)-创建型模式第四篇
  6. 原型模式(Prototype Pattern)-创建型模式第五篇
  7. 装饰者模式 (Decorator Pattern)-结构型模式第一篇
  8. 组合模式 (Composite Pattern)-结构型模式第二篇
  9. 适配器模式 (Adapter Pattern)-结构型模式第三篇
  10. 代理模式 (Proxy Pattern)-结构型模式第四篇
  11. 桥接模式 (Bridge 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}";
}
}
}
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}"; } } }
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 *