设计一个扩展自抽象类geometricobject的新的triangle类

您所在的位置:网站首页 graphiceditor图形 设计一个扩展自抽象类geometricobject的新的triangle类

设计一个扩展自抽象类geometricobject的新的triangle类

2024-07-09 16:54| 来源: 网络整理| 查看: 265

(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。正确的做法是,开发人员应该仅仅对程序中呈现出频繁变化的那些部分做抽象,对于那些基本不会变化或者变化很小的部分,保持原状即可。拒绝不成熟的抽象和抽象本身一样重要。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3