因为疫情在家无聊,把设计模式复习一遍。强烈推荐《Head First 设计模式》这本书,看了好几遍了,每次都受益匪浅。
策略模式概念
策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
涉及到的设计原则:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
通俗的讲这个原则就是把新的需求所需的代码变化封装,和以前稳定的代码区分。
这个原则的另一种思考方式:把会变化的部分取出并封装起来,以便以后可以轻易地改动或扩充此部分,而不影响不需要变化的其他部分。
几乎每个设计模式都提供一套方法令系统中的「某部分改变」不会影响其他部分。
举个例子来具体说明策略模式,就用书中鸭子的例子,我们需要让不同的鸭子实现不同的行为,比如,真鸭子会飞会叫,玩具鸭不会飞,不会叫,并且不产生代码冗余,且符合我们刚提到的设计原则。
示例代码
先来看一个糟糕的实现方式,首先是鸭子抽象类,所有的鸭子都继承这个类。
public abstract class Duck {
public void swim() {
System.out.println("鸭子游泳");
}
public abstract void display();
}
接着说飞行和叫的接口,有这些行为的鸭子需要实现这两个接口。
public interface Flyable {
void fly();
}
public interface Quackable {
void quack();
}
再来就是几种鸭子的类。
public class MallardDuck extends Duck implements Flyable, Quackable {
@Override
public void display() {
System.out.println("Mallard");
}
@Override
public void fly() {
System.out.println("fly");
}
@Override
public void quack() {
System.out.println("quack");
}
}
public class RedheadDuck extends Duck implements Flyable, Quackable {
@Override
public void display() {
System.out.println("Red head");
}
@Override
public void fly() {
System.out.println("read head fly");
}
@Override
public void quack() {
System.out.println("quack");
}
}
public class RubberDuck extends Duck implements Quackable {
@Override
public void display() {
System.out.println("Rubber");
}
@Override
public void quack() {
System.out.println("quack");
}
}
感受一下吧,这个种实现方式哪里不好?代码冗余,重复代码太多,如果很多个鸭子要修改它的行为,这将是噩梦啊。
回到我们之前的设计原则,找出应用中的变化并独立除了,不要把可变和不可变的代码混合。
分开变化和不变的部分
为了打到这个目的,我们将会建立两组类,和Duck类无关的类,为了实现他们的行为,那么从我们的上述例子来看,那就是「fly」和「quack」两个行为了。比如叫的行为,我们可以实现「呱呱叫」、「嘎嘎叫」和「不会叫」,毕竟鸭子叫法是不一样的。
既然行为已经分出来了,接下来该设计它的行为,这又涉及到一个设计原则:针对接口编程,而不是针对实现编程。
划重点,这是什么意思?针对接口编程,就拿咱们这个例子来讲,是在「Duck」类中,行为不是通过具体的实现去表现,而使用接口代替,比如,「FlyBehavior」和「QuackBehavior」去替代鸭子的行为,他们只负责调用方法,具体的实现方式由这两个接口的行为类去实现。
来比较一下两种两种方式的差别,第一种方式,我们通过实现接口来搞定这两个行为,专注于在鸭子类中实现,没办法更改,当我们需要更改的时候只能通过修改鸭子代码去改。而后面一种,鸭子的子类使用接口表现的行为,实际的实现是通过行为类实现的,而不是绑死在鸭子的子类中。这样就为变化提供了方便。
我们来看看代码实现。
策略模式代码实现
鸭子抽象类,
public abstract class Duck {
QuackBehavior quackBehavior;
FlyBehavior flyBehavior;
public abstract void display();
public void performQuack() {
quackBehavior.quack();
}
public void performFly() {
flyBehavior.fly();
}
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
}
这里面,我们包括了两个接口,一个「QuackBehavior」一个「FlyBehavior」,用以在 performQuack 和 performFly 两个方法中执行动作,下面两个 set 方法,是我们可以更改鸭子行为方法。
再来看飞行行为接口,及它的实现类。
public interface FlyBehavior {
void fly();
}
public class FlyNoWay implements FlyBehavior {
@Override
public void fly() {
System.out.println("can't fly");
}
}
public class FlyWithWings implements FlyBehavior {
@Override
public void fly() {
System.out.println("Fly with Wings");
}
}
这里很简单就是一个接口,两个实现类。同理,叫的接口结构类似。
public interface QuackBehavior {
void quack();
}
public class Quack implements QuackBehavior {
@Override
public void quack() {
System.out.println("Quack");
}
}
public class Squeak implements QuackBehavior {
@Override
public void quack() {
System.out.println("Squeak");
}
}
public class MuteQuack implements QuackBehavior {
@Override
public void quack() {
System.out.println("<< Silence >>");
}
}
两个鸭子类。
public class MalardDuck extends Duck {
@Override
public void display() {
System.out.println("Malard");
}
public MalardDuck(FlyBehavior flyBehavior, QuackBehavior quackBehavior) {
this.flyBehavior = flyBehavior;
this.quackBehavior = quackBehavior;
}
}
public class RedHeadDuck extends Duck {
@Override
public void display() {
System.out.println("red head duck");
}
public RedHeadDuck(FlyBehavior flyBehavior, QuackBehavior quackBehavior) {
this.flyBehavior = flyBehavior;
this.quackBehavior = quackBehavior;
}
}
测试类
public class Test {
public static void main(String[] args) {
MalardDuck malardDuck = new MalardDuck(new FlyWithWings(), new Quack());
malardDuck.display();
malardDuck.performFly();
malardDuck.performQuack();
System.out.println("-----------");
RedHeadDuck redHeadDuck = new RedHeadDuck(new FlyNoWay(), new MuteQuack());
redHeadDuck.display();
redHeadDuck.performQuack();
redHeadDuck.performFly();
System.out.println("change ---------");
redHeadDuck.display();
redHeadDuck.setFlyBehavior(new FlyWithWings());
redHeadDuck.performFly();
redHeadDuck.setQuackBehavior(new Quack());
redHeadDuck.performQuack();
}
}
我们通过构造方法,指定鸭子的行为,通过 set 方法修改鸭子的行为,这样当我们需要增加行为的时候,只需要增加新的行为类,重新指定鸭子的行为就可以了,而无需修改鸭子类的实现。
这里也使用了一个设计原则,多用组合,少用继承。
使用组合建立系统具有很大的弹性,不仅可将算法族封装成类,更可以「在运行时动态地改变行为」,只要组合的行为对象符合正确的接口标准即可。
到这里整个策略模式就完事了。以后再有新想法,再补充吧。