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

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

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

欢迎 点赞,点赞,点赞。

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

历史热点文章

2. 合成复用原则设计图:

3. 合成复用原则解决什么:

合成复用原则解决了继承层次结构可能带来的问题,如脆弱的基类问题和不够灵活的扩展性。

4. 合成复用原则特点:

  • 灵活性:组合提供了更大的灵活性,可以动态地添加或移除组件。
  • 复用性:通过组合,可以在不同的地方复用相同的对象。

5. 合成复用原则缺点:

  • 设计复杂性:可能需要更多的设计工作来确定合适的组件和它们之间的关系。
  • 性能考虑:在某些情况下,组合可能带来轻微的性能开销。

6. 合成复用原则使用场景:

当需要复用功能时,首先考虑是否可以使用对象组合来实现,而不是立即求助于继承。

7. 合成复用原则案例

7.1 图形界面组件库案例

图形界面组件库,需要实现各种按钮、文本框和面板等组件。

重构前:

java 复制代码
    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();
        }
    }

重构后

java 复制代码
    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 电商平台案例

允许用户浏览商品、将商品添加到购物车、下订单等。在这个系统中,商品可以是简单的单品,也可以是由多个商品组成的套餐。重构前,系统可能使用继承来处理商品和套餐,这可能导致一些问题,如脆弱的基类问题。

重构前:

java 复制代码
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 类,可能会影响到所有子类。

重构后:

java 复制代码
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. 总结:

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

历史热点文章

相关推荐
码上一元2 小时前
SpringBoot自动装配原理解析
java·spring boot·后端
计算机-秋大田2 小时前
基于微信小程序的养老院管理系统的设计与实现,LW+源码+讲解
java·spring boot·微信小程序·小程序·vue
魔道不误砍柴功4 小时前
简单叙述 Spring Boot 启动过程
java·数据库·spring boot
失落的香蕉4 小时前
C语言串讲-2之指针和结构体
java·c语言·开发语言
枫叶_v4 小时前
【SpringBoot】22 Txt、Csv文件的读取和写入
java·spring boot·后端
wclass-zhengge4 小时前
SpringCloud篇(配置中心 - Nacos)
java·spring·spring cloud
路在脚下@4 小时前
Springboot 的Servlet Web 应用、响应式 Web 应用(Reactive)以及非 Web 应用(None)的特点和适用场景
java·spring boot·servlet
黑马师兄4 小时前
SpringBoot
java·spring
数据小小爬虫4 小时前
如何用Java爬虫“偷窥”淘宝商品类目API的返回值
java·爬虫·php
暮春二十四4 小时前
关于用postman调用接口成功但是使用Java代码调用却失败的问题
java·测试工具·postman