【设计模式】之开闭原则(OCP)
开闭原则是面向对象设计的一个重要原则,其定义如下:
开闭原则(Open-Closed Principle, OCP):一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。
在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。那么势必会对软件的开发带来额外的风险和成本, 这是OCP原则要规范设计的一个主要原因,所有的设计原则都是对软件的开发,设计以及维护带来好处的,OCP也不例外。
OCP原则是面形对象软件设计的基本原则,其旨在指导如何构建稳定的,灵活的,易于维护的软件。其实这个原则也是我们面向对象设计的一个终极要求,更是一个引导,在软件设计过程中要达到OCP原则其实你需要很好的遵守其他的设计原则,换句话说如果其它的设计原则都达到了那么OCP原则自然就符合了,也就是说OCP原则是其他原则综合使用的一个考量,一个检验。
假如我们要设计一个叫做动物的类(Animal)在这个类中我们有一个方法叫Sound, Sound 方法主要用于发出动物的叫声,通常我们的设计代码如下:
public class Animal
{
public void Sound(string animal)
{
switch (animal)
{
case "dog":
System.Console.WriteLine("woof woof woof...");
break;
case "cat":
Console.WriteLine("miaow miaow miaow...");
break;
}
}
}
客户端的调用代码如下:
class Program
{
static void Main(string[] args)
{
Animal animal=new Animal();
animal.Sound("dog");
Console.ReadKey();
}
}
调用返回的结果:

这样看起来似乎很完美,如果想要什么动物发生客户端就传入该动物的名字然后调用Sound方法就可以了。 客户今天只养了两种动物,狗和猫,如果有一天他在养一头羊,他想听到羊的叫声怎么办呢? 直接的想法是在Sound的方法中加一个case子句,写上羊的叫声如下:
public class Animal
{
public void Sound(string animal)
{
switch (animal)
{
case "dog":
System.Console.WriteLine("woof woof woof...");
break;
case "cat":
Console.WriteLine("miaow miaow miaow...");
break;
case "sheep":
Console.WriteLine("mee-mee mee-mee mee-mee...");
break;
}
}
}
客户端调用如下:
static void Main(string[] args)
{
Animal animal=new Animal();
animal.Sound("sheep");
Console.ReadKey();
}
输出:

这看起来似乎是很完美,但是我们回过头想一下,好像哪里不对劲,如果后面客户需要加更多的动物怎么办呢?,是不是这个case要写很长很长,Sound方法要每次都要修改,每次都要全部编译整个工程还要重新部署所有的代码,这中间的风险很大,很容易出现操作上的失误,或者代码修改出现bug,要命的是每次都要把整个代码重新测试一遍,给升级带来了很多的工作量,以及潜在的风险。其实再回头看看,我们这个设计是违反OCP原则的, OCP告诉我们对“修改关闭,对扩展开放“,很显然我们这里修改了代码。同时也违背了SRP原则“一个类或方法只负责干一件事情“,显然Sound 犯法的职责太多。那么我们有没有办法来重构代码,让其遵守这些原则,每次修改该最少的代码即尽可能的减少工作量呢? 答案是肯定的。
我们抽取一个接口叫IAnimal:
public interface IAnimal
{
void Sound();
}
再分别定义三个类 Dog, Cate 和Sheep 并继承IAnimal 接口:
public class Dog : IAnimal
{
public void Sound()
{
Console.WriteLine("woof woof woof...");
}
} public class Cat : IAnimal
{
public void Sound()
{
Console.WriteLine("miaow miaow miaow...");
}
} public class Sheep:IAnimal
{
public void Sound()
{
Console.WriteLine("mee-mee mee-mee mee-mee...");
}
}
客户端如果想听到狗的叫声的代码调用如下:
static void Main(string[] args)
{
IAnimal animal=new Dog();
animal.Sound(); Console.ReadKey();
}
输出:

这下是不是比开始好了很多,并且他还很好的满足了单一职责原则(SRP),每个类只负责一种动物发出的声音,职责单一了, 但是我们发现如果我们想听到猫的叫声还是要修改Main方法中的调用代码, 还要编译部署,风险还是有点大,工作量还是有点大,那么我们能不能不修改代码只需要改个配置来达到修改Main方法调用的结果呢?这样每次就不用编译只需要修改一下配置就好了呢? 答案是肯定的, 我们利用反射加配置就可以了。 这里我们先加一个工具类用于反射。代码如下:
public class ObjectBuildFactory<T>
{
public static T Instance(string key)
{
Type obj = Type.GetType(key);
if (obj == null) return default(T); T factory = (T)obj.Assembly.CreateInstance(obj.FullName); return factory;
}
}
写配置文件如下:
<appSettings>
<add key="Animal" value="ConsoleApp1.Dog"/>
</appSettings>
调用并通过反射创建对象,调用Dog的叫声如下:
static void Main(string[] args)
{
string key = ConfigurationManager.AppSettings["Animal"]; IAnimal animal = ObjectBuildFactory<IAnimal>.Instance(key); animal.Sound(); Console.ReadKey();
}
输出:

好了如果希望听到羊的叫声,只需要改一下我们的配置文件就可以了:
<appSettings>
<add key="Animal" value="ConsoleApp1.Sheep"/>
</appSettings>

其它的代码不需要任何修改直接运行输出如下:

好了这回满足OCP了。
那么好了如果客户期望在增加一种动物,我们应该怎么办呢? 这下就变得非常简单了,我们需要如下两个步骤来完成:
1.增加一个类继承IAnimal接口并实现Sound方法。
2.修改配置文件。
例如我们增加一个动物鸭子代码如下:
public class Duck : IAnimal
{
public void Sound()
{
Console.WriteLine("quack quack quack...");
}
}
配置:
<appSettings>
<add key="Animal" value="ConsoleApp1.Duck"/>
</appSettings>
输出:

