Answer.Geng 阅读(50) 评论(0)

模式简介


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

Composite模式的定义中有两个关键词:树形结构一致性。作为一种结构型模式,Composite模式适用于处理树形结构的问题。其次,对于客户端来说,用户使用这个结构时不必对其中的树枝及叶子进行区分,统一地使用组合结构中的所有对象。

如下是一个使用Group来管理邮箱地址的例子,China组中包含两个组及一个邮箱地址,这是一个非常典型的树状结构。下面,我们将通过介绍遍历China组中所有邮箱地址的示例来讲述Composite模式。

结构说明


角色说明

  • Component

为组合中的对象定义一个共同的接口

  • Leaf

在组合中表示叶节点对象,没有子节点

  • Composite

有子节点的那部分类

示例分析


回到第一节中的示例,首先创建IEmail,为Email和Group定义一个共同的接口,相当于Component角色。

interface IEmail
{
    void Print();
}

创建Email类(Leaf)和Group类(Composite),实现IEmail接口。注意这里Group类Print方法的实现,遍历了emailList集合,对每一个子部件进行操作,这也是Composite模式实现的核心:用户在与组合结构中的对象交互时,如果接收者是一个叶节点,直接处理该请求。如果接收者是Composite(即本示例中的Group),则对其子部件发送处理请求。

class Email : IEmail
{
    public string Name { get; set; }
    public string EmailAddress { get; set; }
    public Email(string name,string address)
    {
        this.Name = name;
        this.EmailAddress = address;
    }
    public void Print()
    {
        Console.WriteLine($"Name:{Name} , EmailAddress:{EmailAddress}");
    }
}

class Group : IEmail
{
    private List<IEmail> emailList = new List<IEmail>();
    private string groupName;
    public Group(string groupName)
    {
        this.groupName = groupName;
    }
    
    public void Add(params IEmail[] mails)
    {
        foreach (var mail in mails)
        {
            emailList.Add(mail);
        }
    }

    public void Remove(IEmail mail)
    {
        emailList.Remove(mail);
    }

    public IEmail GetChild(int index)
    {
        return emailList[index];
    }

    public void Print()
    {
        foreach (var mail in emailList)
        {
            mail.Print();
        }
    }
}

客户端调用,构建树状结构,通过China.Print()遍历其中所有的Email信息。

static void Main(string[] args)
{
    Email wangWu = new Email("Wang Wu", "Wu.Wang@xx.com");
    Group ShangHai = new Group("ShangHai");
    ShangHai.Add(new Email("Zhang San", "San.Zhange@xx.com"), new Email("Li Si", "Si.Li@xx.com"));
    Group BeiJing = new Group("BeiJing");
    BeiJing.Add(new Email("Zhao Liu", "Liu.Zhao@xx.com"));
    Group China = new Group("China");
    China.Add(ShangHai);
    China.Add(wangWu);
    China.Add(BeiJing);
    China.Print();
    Console.ReadLine();
}

输出结果:

对子部件的操作该放在哪


文章开头我们强调了Composite模式定义中的两个关键词。严格来说,邮箱示例中对于Group类的设计是不符合一致性要求的,因为Add、Remove等管理子部件的操作仅存在于Composite类中,客户端在使用组合结构时必须区分这这两种角色。一种解决方法是将这些方法搬到Component接口中,Leaf类提供一些缺省的操作,当然事实上它并不需要添加或删除子节点,这样看起来有点奇怪。

在Component接口中定义子节点管理的方法具有更好的透明性,因为客户端可以一致的使用所有的组件;而在Composite类中定义管理子部件的操作则看起来更加合理,这种设计具有更好的安全性。

在实际开发当中需要对透明性和安全性进行权衡,根据具体情况决定系统如何设计。

适用场景


  • 希望表示对象的部分-整体层次结构

  • 希望客户端忽略单个对象与组合对象的差别,统一的适用组合结构中所有的对象

源码下载


dotnet-design-pattern_composite