设计模式第一站
JDK版本:9(Java8的语言特性)
参考书籍:《HEAD FIRST 设计模式》
IDE: IntelliJ IDEA
策略模式标准定义
定义了算法族,分别封装起来,让他们之间可以相互替换,初始化时将对象委托给该算法类进行行为的分配,此模式让算法的变化独立于使用算法的客户
情景(简化版本)
有一批鸭子,橡皮鸭不会飞,真鸭子会飞,设计Java类以实现这样的关系
比较粗暴的方法
定义抽象类Duck,fly方法为抽象,等待具体类去实现
public abstract class Duck{
public abstract void fly();
/*
*其他具体方法
*/
}
public class RealDuck extends Duck{
@override
public void fly(){
System.out.println("I can fly");
}
}
public class RubberDuck extends Duck{
@override
public void fly(){
System.out.println("I can't fly");
}
}
当然这样缺点很明显,如果fly内的代码要发生更改,或者要有新的Duck如RocketDuck类,就必须知晓fly方法的源码,这样显然增加了耦合度,不利于代码的维护
改进版本(书上的方法)
分析关系,不难发现除了fly方法之外,duck类的其他方法都是固定的,故可以将fly方法抽离出来,单独封装成一个接口(函数式接口)
public interface FlyBehavior {
public void fly();
}
重点部分!
我们希望把fly单独出来,所以在这里建立两个类去实现FlyBehavior接口。
public class FlyNoWay implements FlyBehavior {
@Override
public void fly() {
System.out.println("fly no way!");
}
}
public class FlyWithWings implements FlyBehavior{
@Override
public void fly() {
System.out.println("fly with wings");
}
}
注意!:
我们发现,FlyWithWings和FlyNoWay实际上是两个行为,为什么把他当作类而不是接口去处理?
这是因为接口不具有实现非静态方法的能力(在JDK8以前是如此,关于default方法之后会讲),只有类可以去实现方法。
紧接着的是出现的另一个问题:
现在fly方法已经抽离到另一个类中了,现在所需要做的就是去实现具体类RealDuck/RubberDuck 了。我们需要从Duck和FlyWithWings继承。。。
等等!Java并没有多继承功能!怎么办?
既然没有多继承功能,那我们只能“曲线救国”了
这里我们需要把FlyBehavior作为实例域(成员变量),在初始化Duck对象的时候去将FlyBehavior指向对应的实现类(FlyWithWings/FlyNoWay),这样就能正确调用实例域的fly方法了。
public class Duck {
FlyBehavior flyBehavior;
public Duck() {}
//去调用成员变量的方法
public void performFly(){
flyBehavior.fly();
}
}
public class RealDuck extends Duck {
public RealDuck() {
//在初始化的时候新建一个FlyWithWings对象,将flyBehavior指向它
flyBehavior = new FlyWithWings();
}
//用设定方法来规定鸭子行为
public void setFlyBehavior(FlyBehavior other){
flyBehavior = other
}
}
这里实际上实现的是委托的效果:Duck类将fly方法委托给flyBehavior去做,而flyBehavior指向的是具体的类(FlyWithWings/FlyNoWay),如此完成类似多继承的操作。
体现思想
我们把行为想象成是“一族算法”,在本例中,算法代表鸭子能做的事(不同的飞行法),客户使用封装好的算法族,不需要知道其具体实现,得到解耦的效果
设计原则
- 面向接口编程,而不面向具体实现
- 多用组合,少用继承
default解法(自己的一点想法)
前文说到我们将FlyWithWings/FlyNoWay设计成类是因为只有类才可以去实现方法,但在jdk8中新增的defalut修饰符可以让接口有了实现非静态方法的能力
public interface FlyWithWings extends FlyBehavior{
@Override
//继承并重写了FlyBehavior的fly方法
defalut public void fly() {
System.out.println("fly with wings");
}
}
public class RealDuck extends Duck implements FlyWithWings{
@Override
public void performFly() {
fly();
}
}
public abstract class Duck {
public abstract void performFly();
}
default的加入使得接口彻底的成为了一个没有成员变量的特殊类,使得类多继承变得更加方便。
但是default看似美好,却无形中增加了耦合度,现在,每个鸭子的fly方法被顶死,我们无法在运行时改变鸭子的fly状态(比如从能飞到不能飞)这恐怕并不是什么好主意
函数式接口写法
jdk8中定义,只含有一个抽象方法的接口为函数式接口,熟悉函数式语言的同学都知道,函数式最大的优点就是简洁,让我们来看看是如何简化的
public class RealDuck extends Duck {
public RealDuck() {
flyBehavior = ()-> System.out.println("i can fly");
}
}
有没有一种梦回ES6的感觉?FlyBehavior是函数式接口,不妨就将其看成函数,该函数指向一个lambda表达式(又称箭头函数/匿名函数),如此就完成了类似于之前方法的new 一个FlyWithWings的操作!
最关键的是,我们不再需要建立一个FlyBehavior的实现类。
Tips:这在处理小范围代码的时候较为方便,当代码量一多,还是去把类写出来比较方便
总结
策略模式是设计模式的第一课,其核心是将经常变动的业务代码分离开来,封装成另外一个行为类处理。把该行为类对象作为大类的成员变量,在初始化时把该行为委托给行为类对象进行分配,从而让行为匹配正确。
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!