设计模式(九)—— 组合模式
模式简介
将对象组合成树形结构以表示“部分-整体”的层次结构,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类中定义管理子部件的操作则看起来更加合理,这种设计具有更好的安全性。
在实际开发当中需要对透明性和安全性进行权衡,根据具体情况决定系统如何设计。
适用场景
-
希望表示对象的部分-整体层次结构
-
希望客户端忽略单个对象与组合对象的差别,统一的适用组合结构中所有的对象
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· .NET 9 new features-C#13新的锁类型和语义
· Sdcb Chats 技术博客:数据库 ID 选型的曲折之路 - 从 Guid 到自增 ID,再到
· 语音处理 开源项目 EchoSharp
· 《HelloGitHub》第 106 期
· Spring AI + Ollama 实现 deepseek-r1 的API服务和调用
· 使用 Dify + LLM 构建精确任务处理应用