合成复用原则(Composite Reuse Principle, CRP),也被称作组合/聚合复用原则,它建议优先使用对象组合,而不是通过继承来复用代码。这个原则强调通过将对象组合成树形结构来实现代码复用,从而提供更大的灵活性。
肖哥弹架构 跟大家“弹弹” 代码设计技巧,需要代码关注
欢迎 点赞,点赞,点赞。
关注公号Solomon肖哥弹架构获取更多精彩内容
历史热点文章
- 数据访问对象模式(Data Access Object Pattern):电商平台商品管理实战案例分析
- Holder模式(Holder Pattern):公司员工权限管理系统实战案例分析
- 一个项目代码讲清楚DO/PO/BO/AO/E/DTO/DAO/ POJO/VO
2. 合成复用原则设计图:
3. 合成复用原则解决什么:
合成复用原则解决了继承层次结构可能带来的问题,如脆弱的基类问题和不够灵活的扩展性。
4. 合成复用原则特点:
- 灵活性:组合提供了更大的灵活性,可以动态地添加或移除组件。
- 复用性:通过组合,可以在不同的地方复用相同的对象。
5. 合成复用原则缺点:
- 设计复杂性:可能需要更多的设计工作来确定合适的组件和它们之间的关系。
- 性能考虑:在某些情况下,组合可能带来轻微的性能开销。
6. 合成复用原则使用场景:
当需要复用功能时,首先考虑是否可以使用对象组合来实现,而不是立即求助于继承。
7. 合成复用原则案例
7.1 图形界面组件库案例
图形界面组件库,需要实现各种按钮、文本框和面板等组件。
重构前:
public abstract class GUIComponent {
public abstract void draw();
}
public class Button extends GUIComponent {
@Override
public void draw() {
// 绘制按钮
}
}
public class TextBox extends GUIComponent {
@Override
public void draw() {
// 绘制文本框
}
}
// 使用GUIComponent的客户端代码
public class GUIClient {
public void display() {
new Button().draw();
new TextBox().draw();
}
}
重构后
public interface Component {
void draw();
void add(Component component);
void remove(Component component);
}
public class Leaf implements Component {
@Override
public void draw() {
// 具体的绘制逻辑
}
@Override
public void add(Component component) {
// 叶子节点不支持添加子组件
}
@Override
public void remove(Component component) {
// 叶子节点不支持移除子组件
}
}
public class Composite implements Component {
private List<Component> components = new ArrayList<>();
@Override
public void draw() {
for (Component component : components) {
component.draw();
}
}
@Override
public void add(Component component) {
components.add(component);
}
@Override
public void remove(Component component) {
components.remove(component);
}
}
// 使用Component的客户端代码
public class GUIClient {
public void display() {
Composite panel = new Composite();
panel.add(new Button()); // 将按钮作为组件添加到面板
panel.add(new TextBox()); // 将文本框作为组件添加到面板
panel.draw();
}
}
7.2 电商平台案例
允许用户浏览商品、将商品添加到购物车、下订单等。在这个系统中,商品可以是简单的单品,也可以是由多个商品组成的套餐。重构前,系统可能使用继承来处理商品和套餐,这可能导致一些问题,如脆弱的基类问题。
重构前:
public abstract class Product {
protected String name;
protected double price;
public abstract double calculateTotalPrice(int quantity);
}
public class SimpleProduct extends Product {
@Override
public double calculateTotalPrice(int quantity) {
return price * quantity;
}
}
public class BundleProduct extends Product {
private List<Product> includedProducts;
public BundleProduct() {
includedProducts = new ArrayList<>();
}
public void addProduct(Product product) {
includedProducts.add(product);
}
@Override
public double calculateTotalPrice(int quantity) {
double total = 0;
for (Product product : includedProducts) {
total += product.calculateTotalPrice(quantity);
}
return total;
}
}
问题分析:
- 继承层次结构:
SimpleProduct
和BundleProduct
都继承自Product
类,这限制了商品类型的扩展。 - 脆弱的基类问题:如果需要修改
Product
类,可能会影响到所有子类。
重构后:
public interface Product {
double calculateTotalPrice(int quantity);
}
public class SimpleProduct implements Product {
private String name;
private double price;
public SimpleProduct(String name, double price) {
this.name = name;
this.price = price;
}
@Override
public double calculateTotalPrice(int quantity) {
return price * quantity;
}
}
public class BundleProduct implements Product {
private List<Product> includedProducts;
public BundleProduct() {
includedProducts = new ArrayList<>();
}
public void addProduct(Product product) {
includedProducts.add(product);
}
@Override
public double calculateTotalPrice(int quantity) {
double total = 0;
for (Product product : includedProducts) {
total += product.calculateTotalPrice(quantity);
}
return total;
}
}
public class ShoppingCart {
private List<Product> products;
public ShoppingCart() {
products = new ArrayList<>();
}
public void addProduct(Product product) {
products.add(product);
}
public double calculateTotalPrice() {
double total = 0;
for (Product product : products) {
total += product.calculateTotalPrice(1); // Assume quantity is 1 for simplicity
}
return total;
}
}
// 使用Product和ShoppingCart的客户端代码
public class ECommerceClient {
public static void main(String[] args) {
SimpleProduct laptop = new SimpleProduct("Laptop", 1200.00);
SimpleProduct mouse = new SimpleProduct("Mouse", 25.00);
BundleProduct bundle = new BundleProduct();
bundle.addProduct(laptop);
bundle.addProduct(mouse);
ShoppingCart cart = new ShoppingCart();
cart.addProduct(bundle);
System.out.println("Total price: " + cart.calculateTotalPrice());
}
}
解决的问题:
- 使用接口而非继承:
Product
接口提供了商品的契约,不同的商品类型通过实现该接口来复用代码。 - 灵活性增强:新增商品类型(如
SubscriptionProduct
)时,只需实现Product
接口,无需修改现有类。 - 解耦合:
ShoppingCart
通过接口与商品交互,不关心商品的具体类型,提高了系统的解耦性。 - 易于扩展:可以轻松添加新的商品特性,如折扣或优惠券,而不影响现有的商品实现。
8. 参考开源框架:
在Java Swing和.NET WPF等图形界面框架中,组件通常可以通过组合来构建复杂的用户界面。
9. 总结:
合成复用原则通过对象组合提供了一种灵活的代码复用方式。相比继承,组合可以更好地应对变化,因为新增组件类型不需要修改现有类。此外,组合允许动态地添加或移除组件,使得系统更加灵活。虽然这可能会增加设计的复杂性,但它为构建可扩展和可维护的软件系统提供了强大的支持。遵循合成复用原则有助于创建更加模块化和灵活的系统。
历史热点文章
- 享元模式(Flyweight Pattern):网页游戏中的角色对象管理实战案例分析
- 观察者模式(Observer Pattern):股票交易系统实战案例分析
- 策略模式(Strategy Pattern):电商平台的优惠券系统实战案例分析
- 模板方法模式(Template Method Pattern):视频播放应用实战案例分析
- 命令模式(Command Pattern):网络爬虫任务队列实战案例分析
- 迭代器模式(Iterator Pattern):电商平台商品分类浏览实战案例分析
- 中介者模式(Mediator Pattern):即时通讯软件实战案例分析
- 备忘录模式(Memento Pattern):游戏存档系统实战案例分析
- 状态模式(State Pattern):电商平台订单状态管理实战案例分析
- 责任链模式(Chain of Responsibility Pattern):电商平台的订单审批流程实战案例分析
- 访问者模式(Visitor Pattern):电商平台商品访问统计实战案例分析
- 工厂方法模式(Factory Method Pattern): 电商多种支付实战案例分析
- 抽象工厂模式(Abstract Factory Pattern):多风格桌面应用实战案例分析
- 建造者模式(Builder Pattern): 在线订单系统实战案例分析
- 原型模式(Prototype Pattern): 云服务环境配置实战案例分析
- 适配器模式(Adapter Pattern):第三方支付集成实战案例分析
- 装饰器模式(Decorator Pattern):电商平台商品价格策略实战案例分析
- 单例模式(Singleton Pattern):购物车实战案例分析