设计模式-策略模式
设计模式在我们的日常开发中十分重要,好的软件架构可以使软件更加强壮,使软件变得更易于维护,建立可以维护的软件系统,要诀就在于随时想到系统以后可能需要的变化以及应付变化的原则。相信大多数的人都学习过面向对象语言,c++、java、python,这些面向对象的语言精髓就在于抽象、继承、多态,但是只知道这三样是不能设计出好的软件的,里面需要设计模式的参与。
有过web开发经验的小伙伴可能对Spring
、MyBatis
这类的框架比较了解,这类框架的实现里面充满了设计模式,各种设计模式,从而它才能走的长远,想要走的长远就需要维护,想要维护就需要好的软件架构,再加上它功能的强大,因而走到了今天,还可能走的更远。
设计模式是前人总结出来的经验,我们站在前人的肩膀上,依附着前人的经验,我们就可以设计出更好的、更棒的软件
接下来就以一个场景出发,从而逐渐引出策略模式
场景
我们模拟一个人,人由于有共同的行为,例如姓名、性别、吃饭、睡觉、工作等等,这就能抽象出一个类,由于每个人都有自己的特性,因此用一个具体的类去实现这个抽象出的类
代码如下(代码中的get与set均省略):
1 | public abstract class Person { |
1 | public class Adult extends Person{ |
1 | public class Young extends Person { |
这样设计是不是看上去挺好的,没什么瑕疵,其实这里面有很大的漏洞
存在的问题
第一个问题,很多人没有工作,例如儿童,它是没有任何工作的,就目前的设计来看,没工作也是需要实现doWork()
方法的,例如下面这样
1 | public void doWork() { |
第二个问题,同类人有同一种工作,那是不是每个类都需要重复的实现一些东西,这样做开发效率会很低。例如程序员的工作的敲代码,而学习计算机的学生的工作也是敲代码,这两类人的工作都指向了同一个,这就需要两次实现
第三个问题,很多人具有同一类工作,例如上面的这样,但是,当这份工作随着时代的更新,工作的方式有所衍化,那是不是要到每一个实现类中去修改这种工作方式,还是上面的例子,随着时代的进步,程序员和学习计算机的学生不再需要编码了,每天喝喝咖啡锻炼锻炼身体就是程序员的工作,那是不是需要将每个实现都改一下
解决方案
目前针对第一个问题,倒是有一个解决方案,我们可以把doWork()
抽象为一个接口,有工作的人则实现这个接口,对Adult
和Children
类,可以这样做,首先修改Person
类
1 | public abstract class Person { |
将Person中的抽象方法doWork()
转移到接口Work中
1 | public interface Work { |
这样,有工作的人可以实现这个接口,并且实现其中的方法,没有工作的人则不实现这个接口,Adult
和Children
类如下
1 | public class Adult extends Person implements Work { |
1 | public class Children extends Person { |
这样做是否还存在一个问题,因为Java接口不具有实现代码,所以实现接口不具有代码重用的功能,这也就是引出来了问题二、三。
这里提出来一个设计原则:
找出应用中可能需要变化之处,把他们独立出来并封装,不要和那些不需要变化的代码混合在一起
这个设计原则也是之所以有设计模式的初衷,几乎所有的设计模式都遵守这个原则
换句话说,如果每次新的需求一来,都会使某方面的代码发生变化,那么你就可以确定,这部分代码需要被抽出来,和其他稳定的代码有所区分。
现在我们来尝试解决第二个和第三个问题,其实他俩可以被划分为同一个问题,即代码的重用
其实可以这样做,定义一个接口将work这个方法抽象出去,然后定义一些类去实现这个doWork()
方法,每个类代表了每个不同的工作
1 | public interface Work { |
1 | public class StudyWork implements Work{ |
1 | public class ProgrammeWork implements Work{ |
这样当使用的时候,只需要使用实现的类即可,这样相同的工作就达到了代码复用的原则
这就引出了第二个设计原则:
针对抽象编程,而不是根据实现编程
以Young这个类为例,详细一下使用方式
1 | public class Young extends Person { |
是不是很清晰了,通过构造器指明他从事的工作,并且利用多态的性质,随时可以改变工作的类型,比如换工作了。如果这时候又增加了一类人,这类人从事的工作很少见,我们就可以再创建一个类实现Work接口,然后给这类人,这期间并没有修改原有的代码,真正的让设计具有了弹性
对UML类图不了解的小伙伴可以查看https://juejin.cn/post/6872242138378141710
另外需要说到的一点就是“有一个”比“是一个”更好,这句话的含义引出了第三个设计原则:
少用继承,多用组合
最后给出策略模式的定义:
策略模式定义了算法簇,分别封装起来,让他们之间可以互相替换,此模式让算法的替换独立于使用算法的客户
总结
我们从一个场景出发,首先进行了一个简略的封装,到一步一步提高封装的粒度,到最后使用策略模式,这期间引出了三个设计原则:
- 找出应用中可能需要变化之处,把他们独立出来并封装,不要和那些不需要变化的代码混合在一起
- 针对抽象编程,而不是根据实现编程
- 少用继承,多用组合