如何在不修改代码实现需求变更

软件设计应该为需求变更而设计,应该能够灵活、快速地满足需求变更的要求,毕竟日常工作中产品改需求是常有的事。我们也应该迎合需求变更,持续的需求变更意味着开发的软件保持活力,也让我们为需求变更而进行的设计有了用武之地,让技术也业务进入良性循环,共同进步。

刚开始工作我们处理需求变更大部分都是修改程序,一段时间后,代码被修改的面目全非,换个人都很难看懂,代码也变成了我们常说的坏代码。

在不修改代码实现需求变更,听起来有点厉害,其实这个也是软件设计需要遵循的基本原则:开闭原则

开闭原则

开闭原则:软件实体(模块、类、函数等等)应该对扩展是开放的,对修改是关闭的。

扩展开放是指软件实体的行为是可扩展的,当需求变更的时候,可以对模块进行扩展,使其满足需求变更的要求。

修改关闭是指当对软件实体进行扩展的时候,不需要改动当前的软件实体,不需要修改代码;对于已经完成的类文件不需要重新编辑,对于已经编译打包好的模块,不需要再重新编译。

设计模式中很多模式都可以用来解决软件扩展性问题,也是符合开闭原则的。如下:

使用策略模式实现开闭原则

假设我们需要设计一个可以通过按钮拨号的电话,核心对象是按钮(Button)和拨号器(Dialer)。使用策略模式,我们在按钮和拨号器之间增加抽象接口ButtonServer,Button依赖ButtonServer,Dialer实现ButtonServer。

image

当Button按下的时候,就调用ButtonServer的buttonPressed方法,实际调用的是Dialer的buttonPressed方法,这样既完成了Button按下的时候执行Dialer方法的需求,又不会使Button依赖Dialer,Button可以扩展复用其他需要使用Button的场景,任何实现ButtonServer的类,都可以使用Button,而不需要对Button代码进行修改。Button也不需要判断按钮类型,只需要把按钮类型传给ButtonServer即可,这样做新增按钮类型的时候也不需要修改Button代码。

策略模式是一种行为模式,多个策略实现同一个策略接口,编程的时候 client 程序依赖策略接口,运行期根据不同上下文向 client 程序传入不同的策略实现。在我们这个场景中,client 程序就是 Button,策略就是需要用 Button 控制的目标设备,拨号器、密码锁等等,ButtonServer 就是策略接口。通过使用策略模式,我们使 Button 类实现了开闭原则。

使用适配器模式实现开闭原则

Button我们使用策略模式实现了开闭原则,但是Dialer要实现ButtonServer接口,根据传递的按钮类型参数决定执行的方法,又需要大量的判断语句,不符合开闭原则。

这种情况可以使用适配器模式进行设计,适配器模式是一种结构模式,用于将两个不匹配的接口适配起来,使其能够正常工作。

image

不由Dialer类直接实现ButtonServer接口,而是增加两个DigitButtonDialerAdapter、SendButtonDialerAdapter,由适配器实现ButtonServer接口,在适配器的buttonPressed方法中调用Dialer的enterDigit()和dial(),而Dialer类保持不变,Dialer类实现开闭原则。

这种实现中,在修改Dialer代码的前提下,通过两个适配器实现了ButtonServer接口使得Button能够调用,在适配器的buttonPressed中调用Dialer的方法,适配了Dialer。

使用观察者模式实现开闭原则

通过策略模式和适配器模式,我们使得Button和Dialer都符合了开闭原则,但是如果要求能用一个按钮控制多个设备,比如按钮按下进行拨号的同时,还需要扬声器根据不同按钮发出不同的声音,将来还需要根据不同按钮点亮不同颜色的灯。我们现在的设计可能需要修改适配器的代码,又不符合开闭原则了。

这种情况可以用观察者模式进行设计,继续提高扩展能力。

image

把ButtonServer改名为ButtonListener,表示这是一个监听者接口,仅仅是为了便于识别。

Button类中增加了成员变量List和成员方法addListener,通过addListener,我们可以增加多个需要观察按钮按下事件的监听者实现,当按钮需要控制新设备的时候,只需要将实现了ButtonListener的设备实现添加到Button的List列表即可。

观察者模式是一种行为模式,解决一对多的对象依赖关系,将被观察者对象的行为通知到多个观察者,也就是监听者对象。

使用模板方法实现开闭原则

另一个可能变更点就是如果业务要求按下按钮的时候,除了控制设备,按钮本身还需要执行一些操作,实现一些成员变量的状态更改,不同按钮类型进行的操作和记录状态各不相同,按照当前的设计可能还是需要修改Button的实现。

使用模板方法进行设计:

image

在 Button 类中定义抽象方法 onPress,具体类型的按钮,比如 SendButton 实现这个方法。Button 类中增加抽象方法 onPress,并在 press 方法中调用 onPress 方法。

image

模板方法模式,就是在父类中用抽象方法定义计算的骨架和过程,而抽象方法的实现则留在子类中。在我们这个例子中,press 方法就是模板,press 方法除了调用抽象方法 onPress,还执行通知监听者列表的操作,这些抽象方法和具体操作共同构成了模板。而在子类 SendButton 中实现这个抽象方法,在这个方法中修改状态,完成自己类型特有的操作,这就是模板方法模式。

总结

抽象是实现开闭原则的关键。开闭原则是软件设计的核心原则,是方向性的,在软件设计过程中应该时刻以开闭原则指、审视自己的设计。

此文章为2月Day02学习笔记,内容来源于极客时间《后端技术面试 38 讲》,推荐该课程

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

故里学Java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值