很简单达到了我们的设计目的。
总结:开闭原则(OCP)是我们在面向对象设计过程中必须注入潜意识的一个原则,在设计的过程中要时时刻刻,如影随形,一旦发现违背就要立即重构,不然代码就会变的越来越不易于理解,越来越不易于维护了。
【设计模式】之开闭原则(OCP)的更多相关文章
- C#软件设计——小话设计模式原则之:开闭原则OCP
前言:这篇继续来看看开闭原则.废话少说,直接入正题. 软件设计原则系列文章索引 C#软件设计——小话设计模式原则之:依赖倒置原则DIP C#软件设计——小话设计模式原则之:单一职责原则SRP C#软件 ...
- 设计原则:开闭原则(OCP)
1.什么是开闭原则 开闭原则的英文是Open Closed Principle,缩写就是OCP.其定义如下: 软件实体(模块.类.方法等)应该"对扩展开放.对修改关闭". 从定义上 ...
- 7.10 其他面向对象设计原则1: 开-闭原则OCP
其他面向对象设计原则1: 开-闭原则OCP Open-Closed Principle (OCP)5.1 设计变坏的前兆 Signs of Rotting Design 僵硬性 Rigidit ...
- 深入理解JavaScript系列(7):S.O.L.I.D五大原则之开闭原则OCP
前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第2篇,开闭原则OCP(The Open/Closed Principle ). 开闭原则的描述是: Software ...
- 聊一聊开闭原则(OCP).
目录 简述 最早提出(梅耶开闭原则) 重新定义(多态开闭原则) 深入探讨 OCP的两个特点 对外扩展开放(Open for extension) 对内修改关闭 抽象 关闭修改.对外扩展? 简述 在面向 ...
- 【面向对象设计原则】之开闭原则(OCP)
开闭原则是面向对象设计的一个重要原则,其定义如下: 开闭原则(Open-Closed Principle, OCP):一个软件实体应当对扩展开放,对修改关闭.即软件实体应尽量在不修改原有代码的情况下进 ...
- JavaScript 开闭原则OCP
代码: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3. ...
- 设计模式笔记:开闭原则(OCP,The Open-Closed Principle)
1. 开闭原则概述 开闭原则(OCP,The Open-Closed Principle)两个主要特征: (1)对扩展开放(open for extension):模块的行为的可以扩展的,当应用的需求 ...
- 设计模式原则(6)--Open-Closed Principle(OCP)--开闭原则
作者QQ:1095737364 QQ群:123300273 欢迎加入! 1.定义: 一个软件实体应当对扩展开放,对修改关闭.即软件实体应尽量在不修改原有代码的情况下进行扩展. 2.使用场 ...
随机推荐
- JVM年轻代、年老代、永久代
年轻代: HotSpot JVM把年轻代分为了三部分:1个Eden区和2个Survivor区(分别叫From和To),每次新创建对象时,都会分配到Eden区,当Eden区没有足够的空间进行分配时,虚拟 ...
- 1132: 零起点学算法39——多组测试数据(a+b)
1132: 零起点学算法39--多组测试数据(a+b) Time Limit: 1 Sec Memory Limit: 64 MB 64bit IO Format: %lldSubmitted: ...
- js 形参和实参---2017-04-11
一.定义 1.实参(argument): 全称为"实际参数"是在调用时传递给函数的参数. 实参可以是常量.变量.表达式.函数等, 无论实参是何种类型的量,在进行函数调用时, ...
- maven私服搭建nexus介绍(二)
1.各个仓库介绍 Hosted:宿主仓库 主要放本公司开发的SNAPSHOTS测试版本,RELEASES正式发行版.合作公司第三方的jar包. Proxy:代理仓库 代理中央仓库:代理Apache下测 ...
- 记因PHP的内存溢出导致的事故之解决
如果对您有用记得关注,更多干货. 今天上午刚到公司,就有同事在公司群里反映某个计划任务出现问题了.我就怀着刨根问底的心,去查看了log.发现挺有意思的一个问题,PHP内存溢出导致脚本执行失败.那就一起 ...
- Java程序初始化的顺序
Java程序初始化的顺序 java程序初始化工作可以在许多不同的代码块中来完成(例如:静态代码块.构造函数等),他们执行的顺序如下: 父类静态变量 父类静态代码块 子类静态变量 子类静态代码块 父类非 ...
- NuGet(Nuget Packages)
Nuget是一个.NET平台下的开源的项目,它是Visual Studio的扩展.在使用Visual Studio开发基于.NET Framework的应用时,Nuget能把在项目中添加.移除和更新引 ...
- UEditor使用------图片上传与springMVC集成 完整实例
UEditor是一个很强大的在线编辑软件 ,首先讲一下 基本的配置使用 ,如果已经会的同学可以直接跳过此节 ,今天篇文章重点说图片上传; 一 富文本的初始化使用: 1 首先将UEditor从官网下载 ...
- Unity C#集合
集合分为两种:非泛型集合,泛型集合. 非泛型集合需要引入:System.Collections命名空间,其命名空间下的类有: ArrayList表示大小根据需要动态增加的对象数组. Hashtable ...
- C语言学习第七章
今天开始学习指针,指针在C语言中具有很重要的地位,按照老师所说,学C学不好指针跟没学一样,可见指针在C语言中的重要地位.废话不多说,首先我们先要知道什么是指针. 指针:指针是一个变量,它存储另一个对象 ...