经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » 设计模式 » 查看文章
DesignPattern系列__05开闭原则
来源:cnblogs  作者:本墨  时间:2019/8/7 8:50:27  对本文有异议

介绍

开闭原则是编程设计中最基本、最重要的原则。

定义:一个软件实体如类、方法和模块等,应该对扩展(提供方)开放,对修改(使用方)关闭。用抽象构建框架,用实现扩展细节。

也就是说,在需求发生新的变化时,我们不应该修改原来的代码,而应该通过扩展来满足新的需求。

例子引入

我们要实现一个画图的功能,能够画出圆形、矩形、三角形等,最常见的思路就是利用面向对象的思想,抽象出一个所有图形对象的基类Shape,具体的图形如矩形、圆形灯继承自该类。在Shape中定义一个变量shapeType来保存具体的图形的类型。
定义一个绘图类GraphicEditor,在执行具体的绘图方法(如画一个矩形)时,根据传入的shapeType来执行对应图形的绘制方法。
类图设计如下:

功能初步实现了,但是有什么缺陷吗?让我们来给项目适当的“松松土”:现在我们想要画一个三角形,如何实现呢?
也很简单:再定义一个类Triangle继承自Shape,并且在GraphicEditor修改方法,加入对三角形的类型判断,具体的代码如下:

  1. public class Ocp {
  2. public static void main(String[] args) {
  3. GraphicEditor graphicEditor = new GraphicEditor();
  4. graphicEditor.drawShape(new Rectangle());
  5. graphicEditor.drawShape(new Circle());
  6. graphicEditor.drawShape(new Triangle());
  7. }
  8. }
  9. //这是一个用于绘图的类 [使用方]
  10. class GraphicEditor {
  11. //接收Shape对象,然后根据type,来绘制不同的图形
  12. public void drawShape(Shape s) {
  13. if (s.shapeType == 1)
  14. drawRectangle(s);
  15. else if (s.shapeType == 2)
  16. drawCircle(s);
  17. else if (s.shapeType == 3)
  18. drawTriangle(s);
  19. }
  20. //绘制矩形
  21. public void drawRectangle(Shape r) {
  22. System.out.println(" 绘制矩形 ");
  23. }
  24. //绘制圆形
  25. public void drawCircle(Shape r) {
  26. System.out.println(" 绘制圆形 ");
  27. }
  28. //绘制三角形
  29. public void drawTriangle(Shape r) {
  30. System.out.println(" 绘制三角形 ");
  31. }
  32. }
  33. class Shape {
  34. int shapeType;
  35. }
  36. class Rectangle extends Shape {
  37. public Rectangle() {
  38. super.shapeType = 1;
  39. }
  40. }
  41. class Circle extends Shape {
  42. public Circle() {
  43. super.shapeType = 2;
  44. }
  45. }
  46. //新增画三角形
  47. class Triangle extends Shape {
  48. Triangle() {
  49. super.shapeType = 3;
  50. }
  51. }

OK,新的需求也实现了,现在,发现问题了吗?
我们每次遇见新需求之外,除了定义新的图形类,还要对类GraphicEditor进行修改。
根据前面提到的“开闭原则”中提到的,应该对修改关闭,对扩展开放,我们不应该修改类GraphicEditor,这样会严重影响代码的稳定性和可维护性。
现在,我们尝试按照“开闭原则”来实现这个功能。
根据“开闭原则”,我们应该封装变化,在这里,我们在Shape中定义一个抽象的绘图方法,并在各自实现类内进行具体实现。在类GraphicEditor中,只定义一个接受参数为抽象(Shape)的方法,使得类不再去受到类型影响,满足了“开闭原则”。
具体的代码如下:

  1. public class Ocp {
  2. public static void main(String[] args) {
  3. //使用看看存在的问题
  4. GraphicEditor graphicEditor = new GraphicEditor();
  5. graphicEditor.drawShape(new Rectangle());
  6. graphicEditor.drawShape(new Circle());
  7. graphicEditor.drawShape(new Triangle());
  8. }
  9. }
  10. //这是一个用于绘图的类 [使用方]
  11. class GraphicEditor {
  12. //接收Shape对象,然后根据type,来绘制不同的图形
  13. public void drawShape(Shape s) {
  14. s.draw();
  15. }
  16. }
  17. abstract class Shape {
  18. int shapeType;
  19. //定义一个抽象的画图方法
  20. public abstract void draw();
  21. }
  22. class Rectangle extends Shape {
  23. public Rectangle() {
  24. super.shapeType = 1;
  25. }
  26. @Override
  27. public void draw() {
  28. System.out.println("绘制矩形");
  29. }
  30. }
  31. class Circle extends Shape {
  32. public Circle() {
  33. super.shapeType = 2;
  34. }
  35. @Override
  36. public void draw() {
  37. System.out.println("绘制圆形");
  38. }
  39. }
  40. //新增画三角形
  41. class Triangle extends Shape {
  42. Triangle() {
  43. super.shapeType = 3;
  44. }
  45. @Override
  46. public void draw() {
  47. System.out.println("绘制三角形");
  48. }
  49. }

在改进的代码中,我们将画图方法进行抽象,定义在基类Shape中,并通过子类各自实现对应的画图方法。并且,对于类GraphicEditor而言,只需定义一个接受基类作为参数的方法即可,代码变得整洁、易于维护。

使用注意事项

在实际使用中,需要注意以下几个方面:

1.抽象约束

这点的含义包含三个意思:
1.通过接口或者抽象类约束扩展,对扩展进行边界限定,不允许出现在接口或者抽象类中没有定义的public方法;
2.参数类型,要尽量使用接口或者抽象类,不应该使用实现类。
3.抽象层作为约束,应该尽量保持稳定,一旦确定不容修改。

2.元数据控制模块行为

在实际开发中,要尽量使用注解或者配置文件来控制程序的行为,减少重复开发。比如搭建ssm框架中,使用注解或者配置文件来注入bean。

3.约定优于配置

对于大家普遍遵循的章程或者约定,我们要严格遵守,这样能减少配置文件的编写。比如MyBatis框架对xml文件的扫描,默认会去和接口同名的包下去查找,只要我们遵循这一约定, 就无需格外配置。

4.封装变化

对变化的封装包括两点:
1.相同的变化,应该封装到一个接口或者抽象类中;
2.不同的变化,应该封装到不同的接口或者抽象类中,不应该有两个不同的变化封装在一个接口或者抽象类中。

一句话总结

开闭原则,是一切设计模式的基础,可以说其他原则和设计模式都是为了实现开闭i原则。

原文链接:http://www.cnblogs.com/JackHou/p/11303973.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号