当前位置:首页 > 科技  > 软件

开闭原则,开放的是什么?关闭的又是什么?

来源: 责编: 时间:2024-07-11 17:30:25 610观看
导读真实工作中,你是否是这样操作过:一个需求过来,把原来的代码改一遍,再一个需求过来,又把上一个需求的代码改一遍,很多重复的工作还是在日复一日的重复,有什么好的办法改善吗?相信有经验的小伙伴一定听过:对扩展开放,对修改关闭。

真实工作中,你是否是这样操作过:一个需求过来,把原来的代码改一遍,再一个需求过来,又把上一个需求的代码改一遍,很多重复的工作还是在日复一日的重复,有什么好的办法改善吗?mwI28资讯网——每日最新资讯28at.com

相信有经验的小伙伴一定听过:对扩展开放,对修改关闭。那么,你知道这句话的真正含义吗?今天我们来聊聊开闭原则到底是怎么实现的。mwI28资讯网——每日最新资讯28at.com

什么是开闭原则?

开放封闭原则,英文是:Open–closed principle, 简称OCP,是该原则是 Bertrand Meyer 在1988年提出的,最后被 Robert C. Martin收录到 SOLID原则,开闭原则指出:mwI28资讯网——每日最新资讯28at.com

Software entities should be open for extension, but closed for modification.mwI28资讯网——每日最新资讯28at.com

mwI28资讯网——每日最新资讯28at.com

软件实体应该对扩展开放,对修改关闭。mwI28资讯网——每日最新资讯28at.com

如何实现开闭原则?

"对扩展开放,对修改关闭",如何理解呢?我们先看一个案例,如下图,给出了电商领域库存系统库存变更的简易模型图,库存系统接收外部系统库存变更事件,然后对数据库中的库存进行修改。mwI28资讯网——每日最新资讯28at.com

mwI28资讯网——每日最新资讯28at.com

面对这个业务需求,很多人的代码会写出这样:mwI28资讯网——每日最新资讯28at.com

