合成复用原则:桌面系统与电商系统中构建灵活的软件设计方案

48 篇文章 0 订阅
9 篇文章 0 订阅

在这里插入图片描述

合成复用原则(Composite Reuse Principle, CRP),也被称作组合/聚合复用原则,它建议优先使用对象组合,而不是通过继承来复用代码。这个原则强调通过将对象组合成树形结构来实现代码复用,从而提供更大的灵活性。

肖哥弹架构 跟大家“弹弹” 代码设计技巧,需要代码关注

欢迎 点赞,点赞,点赞。

关注公号Solomon肖哥弹架构获取更多精彩内容

历史热点文章

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;
    }
}

问题分析:

  1. 继承层次结构SimpleProductBundleProduct 都继承自 Product 类,这限制了商品类型的扩展。
  2. 脆弱的基类问题:如果需要修改 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());
    }
}

解决的问题:

  1. 使用接口而非继承Product 接口提供了商品的契约,不同的商品类型通过实现该接口来复用代码。
  2. 灵活性增强:新增商品类型(如 SubscriptionProduct)时,只需实现 Product 接口,无需修改现有类。
  3. 解耦合ShoppingCart 通过接口与商品交互,不关心商品的具体类型,提高了系统的解耦性。
  4. 易于扩展:可以轻松添加新的商品特性,如折扣或优惠券,而不影响现有的商品实现。
8. 参考开源框架:

在Java Swing和.NET WPF等图形界面框架中,组件通常可以通过组合来构建复杂的用户界面。

9. 总结:

合成复用原则通过对象组合提供了一种灵活的代码复用方式。相比继承,组合可以更好地应对变化,因为新增组件类型不需要修改现有类。此外,组合允许动态地添加或移除组件,使得系统更加灵活。虽然这可能会增加设计的复杂性,但它为构建可扩展和可维护的软件系统提供了强大的支持。遵循合成复用原则有助于创建更加模块化和灵活的系统。

历史热点文章

  • 11
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Solomon_肖哥弹架构

你的欣赏就是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值