`
dongpo
  • 浏览: 5874 次
  • 性别: Icon_minigender_1
  • 来自: 上海
最近访客 更多访客>>
社区版块
存档分类
最新评论

第9章 开放-封闭原则(OCP)

阅读更多

9.1 为什么要用OCP

 

    在系统的生命周期中,需求变化是难免的。我们的系统怎样才能适应这些变化,贴切的符合需求呢?如果系统不能适应这些变化,每次进行一个小的改动,都不得不去修改很多地方,这就说明我们的系统具有僵化性了。使用OCP重构,会使你的系统具有可扩展性,当类似的变化再次发生时,可以很容易通过扩展满足变化。

 

9.2 什么是OCP

    O:Open for extention

    C:Closed for modification

    P:Principle

    开放-封闭原则的意思是,可以通过扩展来满足变化,而不需要修改代码(并不是说一点代码都不修改,这是不可能的)。

 

9.3 怎样用OCP

 

    OCP原则的关键,是抽象。我们常说OOPL有封装性、继承性、多态性等优点,类与类之间要高内聚低耦合,归根结底两个字:抽象!没有良好的抽象,即使使用OOPL,代码的封装性、继承性等特点也是比较差的。敏捷开发中的各种原则,各种设计模式,其实都离不开抽象。

    例如:有一个Printer类,负责按升序打印一组数字。Printer使用AscendingSorter进行升序排序。类图如下:



 
   

    第7章说过敏捷开发人员应该做什么:

 

运用实践去发现问题 


     现在需求发生了变化,需要既能按照升序打印数字,又能按照降序打印数字。按照当前的设计思路,需要这样修改:

     1. 需要添加DescendingSorter,按照降序排序;

     2. 需要在Printer的Print函数中,在每个调用AscendingSorter的地方,判断是要使用AscendingSorter还是DescendingSorter。本来,排序是AscendingSorter和DescendingSorter的事情,Printer不需要关心怎么排序,但现在却需要修改Printer。这是我们发现的问题。

 

运用原则去分析问题 


     我们运用OCP原则来分析这个问题。对于排序方式的变化,不能够通过扩展Sorter来满足,而需要去修改Printer。这违背了OCP原则。

 

 

运用设计模式去解决问题 

      这里我们有两个设计模式可以解决这个问题:

 

 

      1. Strategy模式

       Sorter类有一个纯虚函数Sort。AscendingSorter和DescendingSorter继承了Sorter,并分别实现Sort为升序排序和降序排序。Printer的Print函数接受Sorter类型的参数,调用它的Sort进行排序。这样,Printer不必关心是升序还是降序排序。如果以后在增加新的排序方式,只需要增添一个新的Sorter派生类即可,Printer不需要修改(当然,调用Print的地方要修改,指定排序方式)。

 

 

      2. Template Method模式

 

      Printer类给出了一个解决问题的框架。在Print函数中,调用纯虚函数Sort进行排序,并打印数字。Sort的实现延迟到具体的实现类中。

 


 

 

     现在对于排序方式的变化,我们的设计已经符合OCP原则了。再有排序方式的变化时,我们可以简单地通过扩展Sorter来满足,不需要去修改Printer(当然,调用Print的地方需要指定排序方式,这里是免不了要修改的)。

 

9.4 我们的设计能对所有变化都满足OCP吗

     不幸的是,现在又来了一个变化,本来我们打印数字是这种格式“123456”,现在需要能够按照“123,456”格式打印。当前的设计不能通过扩展满足这个变化,我们需要再次运用OCP和设计模式去重构设计,满足这个变化。

 

    我们是否有办法使我们的设计对所有变化都满足OCP呢?也许有人会在设计时,预测各种可能的需求变化,对这些变化做预先设计,使之对所有变化都满足OCP。但是这种做法往往得不偿失:

    1. 做抽象是需要很大代价的。我们可能没有精力去为每个可能的变化做抽象;

    2. 这种预测往往是错误的。影响用户需求的变化各种各样,很难准确的预测。如果预测的变化没有发生,这些预先的设计会成为系统的累赘,我们需要花费大量精力去维护这些根本用不到的设计。这就是第七章说的不必要的复杂性。

   

    那我们应该怎么办呢?答案是:承受第一颗子弹。对于不确定的变化,不要做预先的设计。等到变化发生时,运用OCP原则进行重构,使之能够通过扩展满足类似的变化。

 

    这个过程可以用下面的流程图表示:

 

 

9.5 结论

    一句话:通过抽象,使设计能够通过扩展满足变化,而不需要修改代码。

  • 大小: 21.1 KB
  • 大小: 35.3 KB
  • 大小: 43.3 KB
  • 大小: 21.4 KB
分享到:
评论

相关推荐

    敏捷软件开发原则、模式与实践 C#版

    第一部分 敏捷开发 第1章 敏捷实践 第2章 极限编程概述 第3章 计划 第4章 测试 第5章 重构 第6章 一次编程实践 第二部分 敏捷设计 第7章 什么是敏捷设计 第8章 SRP:单一职责原则 第9章 OCP:开放-封闭原则 第10章 ...

    敏捷软件开发:原则、模式与实践

    第9章 开放—封闭原则(OCP) 第10章 Liskov替换原则(LSP) 第11章 依赖倒置原则(DIP) 第12章 接口隔离原则(ISP) 第三部分 薪水支付案例研究 第13章 COMMAND模式和ACTIVE OBJECT模式 第14章 TEMPLATE METHOD模式和...

    敏捷软件开发:原则、模式与实践.pdf

    第九章 开放—封闭原则(OCP) 9.1 开放—封闭原则(OCP) 9.2 描述 9.3 关键是抽象 9.4 结论 参考文献 第十章 Liskov替换原则(LSP) 10.1 Liskov替换原则(LSP) 10.2 一个违反LSP的简单例子 10.3 正方形和矩形,...

    敏捷软件开发:原则、模式与实践.pdf 高清

    第九章 开放—封闭原则(OCP) 9.1 开放—封闭原则(OCP) 9.2 描述 9.3 关键是抽象 9.4 结论 参考文献 第十章 Liskov替换原则(LSP) 10.1 Liskov替换原则(LSP) 10.2 一个违反LSP的简单例子 10.3 正方形和矩形,...

    敏捷软件开发:原则、模式与实践.pdf

    第9章 开放—封闭原则(OCP) 第10章 Liskov替换原则(LSP) 第11章 依赖倒置原则(DIP) 第12章 接口隔离原则(ISP) 第三部分 薪水支付案例研究 第13章 COMMAND模式和ACTIVE OBJECT模式 第14章 TEMPLATE METHOD模式和...

    敏捷软件开发.pdf

    第9章 开放—封闭原则(OCP) 9.1 开发—封闭原则(OCP) 9.2 描述 9.3 关键是抽象 9.4 结论 参考文献 第10章 Liskov替换原则(LSP) 10.1 Liskov替换原则(LSP) 10.2 一个违反LSP的简单例子 10.3 正方形...

    亮剑.NET深入体验与实战精要2

    第9章 系统与文件操作 355 9.1 获取系统信息 356 9.1.1 用SystemInformation类获取系统信息 356 9.1.2 用Environment 类获取系统信息 357 9.1.3 通过WMI获取系统信息 358 9.1.4 用RegistryKey 类读取系统信息 361 ...

    亮剑.NET深入体验与实战精要3

    第9章 系统与文件操作 355 9.1 获取系统信息 356 9.1.1 用SystemInformation类获取系统信息 356 9.1.2 用Environment 类获取系统信息 357 9.1.3 通过WMI获取系统信息 358 9.1.4 用RegistryKey 类读取系统信息 361 ...

Global site tag (gtag.js) - Google Analytics