• 下雨天
    2019-12-09
    对拓展开放是为了应对变化(需求),对修改关闭是为了保证已有代码的稳定性;最终结果是为了让系统更有弹性!
     2
     164
  • 辣么大
    2019-12-09
    开闭原则:基于接口或抽象实现“封闭”,基于实现接口或继承实现“开放”(拓展)。

    争哥的第一个例子,AlertHandler为抽象,一般是固定不变的。子类TpsAlertHandler为继承;再看第二个例子,MessageQueue,MessageFormater为接口,具体实现为KafkaMessageQueue和JsonMessageFromatter等。以后替换或者增加其他的AlertHandler和message queue很容易。

    两个例子中的抽象类和接口是固定的(封闭),继承或实现是可扩展的。通过“抽象-具体”体现了开闭原则,增加了软件的可维护性。

    开闭原则具体应用,需要慢慢积累经验。争哥也说了,首先需要有对业务深刻的理解。其次就是学习一些设计原则和模式了。

    补充:
    1、Bertrand Meyer 1988 年提出open-closed principle。
    2、再推荐一篇经典文章 Robert C. Martin 2006年写的The Open-Closed Principle。不方便下载的话,我放到github上了:https://github.com/gdhucoder/Algorithms4/tree/master/designpattern/pdf
    展开
    
     30
  • Paul Shan
    2019-12-09
    基于一定的粒度(例如模块,类,属性等),扩展是平行地增加,修改是变更更细粒度的子集。扩展和修改和具体的粒度有关。不同的粒度下,扩展和修改定义不同。
    我个人以为,扩展的结果是引入了更多的平行结构(例如相似的派生类handler),以及支持这些平行结构的代码(利用多态,在关键的地方使用接口)。这些引入会让代码结构变的扁平一些,但是也更晦涩一些。修改,往往会增加代码的深度(这里指更低粒度的复杂度),例如,文中log例子,修改后,check函数有五个参数,内部的if else逻辑更多。但是,如果从参数以及if作用域的角度,这也可算作扩展。所以,扩展还是修改更本质的区别在于修改发生的粒度和层次。
    通常偏好修改发生在更高的层次上,这要求我们能够用接口和组合把系统合理的切分,做到高内聚和低耦合。高内聚可以让修改发生在更高层次上,替换掉整个低层次实现细节。低耦合,可以让模块之间的调用最小化,可以让高层次的修改最小化。
    支持高层次的平行结构不是免费的,除非有明确的收益(例如文中隔离Kafka实现细节的例子),不然还是让重构等待到需要的那一刻,预测未来的大部分平行结构其实不会被真正用到。
    展开
    
     11
  • 墨雨
    2019-12-09
    听前一部分的时候觉得,哇原来代码还可以这样重构,我以后写代码一定要这么写!看到最后,恩……还是要结合具体业务需求,考虑实现开闭的代价,取舍哪些需要适应开闭原则哪些可以忽略,避免过度设计。整体来说在写代码的时候要多思考一下如何应对短期内可能面临的变化。知识+经验+多思考,看起来编程没有银弹,多思考,多总结。
     1
     8
  • 知行合一
    2019-12-09
    对原有代码改动越少,引入问题的概率越小。在熟悉业务的情况下,多思考一步,为了未来需求预留扩展点,就是有扩展性的代码。但凡事都有个度,扩展性和可读性需要不断权衡,需求在不断变化,系统也在不断演化,通过不断重构来解决过度设计的问题。
    
     8
  • (´田ω田`)
    2019-12-09
    修改老功能,可能需要重新进行各种功能验证、测试,并且如果是接收的遗留代码,更是费时费力;
    但是扩展的话,只需要对自己新增加的功能进行测试,工作量会小很多。
    
     8
  • 古杨
    2019-12-26
    我所在的公司,现在写代码入参全用map,写了两年我都不知道什么叫对象了。感觉自己废了☹️
    
     5
  • 木木
    2019-12-11
    文章写的是真的好,很容易读懂。主要的还是要知道为什么要这么做。感谢老师。
    
     5
  • 李小四
    2019-12-09
    设计模式_16
    # 作业:
    开闭原则核心好处是:
    - 减少因为新增功能而增加的工作量
    - 减少因为新增功能而增加的出错数

    # 感想:
    之前一直有一些执念,想要找到某一原则非黑即白的分割线。比如开闭原则,有两个极端:
    - 任何的“修改”都不能接受
    - 任何不能“扩展”的代码都不能接受
    然后就进入了“走火入魔”的状态,最终陷入对原则的怀疑。

    需求变更对于代码结构影响很大时,要提高对其扩展的权重;读到这里时,我拍了一下大腿,我想,我更加理解开闭原则了。
    展开
    
     4
  • 👽
    2019-12-10
    简单来说,就是尽量减少调用方为了应对而导致的变更。
    就例如本文的例子,为了应对变化需要增加函数的参数的时候。所有调用方都需要改代码。
    而如果依照开闭原则,则增加handler 以及相应修改即可。并不会影响调用方。

    其实个人认为,也是通过了 类似于“中间件”的形式。例如,小明,作为公司代表需要跟各个国外公司的人谈业务。他去跟美国人谈业务,需要学英语;跟日本谈业务,要学日语;跟毛子谈业务,又要学毛子语。
    这时候,的解决方案:
    1,跟各个国家说好,大家都说英语。或者都说汉语。就算再有其他的国家,也让他强制用英语。
    2,小明自己只用汉语。然后谈业务时,带个多语种翻译,去谈业务时把翻译带上。这时候,如果有新的国家需要新的语种,那么就让翻译去掌握更多的语种。
    应对今天的例子,翻译掌握的语种,其实就是handler。小明和各国代表谈业务时,各自都不需要变更自己的接口。只需要对【翻译】进行扩展即可。
    展开
    
     3
  • 土豆哪里挖
    2019-12-09
    什么时候出其他语言的demo呢,不懂java,理解起来太痛苦了

    作者回复: 关注我的github:https://github.com/wangzheng0822

    
     3
  • 梦倚栏杆
    2019-12-09
    关于修改后的报警规则代码实现有两个疑问:
    1. ApiStateInfo class 是充血模型还是贫血模型。
    2.其实各个handler侧重的是不同的方方面面,比如错误次数,超时次数。统一接收ApiStateInfo 和 某一个handler接收具体的类比如:ErrorRequestApiStateInfo, TimeOutStateInfo, 哪种方式好呢?比较依据是什么

    作者回复: 1. 是贫血模型
    2. 不好讲,拆分之后,类增加,维护成本高一些,但职责更单一,更加高内聚、低耦合,扩展性更好些。

    
     3
  • feifei
    2019-12-18
    文中的alter 一步一步的改造,看的眼花缭乱的😂,我就问下,为什么不能直接在原始的Alter 类中,重载一个只有新增业务参数的check 放到的,这样不就最简单,原先开发好也不用动,这样对于Alter 类来说不是对扩展开放,对修改关爱了吗?请教下大神,我这种用重载的思路有啥,不好的地方

    作者回复: 重载可以。但如果报警规则很多的花 类会无限膨胀 可读性比较茶

    
     2
  • wenxueliu
    2019-12-10
    spring 是如何应用开闭原则的,可以参考本文https://blog.csdn.net/wenxueliu/article/details/103467359
     1
     2
  • 辉仔lovers
    2019-12-09
    老师 您好,请教几个问题
    AlertHandler 使用的是抽象类,而不是接口。就是为了让子类去继承构造方法吗?
    这个扩展跟spring中handlerMapping的写法一样,类似于策略模式吧?
    单例模式的时候 使用静态代码块来初始化添加handler 随着类加载一次是不是就不用搞成单例的了?
     static{
            alertRule = new AlertRule(/*.省略参数.*/); //省略一些初始化代码
            notification = new Notification(/*.省略参数.*/); //省略一些初始化代码
             alert = new Alert(alertRule,notification);
             alert.addAlertHandler(new TpsAlertHandler(alertRule, notification));
             alert.addAlertHandler(new ErrorAlertHandler(alertRule, notification));
        }

    我们是不是可以把实现类(不同的handler)放到配置文件中。使用jdk的spi扩展机制。更加灵活一些?
    展开
     3
     2
  • 小晏子
    2019-12-09
    对于课后题,想到2点:
    1,减少出错概率,修改出错的概率比扩展要大
    2,边界的问题,比如用户边界,尽量减少用户侧代码的改动,比如文中alert的事例,check函数本身的修改意味着所有使用的地方都要修改,而使用了开闭原则的代码对于老用户是无须修改的,降低了用户修改的成本。
    
     2
  • 薯片
    2019-12-29
    if分支很多,用handler导致类爆炸怎么处理?

    作者回复: 没看懂你说的 为什么handler类会爆炸呢

     4
     1
  • deepz
    2019-12-10
    老师您好,我把代码实践了后发现, 单例初始化那块可能有点问题。private static final ApplicationContext instance = new ApplicationContext();

        private ApplicationContext() {
            instance.initializeBeans();
        }
    这个“instance”报了空指针。
    展开
     4
     1
  • 哈喽沃德
    2019-12-10
    我想这篇对扩展开放,多修改关闭的文章应该会成为争哥这个设计模式系列最好的文章。很难想象,一个杰出的程序员的语言思维逻辑也是如此清晰。
    
     1
  • L.
    2019-12-09
    学到了,谢谢老师;
    
     1
我们在线,来聊聊吧