策略模式:第一了算法族,分别封装起来,让他们之间可以互相替换,次模式让算法的变化独立于使用算法的客户。
首先看个错误的面向对象。
假如我们需要写一个关于鸭子的程序,各种类型的鸭子。第一想到的就是建一个Duck接口,然后各种鸭子实现这个接口。
interface Duck{
public void quack();
} class MiniDuck implements Duck{
@Override
public void quack() {
//........
}
}
但是如果突然有一天,有个新需求,要有一个会飞的鸭子。那么炸了,在Duck上添加一个fly方法,所有的实现类都需要重写这个方法,如果真的有这个耐心把所有的类上都添加了一个新的方法,但是之前的测试你还需要重新一个一个的测试。确定要这么干。这时也许会想到另外一个方法,不在Duck上添加,只需要在会飞的那类鸭子中添加一个方法。如果你这么想,当初设计这个Duck接口干什么。直接写每个类啊,如果真的就这么干了,假如某个某个方法中,传入的参数只需要一个鸭子,不管什么类型的鸭子。要怎么处理。。。
经过上面这么一想,Duck是一定要有的,这时的想法会是在创建两个接口,一个Flyable接口,一个Quackable接口,这两个接口分别有fly和quack方法,Duck中有自己的方法。如果这么弄,那么每个鸭子首先要实现Duck接口,其次,根据具体功能在判断需不需要集成另外两个接口。你感觉这样真的好?如果感觉还可以接受,突然有一天,大多数的鸭子又有了一个查看颜色的方法。你需要在写第三个接口来让实现类鸭子实现。40个类同时实现这个接口,想一想工作量。
设计原则:找到应用中可能要变化之处,把他们独立出来,不要和那些不变的代码混在一起。
这里我们知道鸭子的飞和叫是变化的,就把这两部分抽出来,让他们远离鸭子类。设计两组类处理飞和叫。
设计原则:针对接口编程而不是针对实现编程。(这里所说的针对接口编程指的是针对超类,关键在于利用多态,接口并不一定是interface,可以在没有interface的情况下“针对接口编程”,只是一般来说我们用interface和abstract来当超类)
我们利用接口来代表每个行为,创建一个FlyBehavior和一个QuackBehavior,行为的每个实现都将实现上面的接口,而不是由Duck类来实现上面的接口。原来的时候,行为由Duck超类来实现或由子类具体实现。这两种做法都是依赖于实现。而现在的实现是在每个接口的子类,不会绑定在Duck身上。
直接看代码
interface FlyBehavior {
public void fly();
} class FlyWithWings implements FlyBehavior {
@Override
public void fly() {
System.out.println("i am flying");
}
} class FlyNoWay implements FlyBehavior {
@Override
public void fly() {
System.out.println("i can not fly");
}
} interface QuackBehabior {
public void quack();
} class Quack implements QuackBehabior {
@Override
public void quack() {
System.out.println("Quack");
}
} class MuteQuack implements QuackBehabior {
@Override
public void quack() {
System.out.println("<< Silence >>");
}
} class Squeak implements QuackBehabior {
@Override
public void quack() {
System.out.println("Squeak");
}
} abstract class Duck {
FlyBehavior flyBehavior;
QuackBehabior quackBehabior; public void setFlyBehabior(FlyBehavior fb) {
this.flyBehavior = fb;
} public void setQuackBehavior(QuackBehabior qb) {
this.quackBehabior = qb;
} public Duck() {} public abstract void display(); public void performFly() {
flyBehavior.fly();
} public void performQuack() {
quackBehabior.quack();
} public void swim() {
System.out.println("all ducks float");
}
} class MallardDuck extends Duck {
public MallardDuck() {
quackBehabior = new Quack();
flyBehavior = new FlyWithWings();
} @Override
public void display() {
System.out.println("i am a Mallard duck");
}
}
类图:
这里Duck是不是应该也设计成接口?这个要根据自己具体的程序来定,在这个程序中,并不需要,让Duck成为一个具体类可以让继承的子类与Duck具有相同的属性和方法,比如swim方法,不用每个继承类在去重写swim方法,游泳都是一样的游泳。
这里的设计模式就是用到的策略模式,如果想动态的设置鸭子的飞行方式,在运行时,只要设置一个对应的FlyBehavior即可。
设计原则:多用组合,少用继承