目 录CONTENT

文章目录

开放封闭原则(OCP)— 拥抱变化,拒绝修改

冰点
2020-02-23 / 0 评论 / 0 点赞 / 1 阅读 / 10009 字

前言

大家好,我是冰点,今天为大家讲解SOLID五大原则的核心——开放封闭原则(OCP)。作为面向对象设计的终极目标,OCP是构建可长期稳定迭代系统的关键,核心围绕“对扩展开放,对修改关闭”的核心思想,兼顾系统稳定性与需求扩展性。

一、OCP核心定义

开放封闭原则的核心内涵的是:软件实体(类、模块、接口等)应当对扩展开放,允许通过新增代码实现功能迭代;同时对修改关闭,禁止改动已通过验证的稳定核心代码。其本质是隔离系统的“稳定部分”与“可变部分”,避免因需求变化触碰系统根基,降低故障风险。

二、OCP核心价值

遵循OCP的核心价值主要体现在两方面:一是提升系统稳定性,核心代码无需频繁修改,减少线上故障概率;二是降低维护成本,需求迭代时仅需扩展新代码,无需梳理原有复杂逻辑,提升开发效率。反之,违背OCP会导致代码耦合度高、修改牵一发而动全身。

三、OCP核心落地逻辑

落地OCP的核心逻辑无需复杂操作,关键在于两点:一是抽象定义规范,将系统稳定部分抽象为接口或抽象类,明确核心规范,作为扩展的基础;二是封装可变逻辑,将需求变化对应的功能,封装为独立的实现类,所有变化仅集中在新增代码中,不影响核心逻辑。

需明确的是,OCP并非“完全不能修改代码”,而是禁止修改稳定的核心代码。适度的代码优化的是必要的,但需区分“核心稳定代码”与“可变扩展代码”,通过抽象与封装,实现需求扩展与系统稳定的平衡,这也是面向对象设计的核心精髓。

五、案例分析

以车联网终端协议解析模块为例(贴合物流系统实操场景),若初始仅支持A品牌终端的定位协议解析,遵循OCP设计时,会先抽象出终端协议解析接口(定义解析规范),再封装A品牌协议解析实现类。后续需新增B、C品牌终端解析功能时,无需修改核心接口及A品牌解析类,仅需新增对应实现类并实现接口,即可完成扩展,完美体现“对扩展开放、对修改关闭”的核心思想。

以下是该案例的完整代码示例(Java语言,贴合物流车联网实际开发),清晰呈现OCP原则在车联网场景的落地方式:

1. 抽象稳定核心:定义车联网终端协议解析接口(不随需求变化修改)

// 车联网终端协议解析接口(稳定核心),定义定位数据解析规范
// 适配所有品牌终端解析,不随新增终端品牌修改
public interface TerminalParseInterface {
    // 核心方法:解析终端上报的原始定位数据
    // 参数:原始定位字节数据;返回:解析后的定位DTO(包含经纬度、速度等信息)
    PositionDTO parse(byte[] originalData);
}

2. 初始功能实现:A品牌终端解析类(无需修改)

// 初始功能:A品牌终端协议解析实现类
// 线上稳定运行后,无需修改任何代码
public class ATerminalParser implements TerminalParseInterface {
    @Override
    public PositionDTO parse(byte[] originalData) {
        // 模拟A品牌终端协议解析逻辑(真实场景为解析终端专属字节格式)
        PositionDTO position = new PositionDTO();
        // 解析经纬度、速度(模拟逻辑,贴合真实车联网解析场景)
        position.setLongitude(BigDecimal.valueOf(Double.parseDouble(new String(originalData, 0, 8))));
        position.setLatitude(BigDecimal.valueOf(Double.parseDouble(new String(originalData, 8, 8))));
        position.setSpeed(Integer.parseInt(new String(originalData, 16, 4)));
        position.setTerminalBrand("A品牌");
        return position;
    }
}

3. 需求扩展:新增B、C品牌终端解析(仅扩展,不修改核心)

