写点什么

面向对象设计原则 -- 开放关闭原则 (OCP)

用户头像
张荣召
关注
发布于: 2020 年 09 月 27 日

面向对象分析设计(OOA/OOD)案例分析:

UML练习:设计一个控制电话拨号的软件。

Use Case:拨打电话

  • 我们按下数字按钮,屏幕上显示号码,扬声器发出按键音。

  • 我们按下send按钮,系统接通无线网络,同时屏幕上显示正在拨号。



1.一级直接抽象:(客观世界:问题领域======>对象)静态分析。

   设计一个控制电话拨号的软件(拨号盘)。

    我们按下数字按钮,屏幕上显示号码,扬声器发出按键音。

    我们按下send按钮,系统接通无线网络,同时屏幕上显示正在拨号



             



设计评估:好?不好?评估依据是什么?

评估方法:单从类图上看不出设计的好坏。把类图放到开发上下文环境中-------不断的需求变更

                (开发期间需求变更,上线之后需求变更),需求变更后,设计是否能有效支撑

--------衡量设计好不好的依据



根据UML编码,并评估

public class Button{    //1.具体类:抽象层次比较低。针对具体类编码,降低可扩展性。(比如,需求变更,出现其他字符按钮。)

                                    //------针对接口编程,而非针对具体类编程

      public final static int SEND_BUTTON=-99;

      private    Dialer  dialler;   //2.强依赖Dialler具体类,对Dialler的耦合程度偏高。(违反高内聚,低耦合设计原则)

                                                //Button的移植他处时,必须携带Dialler。dialler如果再对其他对象产生强依赖。则移植性更弱。

      private    Integer token;

      public Button(Dialler dialler,Integer token){

            this.dialer=dialer;

            this.token=token;

      }



      public void press(){

            swith(token){      //3.需求变更:“*”按钮,“#”按钮,要操作需求时,必须到这里来修改代码。

//修改这里的代码,可能会影响其他代码。

                                        //-------违反OCP原则(open-close priciple)

                  case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9:

                            dialler.enterDigit(token);

                            break;

                   case SEND_BUTTON:

                           dialler.dial();

                           break;

                    default:

                            throw new UnspportedOperationException("unkown button pressed:token="+token);

            }

      }

}



public class Dialler{

      public void enterDigit(int digit){

            screen.display(digit);

            speeker.beep(digit);

      }

      public void dial(){

            screen.display("dialing....");

            radio.connect();

      }

}



面向对象设计的目的和原则:

OOD目的:强内聚,低耦合,从而使系统 

                                  易扩展 ---易于增加新的功能

                    更健壮 ---不容易被粗心的程序元破坏

                    可移植 ---能够在多样的环境下运行

                    更简单 ---容易理解,容易维护



OOD原则:

  • OCP(Open/Closed Principle:开闭原则)

      对扩展开放  (open for extension)

      对修改关闭  (closed for modification)

      简言之:不需要修改软件实体(类,模块,函数等),就能实现功能的扩展           



      扩展模块:传统方式修改源代码。如何不修改源码而能实现功能扩展?=====>关键在于抽象。



针对电话拨号软件改进:

改进目标一:实现Button可扩展(*,#等)



改进评估:实现了Button的可扩展。但是Button子类对象对Dialler强耦合,如果Dialler做了修改,Button子类也会被迫做出修改。

产生问题:如果降低对Dialler的耦合?



改进目标二:对Dialler低耦合。

分析:数字按钮======>dialler 做的事情:screen显示,speeker出音。

          Send按钮======>dialler做的事情:连接无线网络,screen显示拨号。

          推论:不同按钮,dialler做不同的事情====>可将dialler要做的事情,视为不同的策略====>使用策略模式对dialler进行扩展。

改进方案:策略模式



改进效果:Button和策略ButtonStrategy抽象层面,保持稳定。Button直接依赖策略,不直接依赖dialler。通过策略委托给dialler。

改进效果的评估:(使用需求变更进行评估),假设Button需要扩展“* 按钮",或者"#按钮”,ButtonStrategy可以保持稳定不变,但是 dialler 需要扩展*,#的 操作,即需要修改switch case语句。====>dialler不具有扩展性。



改进目标三:提高dialler的可扩展性。

改进分析:dialler拨号盘已经实现操作:1.数字按钮操作(screen,speeker),2,连接无线电网络(radio)。为了保持dialler的稳定性,即使有需求变更,不再对dialler进行修改。数字按钮操作==>数字策略适配器;send按钮操作===>send策略适配器;XX扩展按钮操作===>XX策略适配器===>适配到目标对象(不一定是dialler)。



改进效果:dialler 拨号盘只负责数字键和send键的操作,由StrategyAdpter适配dialler的操作;如果有其他需求需要扩展,可使用相应的适配器适配到目标对象的目标操作。

改进效果的评估: 满足Button的可扩展,Button和ButtonStrategy策略之间关系稳定,即使有需求变更,Button和ButtonStrategy的代码,也可保持稳定,不需要修改。



到此,遵循OOD设计原则(OCP),使用策略+适配模式,达到了设计目标。



抛开现在的设计成果,返回原始需求重新审视:

按下数字按钮(subject), screen(observer)观察到这个通知后,更新屏显,

                                           speeker(observer)观察到这个通知后,更新语音提示;

按下send按钮(subject),screen(observer)观察到这个通知后,更新屏幕显示拨号,

                                           speeker(observer)观察到这个通知后,更新语音提示,

                                           radio(observer)观察到这个通知后,链接无线网络。



改进目标四:使用观察者模式重新设计。





用户头像

张荣召

关注

还未添加个人签名 2018.05.02 加入

还未添加个人简介

评论

发布
暂无评论
面向对象设计原则--开放关闭原则(OCP)