序
已经想看设计模式好久,但是人生就像用两个灶眼的炉台做酒席,小炒总是优先于炖老汤,最后小炒都结束,开饭了,盼了一年的老汤也没炖成。但是,今天一定要开始炖老汤。所以上午一猛劲,看完了head first的第一张——策略模式。
进入主题
说设计模式,我觉得还是先从理论高度阐述一下,然后再娓娓道来比较合适。
1、背景叙述
还是想举书中鸭子的示例,毕竟还是比较生动形象。因为鸭子有共同的特征,鸭子有颜色,可以飞、叫。所以定义一个属性和两个方法:
class Duck
{
Color color;
public void fly() {println("I can fly"); }
public void quack() { println("I can quack");}
}
鸭子又有不同的类别,比如家养的鸭子,野鸭。所以可以将上面的类作为基类,家养鸭和野鸭作为派生类。
但是,问题出现了,大黄鸭不会飞,可以叫。全聚德烤鸭不会飞,也不会叫。怎么办。
方法一:使用覆盖(override),在子类中对基类的方法进行修改。即对于不会飞的鸭子,在子类中重写fly()为如下函数。quack()函数同理。
public void fly(){ ;}
不好:对于fly()还好说,但是某个动作变化比较多就会比较麻烦。比如“烹调”这个动作,光中餐里就有:煎、炒、煮、蒸、炖、烤、焗、烧。而炒里面又有爆炒、小火慢炒等等。那么每次设计一个类,都要重新写一遍代码,岂不太费事?况且还有的可以生着吃,根本不用烹调。
方法二:采用接口形式,将方法从基类中剥离出来,放到接口中,若子类需要有某个方法,就实现对应接口即可。比如:
interface Fly
{
public void fly();
}
class WildDuck implements Fly
{
public void fly()
{
println("I can fly");
}
}
class RoastDuck
{
//其他代码,不适用Fly接口
}
不好:类比较少还行,但是类多了,而且这些类要是都实现同一个操作,那就凄惨了。比如饺子、包子、馒头、花卷都可以蒸熟,那这几个类中“烹调”操作全都要重复实现一遍“蒸”这个操作,岂不累死。
2、策略模式
那我们该怎么办?将方法和属性进行剥离,但还不能彻底剥离(接口就是彻底剥离),彻底剥离就体现不出面向对象的特征,面向过程编程就是数据和操作的完全剥离。那么我们该怎么办?先来看看我们需要什么:
1)统一的接口。也就是说,所有鸭子(不管活的死的,生的熟的),都有统一的接口:fly()和quack(),因为这个是一只鸭子该有的必备技能。
2)不同的表现形式。虽然每只鸭子"应该"有这样的技能,但是依然要“因鸭而异”。也就是说,调用不同鸭子的fly()和quack()应该执行不同的动作。
由上面的两点,自然而然想到了“多态”。
但是,“多态”好像只有“类的多态”,没有方法的多态。其实“多态”就是根据方法的,因为类型的不同,所以展现出不同的功能。
所以,从上面的分析来看,我们就应该把fly()和quack()两个方法封装到类中,然后用Duck基类来调用装有fly()和quack()的类对象。为了不成为话唠,我觉得直接上代码是个不错选择。
interface FlyBehave
{
public void fly();
}
class ReallyFly implements FlyBehave
{
public void fly(){ println("I can fly");}
}
class CantFly implements FlyBehave
{
public void fly(){ println("i cant fly") ;}
}
class Duck
{
Color color;
FlyBehave flybehave;//注意此处,采用接口定义属性,为了实现多态
public Duck(FlyBehave f){ flybehave = f;}
public void fly() { flybehave.fly();//多态调用 }
}
//测试一下
public class Test
{
Duck WildDuck=new Duck(new ReallyFly());
WildDuck.fly();//打印显示:I can fly
}
至此,策略模式阐述完毕。总结一下特点:
(1)基类提供统一接口
(2)将基类的方法封装到其属性中,并用接口声明属性,实现方法的多态调用。