// 扩展需求1:新增B品牌终端解析类(物流客户新增B品牌定位终端)
// 仅新增类,不修改接口和原有A品牌解析类
public class BTerminalParser implements TerminalParseInterface {
    @Override
    public PositionDTO parse(byte[] originalData) {
        // 模拟B品牌终端协议解析逻辑(与A品牌格式不同,独立实现)
        PositionDTO position = new PositionDTO();
        position.setLongitude(BigDecimal.valueOf(Double.parseDouble(new String(originalData, 2, 10))));
        position.setLatitude(BigDecimal.valueOf(Double.parseDouble(new String(originalData, 12, 10))));
        position.setSpeed(Integer.parseInt(new String(originalData, 22, 3)));
        position.setTerminalBrand("B品牌");
        return position;
    }
}

// 扩展需求2:新增C品牌终端解析类(物流客户新增C品牌定位终端)
// 仅新增类,不修改任何核心代码
public class CTerminalParser implements TerminalParseInterface {
    @Override
    public PositionDTO parse(byte[] originalData) {
        // 模拟C品牌终端协议解析逻辑(独立实现,不影响其他解析类)
        PositionDTO position = new PositionDTO();
        position.setLongitude(BigDecimal.valueOf(Double.parseDouble(new String(originalData, 1, 9))));
        position.setLatitude(BigDecimal.valueOf(Double.parseDouble(new String(originalData, 10, 9))));
        position.setSpeed(Integer.parseInt(new String(originalData, 19, 5)));
        position.setTerminalBrand("C品牌");
        return position;
    }
}

4. 系统调用:依赖抽象接口,支持多终端动态切换(无需修改调用逻辑)

// 车联网定位解析调用类(物流系统核心调用层)
// 依赖抽象接口,不依赖具体终端解析实现类
public class TerminalParseClient {
    // 注入解析接口,支持动态切换不同品牌终端解析
    private TerminalParseInterface terminalParser;

    // 构造注入,根据物流客户终端品牌,注入对应解析实现类
    public TerminalParseClient(TerminalParseInterface terminalParser) {
        this.terminalParser = terminalParser;
    }

    // 核心调用方法:解析终端上报的定位数据
    public PositionDTO parseTerminalData(byte[] originalData) {
        return terminalParser.parse(originalData);
    }

    // 测试示例(贴合物流系统实际调用场景)
    public static void main(String[] args) {
        // 1. 初始需求:解析A品牌终端数据(无需修改任何代码)
        TerminalParseClient aClient = new TerminalParseClient(new ATerminalParser());
        PositionDTO aPosition = aClient.parseTerminalData("116.39722239.90755560".getBytes());
        System.out.println("A品牌终端定位:" + aPosition);

        // 2. 扩展需求:解析B品牌终端数据(仅新增实现类,不修改调用逻辑)
        TerminalParseClient bClient = new TerminalParseClient(new BTerminalParser());
        PositionDTO bPosition = bClient.parseTerminalData("0116.402333039.9126660080".getBytes());
        System.out.println("B品牌终端定位:" + bPosition);

        // 3. 扩展需求:解析C品牌终端数据(仅新增实现类,不修改调用逻辑)
        TerminalParseClient cClient = new TerminalParseClient(new CTerminalParser());
        PositionDTO cPosition = cClient.parseTerminalData("116.41244400039.922777000090".getBytes());
        System.out.println("C品牌终端定位:" + cPosition);
    }
}

核心解析接口(TerminalParseInterface)作为车联网模块的稳定部分,始终不修改,定义了所有终端解析的统一规范;初始A品牌解析类线上稳定后,无需任何改动;当物流客户新增B、C品牌定位终端时,仅需新增对应解析实现类,调用端通过依赖注入切换实现即可,完全遵循OCP原则。既保证原有A品牌终端解析功能稳定,又能快速适配新终端需求,避免修改核心代码引入线上风险,贴合物流车联网系统高频迭代的实操场景。

0

评论区