(1) 定义

一个软件实体(类、模块、函数等),对于扩展是开放的,对于更改是封闭的。

对于扩展是开放的:这意味着模块的行为是可以扩展的。当应用的需求发生改变时,我们可以对模块进行扩展,比如增加新的类或接口,使其具有满足那些改变的新行为。

对于更改是关闭的:对模块进行扩展时,不必改动模块的源代码,即模块中原有的类或接口不应该改变。

——《敏捷软件开发:原则、模式与实践》

如果程序中的一处改动,会产生连锁反应,导致其他一系列模块的改动,那么设计就具有僵化性的臭味。此时,我们就应该根据OCP原则对程序重构,如果正确地应用OCP,这样以后再进行同样的改动时,就只需要增加新的代码,而不必改动已经正常运行的代码。

注意:编程中遵循的其他设计原则(单一职责原则、里氏替换原则、依赖倒置原则、接口隔离原则等),以及使用设计模式(23种设计模式)的目的就是遵循开闭原则,使程序对扩展开放,对修改关闭,避免程序中某一处的改动对已经正在运行的部分产生影响,提高程序的可扩展性和可维护性。

(2) 怎样在不改动程序源代码的情况下去更改它的行为呢?(如何让程序符合OCP原则呢?)

用抽象构建框架,用实现扩展细节。

(3) 开闭原则案例

用一个经典的画图形的程序,来简单描述何为OCP原则。

  • 违反OCP的情况:
