天天看点

【设计模式】四、面向对象设计原则之开闭原则

系列文章|源码

https://github.com/tyronczt/design-mode-learn

定义-是什么

开放封闭原则(OCP,Open Closed Principle)是所有面向对象原则的核心。软件设计本身所追求的目标就是封装变化、降低耦合,而开放封闭原则正是对这一目标的最直接体现。其他的设计原则,很多时候是为实现这一目标服务的。

Software entities like classes,modules and functions should be open for extension but closed for modifications 一个软件实体, 如类, 模块, 函数等应该对扩展开放, 对修改封闭.

这也是开放封闭原则的核心思想:对扩展开放,对修改封闭。

思考-为什么

在需求不断变化的软件设计过程中 ,如何保障系统的稳定性及可维护性,开闭原则至关重要,即多拓展,少修改。

作用

  1. 减少测试成本。只需测试拓展的代码逻辑,减少对原有代码的功能测试;
  2. **提高复用性。**开闭原则的思路使程序设计更加原子性,粒子越小,复用的可能性越大;
  3. **提高可维护性。**面向对象开发的要求;

    应用-怎么用

    使用开闭原则的方式

    1、抽象约束

    一,通过接口或者抽象类约束扩展,对扩展进行边界限定,不允许出现在接口或抽象类中不存在的public方法;

    二,参数类型、引用对象尽量使用接口或者抽象类,而不是实现类;

    三,抽象层尽量保持稳定,一旦确定即不允许修改。

    2、元数据(metadata)控制模块行为

    元数据就是用来描述环境和数据的数据,通俗地说就是配置参数,参数可以从文件中获得,也可以从数据库中获得。 Spring容器就是一个典型的元数据控制模块行为的例子,其中达到极致的就是控制反转(Inversion of Control)。

    3、封装变化

    对变化的封装包含两层含义:

    一,将相同的变化封装到一个接口或者抽象类中;

    二,将不同的变化封装到不同的接口或抽象类中,不应该有两个不同的变化出现在同一个接口或抽象类中。

    4、制定项目章程

    在一个团队中,建立项目章程是非常重要的,因为章程中指定了所有人员都必须遵守的约定,对项目来说,约定优于配置。

    案例分析

    案例:支付业务

    需求:商城系统,用户买好东西后,准备付款,支持支付宝、微信、银行卡支付。

    代码仓库地址:https://github.com/tyronczt/design-mode-learn/tree/main/design-mode-learn-2-01

原始代码
public enum PaymentTypeEnum {
    ALI_PAY("AliPayment"),
    WECHAT_PAY("WechatPayment"),
    BANK_PAY("BankPayment");
}
public interface IPayBusinessService {
    void payOperate(PaymentMethodsEnum type, UserInfo userInfo);
}

public class PayBusinessSerivceImpl implements IPayBusinessService {

    @Override
    public void payOperate(PaymentMethodsEnum type, UserInfo userInfo) {
        // 支付宝
        if (type == PaymentTypeEnum.ALI_PAY) {
            System.out.println("正在支付宝付款...");
        } else if (type == PaymentTypeEnum.WECHAT_PAY) {
            System.out.println("正在微信付款...");
        } else if (type == PaymentTypeEnum.BANK_PAY) {
            System.out.println("正在银行卡付款...");
        }
    }
}
           

上述代码实现, 虽然满足了业务需求,但是后期维护会比较麻烦。主要原因是:

①作为支付这样的核心业务逻辑,相对比较复杂,都在一个方法内实现,这个方法会很庞大,不利于排查问题;

②支付方式随着业务的发展也会增加,新支付方式的增加会应该整个业务逻辑,增加测试成本,不利于系统的稳定性;

改进方案
public class Pay {
    private PaymentTypeEnum paymentTypeEnum;

    public PaymentTypeEnum getPaymentTypeEnum() {
        return paymentTypeEnum;
    }

    public void setPaymentTypeEnum(PaymentTypeEnum paymentTypeEnum) {
        this.paymentTypeEnum = paymentTypeEnum;
    }
}

public interface IPaymentService {
    void pay(Pay pay);
}

public class AliPaymentServiceImpl implements IPaymentService{
    @Override
    public void pay(Pay pay) {
        System.out.println("正在支付宝付款...");
    }
}

public class WechatPaymentServiceImpl implements IPaymentService{
    @Override
    public void pay(Pay pay) {
        System.out.println("正在微信付款...");
    }
}

public class BankPaymentServiceImpl implements IPaymentService{
    @Override
    public void pay(Pay pay) {
        System.out.println("正在银行付款...");
    }
}
           

改进方案将不同支付方式通过不同的类来实现支付逻辑,修改逻辑不会对原有代码产生影响,同时也把公用的支付逻辑进行抽离。

此处只是简单的实现,在真实生产代码中还会结合工厂、策略模式等,将支付方式在项目启动时注入工厂类中,在使用具体策略时,只需从工厂类中拿到对应策略的实现类即可完成相应逻辑。

参考

设计模式六大原则(六)----开闭原则

如何正确理解策略模式?开闭原则是关键!

继续阅读