I walk very slowly, but I never walk backwards
设计模式原则 - 开闭原则
寂然
大家好,我是寂然~,本节课呢,我来给大家介绍设计模式原则之开闭原则,话不多说,我们直接进入正题,老规矩,首先带大家了解一下开闭原则的官方定义,并作一个解释,然后我们通过案例代码来具体分析
官方定义
开闭原则( Open Close Principle ),又称为OCP原则,他的官方定义如下:
Software entities like classes,modules and functions should be open for extension but closed for modifications.
一个软件实体如类,模块和函数应该对扩展开放,对修改关闭
基本介绍
对扩展开放指的是提供方,对修改关闭指的是调用方
一个软件产品只要在生命周期内,都会发生变化,即然变化是一个事实,我们就应该在设计时尽量适应这些变化,以提高项目的稳定性和灵活性,真正实现“拥抱变化”,开闭原则告诉我们应尽量通过扩展软件实体的行为来实现变化,而不是通过修改现有代码来完成变化,它是针对软件的未来事件而制定的对现行开发设计进行约束的一个原则
案例演示 - 绘图工具
OK,大家简单了解过开闭原则后,我们来看如下一段代码,帮助大家更深入的理解
有一个绘图工具类,接收 Shape 对象,然后根据不同的 type,来绘制不同的图形,具体代码如下图所示
//这是一个用于绘图的类 [使用方]
class GraphicEditor {
//接收 Shape 对象,然后根据不同的 type,来绘制不同的图形
public void drawShape(Shape s) {
if (s.m_type == 1) {
drawRectangle(s);
} else if (s.m_type == 2) {
drawCircle(s);
}
}
//绘制矩形
public void drawRectangle(Shape r) {
System.out.println(" 正在绘制矩形中---绘制成功 ");
}
//绘制圆形
public void drawCircle(Shape r) {
System.out.println(" 正在绘制圆形中---绘制成功");
}
}
//Shape 类,基类
class Shape {
int m_type;
}
class Rectangle extends Shape {
Rectangle() {
super.m_type = 1;
}
}
class Circle extends Shape {
Circle() {
super.m_type = 2;
}
}
上面这段代码首先是可以完成需求的,OK,那现在我们新增绘制三角形的功能,大家想一下实现思路
首先我们要新增 Triangle 类,然后新增绘制三角形的方法, drawShap()方法里添加判断 并调用就Ok,新增代码示例如下图所示
//新增绘制三角形的子类
class Triangle extends Shape {
Triangle() {
super.m_type = 3;
}
}
//类 GraphicEditor 中·添加判断,type=3,绘制三角形
if (s.m_type == 1) {
drawRectangle(s);
} else if (s.m_type == 2) {
drawCircle(s);
} else if (s.m_type == 3) {
drawTriangle(s);
}
// 类 GraphicEditor 中新增绘制三角形的方法
public void drawTriangle(Shape r) {
System.out.println(" 正在绘制三角形中---绘制成功 ");
}
案例分析
Ok,上面的代码是比较好理解的,但是大家想一下,针对上面的业务逻辑来说,类 GraphicEditor 扮演的其实是调用方的角色,但是出现了新增的需求后,我们对于 类 GraphicEditor 进行了一系列改动,这显然违背了ocp原则
因为我们上面提到,ocp原则对调用方的修改是关闭的,那大家想一下,新增绘制三角形的功能,我们还可以怎么样实现呢?下面我们在满足ocp原则的情况下,对上面的代码进行重构
解决方案
这时有的小伙伴说了,可以把创建 Shape 类做成抽象类,并提供一个抽象的 draw 方法,让子类去实现即可,这样我们有新的图形种类时,只需要让新的图形类继承 Shape,并实现 draw 方法即可,使用方的代码就不需要修 改了,这样既满足了业务逻辑,又符合开闭原则,具体代码示例如下
//这是一个用于绘图的类 [使用方]
class GraphicEditor {
//接收 Shape 对象,然后根据不同的 type,来绘制不同的图形
public void drawShape(Shape s) {
s.draw();
}
}
//Shape变为抽象类,并提供抽象draw方法
abstract class Shape {
int m_type;
public abstract void draw();//抽象方法
}
class Rectangle extends Shape {
Rectangle() {
super.m_type = 1;
}
@Override
public void draw() {
System.out.println(" 正在绘制矩形中---绘制成功 ");
}
}
class Circle extends Shape {
Circle() {
super.m_type = 2;
}
@Override
public void draw() {
System.out.println(" 正在绘制圆形中---绘制成功 ");
}
}
OK,那这样的话,同样还是上面的需求,我们新增绘制三角形的功能,那我们只需要新建一个 类Shape 的子类,实现draw() 方法即可,具体代码示例如下
class Triangle extends Shape {
Triangle() {
super.m_type = 3;
}
@Override
public void draw() {
System.out.println(" 正在绘制三角形中---绘制成功 ");
}
}
案例总结
通过上面的重构,我们可以看到,当我们新增绘制三角形的功能,不需要修改调用方,即本案例中扮演调用方角色的类 GraphicEditor 不需要修改,而是扩展了提供方的类,来完成需求,就遵守了开闭原则,换句话说,开闭原则要求我们这样做,即 针对提供方,对扩展开放,针对调用方,对修改关闭
注意事项
-
开闭原则是编程中最基础,最重要的设计原则,只要是面向对象编程,在开发时都会强调开闭原则
-
开闭原则是最基础的设计原则,其它的五个设计原则都是开闭原则的具体形态,也就是说其它的五个设计原则是指导设计的工具和方法,而开闭原则才是其精神领袖。依照java语言的称谓,开闭原则是抽象类,而其它的五个原则是具体的实现类
-
开闭原则可以提高复用性和维护性
下节预告
OK,下一节,我们正式进入设计模式原则之迪 米特法则的学习,我会为大家用多个案例分析,来解读设计模式原则之迪米特法则,以及它的注意事项和细节,最后,希望大家在学习的过程中,能够感觉到设计模式的有趣之处,高效而愉快的学习,那我们下期见~