public class Stock {    public void updateStock(String event){        if("outOfStock" == event){            // todo 出库事件   库存操作        }else if("warehousing" == event){            // todo 入库事件   库存操作        }    }}

这时,新的需求来了:WMS仓储系统内部会产生盘点事件(盘盈/盘亏),这些事件会导致变更库存。于是,代码就会发展成下面这样:mwI28资讯网——每日最新资讯28at.com

public class Stock {    public void updateStock(String event){        if("outOfStock" == event){            // todo 出库事件   库存操作        }else if("warehousing" == event){            // todo 入库事件   库存操作        }else if("panSurplus" == event){            // todo 盘盈事件   库存操作        }else if("loss" == event){            // todo 盘亏事件   库存操作        }    }}

很显然,上述代码的实现,每来一个需求,就需要修改一次代码,在方法中增加一个 else if分支,因此 Stock类就一直处于变更中,不稳定。mwI28资讯网——每日最新资讯28at.com

有没有什么好的办法,可以使得这个代码不用被修改,但是又能够灵活的扩展,满足业务需求呢?mwI28资讯网——每日最新资讯28at.com

这个时候我们就要搬出 java的三大法宝:继承,实现,多态。mwI28资讯网——每日最新资讯28at.com

我们发现整个业务模型是:事件导致库存变更。所以,能不能把事件抽离出来?把它抽象成一个接口,代码如下:mwI28资讯网——每日最新资讯28at.com

public interface Event {    void updateStock(String event);}

每种事件对应一种库存变更,抽象成一个具体的实现类,代码如下:mwI28资讯网——每日最新资讯28at.com

入库事件mwI28资讯网——每日最新资讯28at.com

public class WarehousingEvent implements Event {    public void updateStock(String event){        // 业务逻辑    }}

出库事件mwI28资讯网——每日最新资讯28at.com

public class OutOfStockEvent implements Event {    public void updateStock(String event){        // 业务逻辑    }}

xxx事件mwI28资讯网——每日最新资讯28at.com

public class XXXEvent implements Event {    public void updateStock(String event){        // 业务逻辑    }}

最后,Stock类中 updateStock()库存变更逻辑就可以抽象成下面这样:mwI28资讯网——每日最新资讯28at.com

public class Stock {    public void updateStock(String event){        // 根据事件类型获取真实的实现类        Event event = getEventInstance(event);        // 库存变更操作        event.updateStock();    }}

经过抽象、分离和改造之后,Stock.updateStock()类就稳定下来了,再也不需要每增加一个事件就需要增加一个 else if分支处理,这种抽象带来的好处也是很明显的:每次有新的库存变更事件,只需要增加一个实现类,其他的逻辑都不需要更改,当库存事件无效时只需要把实现类删除即可。mwI28资讯网——每日最新资讯28at.com

开闭原则是常见方式

在Java编程中,遵循开闭原则的常见方式有:使用抽象类和接口、使用策略模式、使用装饰器模式等。mwI28资讯网——每日最新资讯28at.com

1.抽象类和接口

抽象类和接口是 Java中实现 开闭原则的基础,通过定义抽象类或接口,程序员可以在不修改已有代码的情况下,通过继承或实现来扩展新功能。因此,我们强烈建议:面向接口编程。mwI28资讯网——每日最新资讯28at.com

2.策略模式

策略模式是一种行为设计模式,允许在运行时选择算法的实现,策略模式通过定义一系列算法,并将每个算法封装在独立的类中,使得它们可以相互替换。mwI28资讯网——每日最新资讯28at.com

在上面的示例讲解中,其实使用的就是策略模式,当后期有其他的库存事件时,我们只需要添加扩展类,而无需修改现有的代码。mwI28资讯网——每日最新资讯28at.com

3.装饰器模式

装饰器模式是一种结构设计模式,允许向一个对象动态添加行为。装饰器模式通过创建一个装饰器类来包装原始类,从而增加新的功能。示例代码:mwI28资讯网——每日最新资讯28at.com

// 定义一个接口public interface Coffee {    String getDescription();    double getCost();}// 实现接口的具体类public class SimpleCoffee implements Coffee {    @Override    public String getDescription() {        return "Simple Coffee";    }    @Override    public double getCost() {        return 5.0;    }}// 创建装饰器抽象类public abstract class CoffeeDecorator implements Coffee {    protected Coffee decoratedCoffee;    public CoffeeDecorator(Coffee coffee) {        this.decoratedCoffee = coffee;    }    @Override    public String getDescription() {        return decoratedCoffee.getDescription();    }    @Override    public double getCost() {        return decoratedCoffee.getCost();    }}// 实现具体的装饰器类public class MilkDecorator extends CoffeeDecorator {    public MilkDecorator(Coffee coffee) {        super(coffee);    }    @Override    public String getDescription() {        return decoratedCoffee.getDescription() + ", Milk";    }    @Override    public double getCost() {        return decoratedCoffee.getCost() + 1.5;    }}public class SugarDecorator extends CoffeeDecorator {    public SugarDecorator(Coffee coffee) {        super(coffee);    }    @Override    public String getDescription() {        return decoratedCoffee.getDescription() + ", Sugar";    }    @Override    public double getCost() {        return decoratedCoffee.getCost() + 0.5;    }}// 客户端代码public class CoffeeShop {    public static void main(String[] args) {        Coffee coffee = new SimpleCoffee();        System.out.println(coffee.getDescription() + " $" + coffee.getCost());        coffee = new MilkDecorator(coffee);        System.out.println(coffee.getDescription() + " $" + coffee.getCost());        coffee = new SugarDecorator(coffee);        System.out.println(coffee.getDescription() + " $" + coffee.getCost());    }}

在这个示例中,Coffee接口定义了获取描述和成本的方法,SimpleCoffee类实现了这个接口。CoffeeDecorator类是一个抽象类,实现了 Coffee接口,并持有一个 Coffee对象。MilkDecorator和SugarDecorator类分别继承了CoffeeDecorator类,并扩展了其功能。如果我们需要增加新的装饰器,只需要继承 CoffeeDecorator类并实现其方法即可,而无需修改现有的代码。mwI28资讯网——每日最新资讯28at.com

总结

本文通过一个电商中库存实例,演示了开闭原则的整个抽象和实现过程,并给出了开闭原则最常用的 3种实现方式。mwI28资讯网——每日最新资讯28at.com

开闭原则的核心是对扩展开放,对修改关闭,因此,当业务需求一直需要修改同一段代码时,我们就得多思考代码修改的理由是什么?它们之间是不是有一定的共同性?能不能把这些变更点分离出来,通过扩展来实现而不是修改代码?mwI28资讯网——每日最新资讯28at.com

其实在业务开发中还有很多类似的场景,比如:电商系统中的会员系统,需要根据用户不同的等级计算不同的费用;机票系统,根据用户不同的等级(普通,白金用户,黄金用户...)提供不同的售票机制;网关系统中,根据不同的粒度(接口,ip,服务,集群)来实现限流;mwI28资讯网——每日最新资讯28at.com

可能有小伙伴会反驳,业务场景有类似的场景,但是逻辑简单,几个 if-else就搞定了,没有必要去搞这么复杂的设计。mwI28资讯网——每日最新资讯28at.com

本人建议:功夫在平时,功夫在细节。mwI28资讯网——每日最新资讯28at.com

很多人总抱怨业务开发技术成长慢,特别是对于初级程序员,但是大部门人的起点都是业务的 CRUD,如果能在业务 CRUD过程中想办法"挖掘"某些 设计模式,通过这种长期的刻意练习,量变产生质变,慢慢就能领会这些经典设计原则的奥妙,终有一天也能写出让人赏心悦目的代码。mwI28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-100459-0.html开闭原则,开放的是什么?关闭的又是什么?

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: IDC:苹果 Vision Pro 今年销量不超过 50 万台,平价款明年发布

下一篇: Python 数据类型(如整数、浮点数、字符串、列表、元组、字典)

标签:
  • 热门焦点
  • 一加Ace2 Pro真机揭晓 钛空灰配色质感拉满

