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

(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类图如下:

38b678490de4ea7467480f12f71adb09.png
图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类图如下:

3dbe3ce0365d35c15b6ed5af232f8607.png
图2

(4) 总结

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值