首先,我们看下开放-封闭原则(Open-Closed Principle,简称OCP)的概念:

    • 是指软件实体(类、模块、函数等)应该可以扩展,但是不可修改。
    • 任何新功能(functionality)应该通过添加新class、属性或方法来实现,而不是通过改变现有的代码。

  实现准则:

    • 在继承子类中实现新的功能
    • 允许客户端(clients)访问带有抽象接口的原始基类

为什么要遵循开放-封闭原则?

    • 如果不遵循OCP原则,一个类或函数总是允许添加新的逻辑,那么我们不能不重新测试新的逻辑依赖的整个类
    • 如果不遵循OCP原则很可能破坏单一职责原则,因为随着后期功能的添加,一个类或函数可能完成多项任务,从这个角度来讲,单一职责原则和开放-封闭原则是高度相互依赖的
    • 如果不遵循OCP原则,在现有类上添加功能,后期该类的体积将越来越大,导致维护该类的困难增加

  遵循开放-封闭原则使得设计在面对需求的改变时可以保持相对稳定,因为我们并没有改动原有的代码,而原有代码已被使用和测试过,是稳定的。我们仅仅是在重用原始代码的基础上通过类基础的方式添加了新的代码。

  开放-封闭原则要求在设计之初,尽量让原始基类足够好,写好之后就不要去修改了,如果有新需求,增加一些类就行了,原始基类代码能不动就不动。事实上对原始基类的绝对修改关闭是不可能的,既然不可能完全封闭,设计人员必须对于他设计的模块应该对哪些变化封闭做出选择。他必须先猜测出最有可能发生变化的地方,然后通过构造抽象来隔离那些变化。

  事实上,事先猜测可能的变化也是有困难的,但我们可以在发生小变化时及时想办法应对发生更大变化的可能,也即等到变化发生就考虑创建抽象来隔离以后发生的同类变化。当然,并不是什么时候应对变化都是容易的。我们希望在开发工作展开不久就知道可能发生的变化。查明可能发生变化所等待的时间越长,要创建正确的抽象就越困难,也就是说当变化部分的代码已经在多个地方用到了,再考虑抽象和分离,代价就变大了。

  下面我们通过一个实例来说明开放-封闭原则。

   public class Employee
{
public int ID { get; set; }
public string Name { get; set; } public Employee()
{
}
public Employee(int id, string name)
{
this.ID = id;
this.Name = name;
}
public double CalculateBonus(double salary)
{
return salary * 0.1;
}
}

  以上是一个普通的Employee类,里面封装了ID和Name字段和一个计算奖金的方法,目前运行良好。当我们接到一个新需求,需要区分正式工和合同工并对正式工的奖金计算改用薪水乘以0.2 时,我们应该通过何种方式实现呢?。我们先来看看不用开放-封闭原则如何实现。

 public class Employee
{
public int ID { get; set; }
public string Name { get; set; }
public string EmployeeType { get; set; }
public Employee()
{
}
public Employee(int id, string name,string employeeType)
{
this.ID = id;
this.Name = name;
this.EmployeeType = employeeType;
}
public double CalculateBonus(double salary)
{
if (this.EmployeeType == "Permanent")
return salary * 0.2;
else
return salary * 0.1;
}
}

  从上面代码可以看出,不使用开放-封闭原则时,我们直接在原始Employee类中添加了一个EmployeeType字段,修改了Employee构造器,并在CalculateBonus方法中新增了判断员工类型的逻辑。这种实现方式的缺点见本篇开头部分所述。

  接下来看如何使用开放-封闭原则实现。

 public abstract class Employee
{
public int ID { get; set; }
public string Name { get; set; } public Employee()
{
}
public Employee(int id, string name)
{
this.ID = id;
this.Name = name;
}
public abstract double CalculateBonus(double salary);
}
public class PermanentEmployee : Employee
{
public PermanentEmployee()
{
}
public PermanentEmployee(int id, string name) : base(id, name)
{
}
public override double CalculateBonus(double salary)
{
return salary * 0.2;
}
}
public class TemporaryEmployee : Employee
{
public TemporaryEmployee()
{
}
public TemporaryEmployee(int id, string name) : base(id, name)
{
}
public override double CalculateBonus(double salary)
{
return salary * 0.1;
}
}

  从以上代码可以看到,遵循开放-封闭原则的实现通过后期创建新class的方式添加新功能,并未在原始Employee基类的实现上做改动,仅仅是将可能变化的CalculateBonus方法通过添加abatract关键字来将具体的实现转移到后期添加的继承子类中,这样就将变化隔离出来了。

  开放-封闭原则是面向对象设计的核心所在。遵循这个原则可以带来面向对象技术所声称的巨大好处,也就是可维护、可扩展、可复用、灵活性好。开放人员应该仅对程序中呈现出频繁变化的部分构造抽象,然而,对于应用程序中的每个部分都刻意进行抽象同样不是一个好主意。拒绝不成熟的抽象和抽象本身一样重要。

