面向对象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.什么是设计模式: 针对反复出现的问题的经典解决方案,是对特定条件下( ...
随机推荐
- linux中grep工具
正则表达式 以前我们用grep在一个文件中找出包含某些字符串的行,比如在头文件中找出一个宏定义.其实grep还可以找出符合某个模式(Pattern)的一类字符串.例如找出所有符合xxxxx@xxxx. ...
- linux性能调优
1-1.0 关于ulimit linux对每个用户,系统限制其最大进程数.为提高性能,可根据设备资源情况,设置各linux用户最大进程数. [Qrui@root ~]#ulimit -a 用来显示当 ...
- 用dwz时, 由于粗心产生的一些问题(记录方便自己查阅)
在打开"添加" 或 "修改" , 用dialog弹出时 , 点击提交的时候, dialog 不能关闭, 也不能刷新 解决办法: 注意form标签, onsubm ...
- XMLHttpRequest.status 返回服务器状态码
XMLHttpRequest.status: 1xx-信息提示 这些状态代码表示临时的响应.客户端在收到常规响应之前,应准备接收一个或多个1xx响应. 100-继续. 101-切换协议. 2xx-成功 ...
- js常用代码整理
引用js <script type="text/javascript" src="js/jquery-1.11.2.min.js"></scr ...
- Python运维开发基础03-语法基础
上节作业回顾(讲解+温习60分钟) #!/usr/bin/env python3 # -*- coding:utf-8 -*- # author:Mr.chen #只用变量和字符串+循环实现" ...
- testng参数化(提供测试数据)
testng提供测试数据的两个注释:@DataProvide和@Parameter 一.通过testng.xml中设置参数 (实际上testng.xml只是一个名字,可以起任何一个名字,只要是.x ...
- 118. Pascal's Triangle (Array)
Given numRows, generate the first numRows of Pascal's triangle. For example, given numRows = 5, Retu ...
- ubuntu 12.04安装jdk 8
转载:http://www.itnose.net/detail/6196130.html Ubuntu12.4安装jdk1.8 1.要安装的jdk,我把它拷在了共享文件夹里面. (用优盘拷也可以 ...
- centos7之saltstack使用手册
武sir的图镇楼: salt是一个异构平台基础设置管理工具(虽然我们通常只用在Linux上),使用轻量级的通讯器ZMQ,用Python写成的批量管理工具,完全开源,遵守Apache2协议,与Puppe ...