    一加Ace2 Pro真机揭晓 钛空灰配色质感拉满

    终于,在经过了几波预热之后,一加Ace2 Pro的外观真机图在网上出现了。还是博主数码闲聊站曝光的,这次的外观设计还是延续了一加11的方案,只是细节上有了调整,例如新加入了钛空灰
  • K60至尊版刚预热 一加Ace2 Pro正面硬刚

    K60至尊版刚预热 一加Ace2 Pro正面硬刚

    Redmi这边刚如火如荼的宣传了K60 Ultra的各种技术和硬件配置,作为竞品的一加也坐不住了。一加中国区总裁李杰发布了两条微博,表示在自家的一加Ace2上早就已经采用了和PixelWo
  • CSS单标签实现转转logo

    CSS单标签实现转转logo

    转转品牌升级后更新了全新的Logo,今天我们用纯CSS来实现转转的新Logo,为了有一定的挑战性,这里我们只使用一个标签实现,将最大化的使用CSS能力完成Logo的绘制与动画效果。新logo
  • 得物效率前端微应用推进过程与思考

    得物效率前端微应用推进过程与思考

    一、背景效率工程随着业务的发展,组织规模的扩大,越来越多的企业开始意识到协作效率对于企业团队的重要性,甚至是决定其在某个行业竞争中突围的关键,是企业长久生存的根本。得物
  • 19个 JavaScript 单行代码技巧,让你看起来像个专业人士

    19个 JavaScript 单行代码技巧,让你看起来像个专业人士

    今天这篇文章跟大家分享18个JS单行代码,你只需花几分钟时间,即可帮助您了解一些您可能不知道的 JS 知识,如果您已经知道了,就当作复习一下,古人云,温故而知新嘛。现在,我们就开始今
  • 三分钟白话RocketMQ系列—— 如何发送消息

    三分钟白话RocketMQ系列—— 如何发送消息

    我们知道RocketMQ主要分为消息 生产、存储(消息堆积)、消费 三大块领域。那接下来,我们白话一下,RocketMQ是如何发送消息的,揭秘消息生产全过程。注意,如果白话中不小心提到相关代
  • 新电商三兄弟,“抖快红”成团!

    新电商三兄弟,“抖快红”成团!

    来源:价值研究所作 者:Hernanderz 随着内容电商的概念兴起,抖音、快手、小红书组成的“新电商三兄弟”成为业内一股不可忽视的势力,给阿里、京东、拼多多带去了巨大压
  • ESG的面子与里子

    ESG的面子与里子

    来源 | 光子星球撰文 | 吴坤谚编辑 | 吴先之三伏大幕拉起,各地高温预警不绝,但处于厄尔尼诺大“烤”之下的除了众生,还有各大企业发布的ESG报告。ESG是“环境保
  • OPPO K11评测:旗舰级IMX890加持 2000元档最强影像手机

    OPPO K11评测:旗舰级IMX890加持 2000元档最强影像手机

    【Techweb评测】中端机型用户群体巨大,占了中国目前手机市场的大头,一直以来都是各手机品牌的“必争之地”,其中OPPO K系列机型一直以来都以高品质、
Top