面向对象SOLID设计原则之Open-Closed原则的更多相关文章

  1. 第2章 面向对象的设计原则(SOLID):6_开闭原则

    6. 开闭原则(Open Closed Principle,OCP) 6.1 定义 (1)一个类应该对扩展开放,对修改关闭.要求通过扩展来实现变化,而且是在不修改己有的代码情况下进行扩展,也不必改动己 ...

  2. 第2章 面向对象的设计原则(SOLID):4_接口隔离原则(ISP)

    4. 接口隔离原则(Interface Segregation Principle,ISP) 4.1 定义 (1)使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口.类间的 ...

  3. SOLID 设计原则

    SOLID 原则基本概念: 程序设计领域, SOLID (单一功能.开闭原则.里氏替换.接口隔离以及依赖反转)是由罗伯特·C·马丁在21世纪早期 引入的记忆术首字母缩略字,指代了面向对象编程和面向对象 ...

  4. 7.10 其他面向对象设计原则1: 开-闭原则OCP

    其他面向对象设计原则1: 开-闭原则OCP  Open-Closed Principle (OCP)5.1 设计变坏的前兆 Signs of Rotting Design  僵硬性 Rigidit ...

  5. 面向对象SOLID原则的自我理解

    S.O.L.I.D 是面向对象设计(OOD)和面向对象编程(OOP)中的几个重要编码原则(Programming Priciple)的首字母缩写.面向对象设计的原则SRP The Single Res ...

  6. 面向对象设计(OOD)七大原则

    这篇文章我会不停的维护它,它将会越来越长,但它是关于我在面向对象中的一些学习的思考心得.希望对自己对各位都能实用处.     开篇前,说明一下写这篇文章的原因.原因是由于设计模式.由于设计模式里的各种 ...

  7. GOF 的23种JAVA常用设计模式总结 03 面向对象七大设计原则

    在软件开发中,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,程序员要尽量根据 7 条原则来开发程序,从而提高软件开发效率.节约软件开发成本和维护成本. 各位代码界的大佬们总结出的七 ...

  8. 《设计模式之美》 <03>面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?

    面向对象 现在,主流的编程范式或者是编程风格有三种,它们分别是面向过程.面向对象和函数式编程.面向对象这种编程风格又是这其中最主流的.现在比较流行的编程语言大部分都是面向对象编程语言.大部分项目也都是 ...

  9. Java 面向对象的设计原则

    一. 1.面向对象思想的核心: 封装.继承.多态.   2.面向对象编程的追求: 高内聚低耦合的解决方案: 代码的模块化设计: 3.什么是设计模式: 针对反复出现的问题的经典解决方案,是对特定条件下( ...

随机推荐

  1. python学习-day 2

    1.执行Python脚本的两种方式1)调用解释器 Python +绝对路径+文件名称2)调用解释器 Python +相对路径+文件名称 2.简述位.字节的关系8位为1个字节 3.简述ASCII.uni ...

  2. 一个简单的MVVM雏形

    这是@尚春实现的MVVM,使用定时器轮询,只支持{{}}与input.value的修改. 这只能算是一个玩具,真正的MVVM需要有更复杂的扫描机制,JS解析器,双向绑定链什么的. <!DOCTY ...

  3. VS Code设置中文和配置Python环境

    前言: Visual Studio Code(以下简称VSCode)是一个轻量且强大的代码编辑器,支持Windows,OS X和Linux.内置JavaScript.TypeScript和Node.j ...

  4. hadoop之HDFS学习笔记(二)

    主要内容:hdfs的核心工作原理:namenode元数据管理机制,checkpoint机制:数据上传下载流程 1.hdfs的核心工作原理 1.1.namenode元数据管理要点 1.什么是元数据? h ...

  5. ColorMask

    [ColorMask] When using multiple render target (MRT) rendering, it is possible to set up different co ...

  6. 非root用户使用1024以下端口

      如果你有一个最新的内核,确实有可能使用它作为非root用户启动服务,但绑定低端口.最简单有效的办法是: #setcap 'cap_net_bind_service=+ep' /path/to/pr ...

  7. sqlserver datetime的bug?

    sqlserver datetime 的毫秒的个位似乎存在bug,只有0.3.7这三个值,比如: 2018-01-20 23:59:59:999会变成2018-01-21 00:00:00.000 2 ...

  8. Multithreading in C

    Multithreading in C, POSIX(可移植操作系统接口Portable Operating System Interface X ) style Multithreading - A ...

  9. select获取下拉框的值 下拉框默认选中

    本文主要介绍select下拉框的相关方法. 1.通过id获取下拉框的value和文本值 例如:  <select class="form-control" id=" ...

  10. 怎样知道自己机器的出口网关IP(即外部IP)

    方法一: 在浏览器中输入 :  http://www.ip138.com/ 可以查到外部IP地址及地理位置 方法二: Just put in  “where is my ip” in google t ...