面向对象SOLID设计原则之Open-Closed原则
首先,我们看下开放-封闭原则(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原则的更多相关文章
- 第2章 面向对象的设计原则(SOLID):6_开闭原则
6. 开闭原则(Open Closed Principle,OCP) 6.1 定义 (1)一个类应该对扩展开放,对修改关闭.要求通过扩展来实现变化,而且是在不修改己有的代码情况下进行扩展,也不必改动己 ...
- 第2章 面向对象的设计原则(SOLID):4_接口隔离原则(ISP)
4. 接口隔离原则(Interface Segregation Principle,ISP) 4.1 定义 (1)使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口.类间的 ...
- SOLID 设计原则
SOLID 原则基本概念: 程序设计领域, SOLID (单一功能.开闭原则.里氏替换.接口隔离以及依赖反转)是由罗伯特·C·马丁在21世纪早期 引入的记忆术首字母缩略字,指代了面向对象编程和面向对象 ...
- 7.10 其他面向对象设计原则1: 开-闭原则OCP
其他面向对象设计原则1: 开-闭原则OCP Open-Closed Principle (OCP)5.1 设计变坏的前兆 Signs of Rotting Design 僵硬性 Rigidit ...
- 面向对象SOLID原则的自我理解
S.O.L.I.D 是面向对象设计(OOD)和面向对象编程(OOP)中的几个重要编码原则(Programming Priciple)的首字母缩写.面向对象设计的原则SRP The Single Res ...
- 面向对象设计(OOD)七大原则
这篇文章我会不停的维护它,它将会越来越长,但它是关于我在面向对象中的一些学习的思考心得.希望对自己对各位都能实用处. 开篇前,说明一下写这篇文章的原因.原因是由于设计模式.由于设计模式里的各种 ...
- GOF 的23种JAVA常用设计模式总结 03 面向对象七大设计原则
在软件开发中,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,程序员要尽量根据 7 条原则来开发程序,从而提高软件开发效率.节约软件开发成本和维护成本. 各位代码界的大佬们总结出的七 ...
- 《设计模式之美》 <03>面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?
面向对象 现在,主流的编程范式或者是编程风格有三种,它们分别是面向过程.面向对象和函数式编程.面向对象这种编程风格又是这其中最主流的.现在比较流行的编程语言大部分都是面向对象编程语言.大部分项目也都是 ...
- Java 面向对象的设计原则
一. 1.面向对象思想的核心: 封装.继承.多态. 2.面向对象编程的追求: 高内聚低耦合的解决方案: 代码的模块化设计: 3.什么是设计模式: 针对反复出现的问题的经典解决方案,是对特定条件下( ...
随机推荐
- 我的solr学习笔记--solr admin 页面 检索调试
前言 Solr/Lucene是一个全文检索引擎,全文引擎和SQL引擎所不同的是强调部分相关度高的内容返回,而不是所有内容返回,所以部分内容包含在索引库中却无法命中是正常现象. 多数情况下我们 ...
- solr常见异常解决办法
科普篇 来自百度百科:Solr简介Solr是一个基于Lucene的Java搜索引擎服务器.Solr 提供了层面搜索.命中醒目显示并且支持多种输出格式(包括 XML/XSLT 和 JSON 格式).它易 ...
- 7_python之路之python计算器
7_python之路之python计算器 1.程序说明:Readme.cmd 1.程序文件及说明: calculator.py 2.python版本:python-3.5.3 3.程序使用:pytho ...
- 剖析tcp与udp及应用场景协议方案选择
什么是TCP和UDP TCP和UDP是TCP/IP协议中的两个传输层协议,它们使用IP路由功能把数据包发送到目的地,从而为应用程序及应用层协议(包 括:HTTP.SMTP.SNMP.FTP和Telne ...
- 「小程序JAVA实战」小程序首页视频(49)
转自:https://idig8.com/2018/09/21/xiaochengxujavashizhanxiaochengxushouyeshipin48/ 视频显示的内容是视频的截图,用户的头像 ...
- 最全互联网Linux工作规划!
首先祝贺你选择学习Linux,你可能即将踏上Linux的工作之旅,出发之前,让我带你来看一看关于Linux和Linux运维的一切. Linux因其高效率.易于裁剪.应用广等优势,成为了当今中高端服务器 ...
- AABB和平面的相交性检测
[AABB和平面的相交性检测]
- 130. Surrounded Regions (Graph; DFS)
Given a 2D board containing 'X' and 'O', capture all regions surrounded by 'X'. A region is captured ...
- 今天无意中发现的WWW.threadPriority
WWW.threadPriority Description Priority of AssetBundle decompression thread. You can control dec ...
- 解题报告-683. K Empty Slots
There is a garden with N slots. In each slot, there is a flower. The N flowers will bloom one by one ...