第一话:初代营销系统
技术背景:
营销系统初期,技术负责人小易考虑到初期仅需支持两种渠道,采用直接耦合方式可快速上线
邮件活动
typescript
代码解读
复制代码
@Getter @Setter public class EmailCampaign { private String name; public EmailCampaign(String name) { this.name = name; } public void execute() { System.out.println("执行电子邮件活动: " + name); } }
社交媒体活动
typescript
代码解读
复制代码
@Getter @Setter public class SocialMediaCampaign { private String name; public SocialMediaCampaign(String name) { this.name = name; } public void execute() { System.out.println("执行社交媒体活动: " + name); } }
组合活动
csharp
代码解读
复制代码
public class BundleCampaign { private EmailCampaign emailCampaign; private SocialMediaCampaign socialMediaCampaign; public BundleCampaign(EmailCampaign emailCampaign,SocialMediaCampaign socialMediaCampaign){ this.emailCampaign = emailCampaign; this.socialMediaCampaign = socialMediaCampaign; } public void execute(){ emailCampaign.execute(); socialMediaCampaign.execute(); System.out.println("执行一系列活动: " + emailCampaign.getName() + " + " + socialMediaCampaign.getName()); } }
架构示意图
css
代码解读
复制代码
A[营销系统] A --> B[邮件活动] A --> C[社交活动] A --> D[硬编码组合]
埋下隐患:
- 新增活动类型需修改BundleCampaign类
- 组合深度限制为固定两层
第二话:需求突袭
技术背景: 市场总监
提出P0级需求:支持三级组合营销(主活动=短信+子组合(邮件+社交))
,要求不影响线上运行。
临时方案:采用硬编码支持新类型,并新建一个短信活动。
短信活动
typescript
代码解读
复制代码
@Getter @Setter public class SMSCampaign { private String name; public SMSCampaign(String name) { this.name = name; } public void execute() { System.out.println("执行短信活动: " + name); } }
硬编码组合类
typescript
代码解读
复制代码
public class BundleCampaign { List<Object> campaigns = new ArrayList<>(); public void addCampaign(Object campaign) { campaigns.add(campaign); } public void execute() { for (Object obj : campaigns) { if (obj instanceof EmailCampaign) { ((EmailCampaign) obj).execute(); } else if (obj instanceof SocialMediaCampaign) { ((SocialMediaCampaign) obj).execute(); } else if (obj instanceof SMSCampaign) { ((SMSCampaign) obj).execute(); } else { return; } } } }
方案缺陷:
- 当需要新增第四种活动时,不得不在
BundleCampaign
中添加更多if-else
- 每次新增活动,都需要修改核心逻辑,严重影响到测试的完整性
- 判断中需要类型强制转换,嵌套组合支持不完善
第三话:设计模式重构
技术背景: 技术负责人小易考虑到若不对当前营销系统进行重构,很有可能在未来的某天出现重大系统故障问题
,决定使用组合模式进行重构。
设计模式解析
核心思想:将对象组织成树形结构,统一处理单个对象和组合对象。
主要角色:
- Component(抽象类):定义统一接口(如
execute()
) - Leaf(叶子节点):基础活动(邮件、社交)
- Composite(组合节点):可包含其他
Component
的组合活动(BundleCampaign
)
重构:应用组合模式
1. 定义抽象组件
typescript
代码解读
复制代码
@Getter @Setter public abstract class Campaign { protected String name; public Campaign(String name) { this.name = name; } /** * 定义活动执行抽象方法 */ public abstract void execute(); public void add(Campaign campaign) { throw new UnsupportedOperationException("不支持组合活动"); } }
2. 叶子节点:基础活动
java
代码解读
复制代码
public class EmailCampaign extends Campaign { public EmailCampaign(String name) { super(name); } @Override public void execute() { System.out.println("执行电子邮件活动: " + name); } } public class SMSCampaign extends Campaign { public SMSCampaign(String name) { super(name); } @Override public void execute() { System.out.println("执行短信活动: " + name); } } public class SocialMediaCampaign extends Campaign { public SocialMediaCampaign(String name) { super(name); } @Override public void execute() { System.out.println("执行社交媒体活动: " + name); } }
3. 组合节点:可嵌套的组合活动
typescript
代码解读
复制代码
public class BundleCampaign extends Campaign { private List<Campaign> campaigns = new ArrayList<>(); public BundleCampaign(String name) { super(name); } @Override public void execute() { System.out.println(name + " 开启!"); for (Campaign c : campaigns) { c.execute(); } } @Override public void add(Campaign campaign) { campaigns.add(campaign); } }
测试代码
ini
代码解读
复制代码
public class CompositeTest { @Test public void testCampaign() { Campaign emailCampaign = new EmailCampaign("双十一折扣"); Campaign socialMediaCampaign = new SocialMediaCampaign("微博热搜"); Campaign bundleCampaign = new BundleCampaign("主活动"); bundleCampaign.add(emailCampaign); bundleCampaign.add(socialMediaCampaign); Campaign childBundle = new BundleCampaign("子活动"); childBundle.add(new SMSCampaign("追加优惠")); bundleCampaign.add(childBundle); bundleCampaign.execute(); } }
输出:
makefile
代码解读
复制代码
主活动开启! 执行电子邮件活动: 双十一折扣 执行社交媒体活动: 微博热搜 子活动开启! 执行短信活动: 追加优惠
重构后效果
系统改进点
- 新增类型无需修改已有代码(符合开闭原则)
- 客户端无需区分叶子/组合节点(统一接口)
- 支持无限层级嵌套(灵活组合)
复杂度对比
维度 | 重构前 | 重构后 |
---|---|---|
新增活动类型 | 修改核心类 | 新增叶子类即可 |
组合嵌套深度 | 固定2层 | 无限递归 |
类型安全检查 | 运行时判断 | 编译时保障 |
扩展成本 | O(n)线性增长 | O(1)恒定 |
长话短说
核心思想与使用指南
核心思想:
- 统一性:客户端无需区分叶子节点和组合节点
- 递归组合:通过树形结构实现无限嵌套
实施路线图
- 识别树形结构:找出系统中的层次关系
- 抽象组件接口:定义通用操作方法
- 区分叶子节点:实现不可再分的元素
- 构建复合节点:实现可嵌套的容器
- 实现递归逻辑:在复合节点中遍历子元素
- 添加类型约束(可选):通过安全模式限制操作
代码步骤
- 定义抽象类/接口,声明通用方法(如
execute()
)。 - 创建叶子类,实现基础功能。
- 创建组合类,管理子组件集合,并实现递归调用。
- 客户端通过抽象类型操作所有对象。
最佳实践场景
- 文件系统:文件与文件夹的统一管理
- 组织架构:部门与员工的层级关系
- UI组件:窗口与控件的嵌套组合
- 语法树:程序结构的抽象表示
关键决策点
何时选择组合模式?
- 需要表示对象的部分-整体层次结构
- 希望客户端忽略组合与单个对象的不同
- 需要递归组合的结构化数据
何时避免使用?
- 系统不存在明显的层次结构
- 组合操作需要完全不同的接口
- 性能敏感的叶子节点操作