/** * 图形枚举类 */public enum ShapeType {    /**     * 圆形     */    Circle,    /**     * 正方形     */    Square}/** * 图形 */public class Shape {    ShapeType itsType;}/** * 圆形 */public class Circle extends Shape{    public Circle(){        super.itsType = ShapeType.Circle;    }    public void draw(){        System.out.println("绘制圆形");    }}/** * 正方形 */public class Square extends Shape{    public Square(){        super.itsType = ShapeType.Square;    }    public void draw(){        System.out.println("绘制正方形");    }}/** * 图形编辑器 */public class GraphicEditor {    public void drawShape(Shape shape){        if (shape.itsType == ShapeType.Circle){            drawCircle(new Circle());        }else if(shape.itsType == ShapeType.Square){            drawSquare(new Square());        }    }    public void drawCircle(Circle circle){        circle.draw();    }    public void drawSquare(Square square){        square.draw();    }}/** * 客户端 */public class Client {    public static void main(String[] args) {        GraphicEditor graphicEditor = new GraphicEditor();        // 绘制圆形        graphicEditor.drawShape(new Circle());        // 绘制正方形        graphicEditor.drawShape(new Square());    }}

程序运行结果:

绘制圆形

绘制正方形

UML类图如下:

图1

GraphicEditor不符合OCP原则,原因有两点:

1、它对于新的形状的添加不是封闭的,如果我们希望图形编辑器可以画三角形,就必须更改GraphicEditor这个类,在其drawShape函数中加一个if分支用于画三角形

2、当我们增加一种图形形状时,必须要在ShapeType枚举中添加一个成员,由于所有图形形状都依赖于ShapeType枚举,所以我们必须重新编译所有的图形形状类,并且也必须要重新编译所有依赖于Shape类的模块。

由此可见,增加一种新的形状类型带来的影响是巨大的,对程序的改动是巨大的。

当增加一个三角形形状,代码如下所示。我们需要在ShapeType枚举中增加一种图形形状类型,同时增加一个三角形类,最重要的是我们需要修改GraphicEditor类的drawShape函数,给其增加一个if分支表示三角形,我们改动了原有的类,这违反了开闭原则。

/** * 图形枚举类 */public enum ShapeType {    /**     * 圆形     */    Circle,    /**     * 正方形     */    Square,    /**     * 三角形     */    Triangle}/** * 三角形 */public class Triangle extends Shape{    public Triangle(){        super.itsType = ShapeType.Triangle;    }    public void draw(){        System.out.println("绘制三角形");    }}/** * 图形编辑器 */public class GraphicEditor {    public void drawShape(Shape shape){        if (shape.itsType == ShapeType.Circle){            drawCircle(new Circle());        }else if(shape.itsType == ShapeType.Square){            drawSquare(new Square());        }else if(shape.itsType == ShapeType.Triangle){            drawTriangle(new Triangle());        }    }    public void drawCircle(Circle circle){        circle.draw();    }    public void drawSquare(Square square){        square.draw();    }    public void drawTriangle(Triangle triangle){        triangle.draw();    }}/** * 客户端 */public class Client {    public static void main(String[] args) {        GraphicEditor graphicEditor = new GraphicEditor();        // 绘制圆形        graphicEditor.drawShape(new Circle());        // 绘制正方形        graphicEditor.drawShape(new Square());        // 绘制三角形        graphicEditor.drawShape(new Triangle());    }}

程序运行结果:

绘制圆形

绘制正方形

绘制三角形

  • 遵循OCP的情况:

把Shape类做成一个抽象类,并提供一个抽象的draw方法,让派生类去实现draw方法,GraphicEditor的drawShape方法使用Shape抽象类作为入参,调用Shape的draw方法。根据多态(面向对象三个基本特征之一)的向上转换(向上转换思想这个概念,来自《java编程思想》)思想 ,GraphicEditor的drawShape方法会根据传进来的不同的Shape抽象类的派生类,调用不同派生类的draw方法。

如果我们希望增加一个图形形状,只需增加一个Shape的派生类,让其实现draw方法即可,无需对GraphicEditor类做改动。

上述就是应用到23种设计模式中的策略模式,目的是使程序满足OCP原则,达到对扩展开放,对修改关闭的目的。

/** * 图形形状抽象类 */public abstract class Shape {    public abstract void draw();// 抽象方法}/** * 图形形状:圆形 */public class Circle extends Shape {    @Override    public void draw() {        System.out.println("绘制圆形");    }}/** * 图形形状:正方形 */public class Square extends Shape {    @Override    public void draw() {        System.out.println("绘制正方形");    }}/** * 图形编辑器 */public class GraphicEditor {    /**     * 向上转换:传入进来的Circle、Square等等是Shape的派生类,会向上      * 转换成Shape。     * @param shape     */    public void drawShape(Shape shape){        shape.draw();    }}/** * 客户端 */public class Client {    public static void main(String[] args) {        GraphicEditor graphicEditor = new GraphicEditor();        graphicEditor.drawShape(new Circle());// 绘制圆形        graphicEditor.drawShape(new Square());// 绘制正方形    }}

程序运行结果:

绘制圆形

绘制正方形

当我们增加一个三角形形状,代码如下所示。我们只需要增加一个Triangle类,让其继承Shape抽象类,并实现抽象draw方法,然后在Client增加一行代码即可,GraphicEditor类并不需要改动,此时我们就做到了对扩展开放(增加一个Triangle类),对修改关闭(原有的GraphicEditor类并不需要改动)。

/** * 图形形状:三角形 */public class Triangle extends Shape{    @Override    public void draw() {        System.out.println("绘制三角形");    }}/** * 客户端 */public class Client {    public static void main(String[] args) {        GraphicEditor graphicEditor = new GraphicEditor();        graphicEditor.drawShape(new Circle());// 绘制圆形        graphicEditor.drawShape(new Square());// 绘制正方形        graphicEditor.drawShape(new Triangle());// 绘制三角形    }}

程序运行结果:

绘制圆形

绘制正方形

绘制三角形

UML类图如下:

图2

(4) 总结

面向对象的其他几个设计原则以及设计模式,都是为了使程序符合OCP原则,遵循OCP原则可以带来面向对象技术所声称的巨大好处,也就是灵活性、可扩展性、可重用性以及可维护性。

然而,并不是说只要使用一种面向对象语言就必须要遵循OCP原则。对于应用程序中的每个部分都肆意地进行抽象并不是一个好的注意,这只是为了OCP而OCP。正确的做法是,开发人员应该仅仅对程序中呈现出频繁变化的那些部分做抽象,对于那些基本不会变化或者变化很小的部分,保持原状即可。拒绝不成熟的抽象和抽象本身一样重要。

设计一个扩展自抽象类geometricobject的新的triangle类_面向对象设计原则之开放封闭原则(开闭原则OCP)...相关推荐

  1. 设计一个扩展自抽象类geometricobject的新的triangle类_如何进行代码抽象设计?

    如何写好一段代码?直接CRUD.if-else.for.一个大方法等实现,从完成任务的角度来说没任何问题,但是会遇到一些问题,例如:后续写个新逻辑又得重新开发,而且代码相似度很高:想添加一个额外的功能 ...

  2. 设计一个扩展自抽象类geometricobject的新的triangle类_C++ 接口(抽象类)

    C++ 接口(抽象类) 接口描述了类的行为和功能,而不需要完成类的特定实现. C++ 接口是使用抽象类来实现的,抽象类与数据抽象互不混淆,数据抽象是一个把实现细节与相关的数据分离开的概念. 如果类中至 ...

  3. 面向对象软件设计原则【JAVA】(开闭原则、里氏代换、依赖倒转、接口隔离、迪米特法则、合成复用原则)

    软件设计原则[JAVA](开闭原则.里氏代换.依赖倒转.接口隔离.迪米特法则.合成复用原则) 1.开闭原则 2.里氏代换原则 3.依赖倒转原则 4.接口隔离原则 5.迪米特法则 6.合成复用原则 1. ...

  4. 设计一个扩展自抽象类geometricobject_设计模式4之模板方法模式

    背景 在开发中,你肯定有遇到过这样一种场景:你知道某个方法的关键步骤以及执行顺序,但是里面有的具体步骤并清楚.你会怎么做? 比如你早上起床到公司,我们分解步骤: "起床洗漱吃早餐乘坐交通工具 ...

  5. 设计一个处理两种类型地址的地址簿程序_编译器设计-符号表-中间代码生成

    编译器设计-符号表-中间代码生成 Compiler Design - Symbol Table Compiler - Intermediate Code Generation 一.Compiler D ...

  6. 面向对象软件设计的“开—闭”原则

    1.什么是开闭原则         "开-闭"原则是指软件实体应当对扩展性开放,对修改关闭.即软件实体应该在不修改的前提下扩展,这个原则实际上为软件设计指明了目标.我们知道软件设计 ...

  7. 软件设计七大原则实战(二)-开闭原则

    1  开闭原则的定义 开闭原则是Java世界里最基础的设计原则,它指导我们如何建立一个稳定的.灵活的系统,先来看开闭原则的定义: Software entities like classes,modu ...

  8. 从零开始学习Java设计模式 | 软件设计原则篇:开闭原则

    从本讲开始,咱们就要开始学习第一章中的第三部分内容,即软件设计原则了. 在软件开发中,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,程序员要尽量根据6条原则来开发程序,从而提高软件 ...

  9. Java面向对象设计原则1——开闭原则

    在我们学习面向对象编程的时候,总会出现一些问题,好比以前刚刚写好的代码,上线测试可以.正常运行,突然有一天说要加一个功能,改完之后,发现以前正常运行的功能不能用了,类似这样的问题有好多好多,为了避免类 ...

最新文章

  1. 扩展Ext2类 Extending Ext2 Class
  2. ubuntu运行Faster R-CNN
  3. Shell——read读取控制台输入和函数
  4. 在团800运维工作总结之couchbase使用
  5. 速收藏,《机器学习实战》Python3环境算法实现代码
  6. Cython 3.0 中文文档校对活动 | ApacheCN
  7. 人工智能作业考试汇总
  8. ndarray、array、asarray的区别以及查看变量地址、is和==的区别
  9. 出现画面抖动_手机拍照时模糊,抖动?赶紧来拯救你的拍摄技术吧!
  10. 基金公司产品管理系统搭建与推广
  11. LBP特征算子原理过程
  12. “磁碟机”病毒疫情紧急!已有超过5万台电脑被感染
  13. 苏宁小店助销湘西椪柑 6小时双线销量超60000斤
  14. 数据分析中常见的10种数据编码方式
  15. STM32通过IIC驱动MLX90614红外温度传感器
  16. 手游运营数据分析指标百科全说
  17. 新加坡南洋理工大学招募博士生、博士后、研究工程师和研究科学家
  18. 美团外卖的用户画像怎么设计?用户画像全流程讲解!
  19. 极客日报:蚂蚁启动春招,技术人才需求超八成;微软动视暴雪收购案遭调查;Firefox 98 发布|极客头条
  20. xshell基础使用命令

热门文章

  1. oracle rman 实例,Oracle数据库rman常用命令的使用示例
  2. 基于JAVA+SpringMVC+Mybatis+MYSQL的周报管理系统
  3. 三星note3 android4.3,三星N9002 (Note3联通双卡版 Android4.3)一键救砖教程,轻松刷回官方系统...
  4. Dijkstra求最短路 II
  5. day55 linux 基础以及系统优化
  6. Magisk —— 安卓新一代的第三方拓展,systemless模式
  7. 【送书福利】第一次送书活动(总共10本)
  8. boost 学习笔记
  9. 设计模式——解释器模式
  10. 数据分析应用统计学之基本统计量【平均数、众数、中位数、四分位数】