缘由
今天周五,下午那集课是讲设计模式的,今天讲了三个。现在总结一个, 策略模式。上课讲的什么还是有点晕,不过好在看 head first 设计模式,还是把这个设计模式看懂了。下面这篇博客几乎是转述了head first 设计模式 里面的内容,表述的可以说更为清楚
下面,我讲讲我的心得和体会。
背景
文章的背景主要是说:在一个游戏中需要很多种类(可以简单理解为外观)的鸭子,这些鸭子都会游泳、会叫。很自然的就想到了一种处理,那就是继承。因为所有的所有的鸭子只是有一点不同而已(表现在代码中就是方法、函数),那就是外观。那么先生成一个父类写好了所有的方法会游泳、会叫,那么外观设置抽象函数,让子类自己去实现就可以了。
如下图所示:
我认为这是一种非常正常的处理方法,非常容易让人想到。那么会有什么缺点呢?
增加鸭子的飞功能
下面项目又要增加鸭子飞的功能,大概是为了增加游戏的趣味性吧。这一点也非常简单,直接在我们直接在父类里面增加飞的方法就可以,那么所有子类的鸭子(拥有不同的外观)都具有了飞的功能。如下图所示:但是并不是所有鸭子都可以飞
并非所有鸭子都能够飞,勉强一点就认为是游戏里有一些橡皮鸭,这些鸭子是不会飞的。或者项目经理就说了:这个鸭子能飞,那个鸭子不能飞。那怎么办?而且有些鸭子不会飞也不会叫,就像木头鸭子。但是飞和叫的方法都是在父类里面实现好了的,难道我们就可以去子类里面一个一个的按要求去重写叫的方法和飞的方法?听起来已经很麻烦了。不过确实可以这么做。下图所示例子虽然是叫,但是飞和叫是类似的。有些鸭子这个版本能飞,下个版本不能飞
过了一个月,游戏策划觉得不对,这类鸭子不该飞,但是另一类鸭子是该飞的。那么我们程序员就会发现策划是不是玩咱们啊。而且老是不稳定,这个鸭子那样改改、那个鸭子那样改改。真坑啊。好吧。确实还可以再去子类一个一个的改。但是这里有了一个稍微先进的一个办法。我们把飞的方法放在接口里,把叫的方法也放在接口里。那个鸭子要飞,就让他实现飞的接口。另一个鸭子要叫,那就让他实现叫的那个接口就好了。如下图所示:不是所有的鸭子叫法都一样
结果项目经理突然又说:这个鸭子是呱呱叫,另一个鸭子是吱吱叫。确实没问题,可以在不同的鸭子里面单独在去复写叫的方法,但是当在有上百的鸭子的时候,有十几只鸭子的代码一样,那就是一致复制、粘贴,好嘛。下次这十几只鸭子又要变个叫法,又是十几只鸭子的叫的代码的复制、粘贴,确实是非常痛苦的吧?需要一个应变的招
说到这里,书中给了我们两句警示名言:- 软件开发中的不变的真理:变(这个好像在考研政治中有类似的话哇)
- 设计原则:Identify the aspects of your application that vary and separate them from what stays the same.(找出应用中可能需要变化的之处,把他们独立出来,不要和那些不需要改变的代码混在了一起)
这里我们就会发现,主要变的就是叫和飞,而且不只是飞不飞和叫不叫的问题,是这样叫和那样叫(多种叫法)的问题。那么我们就把这两组单独处理。为什么是两组,因为现在不是“叫不叫”的问题,是这样叫和那样叫的问题。也就是说,如果一个鸭子只能选择叫还是不叫,那么很好解决,但是如果一个鸭子还必须要选择吱吱叫、嘎嘎叫以及不叫,那么问题又更复杂了。这个时候,我们管嘎嘎叫、吱吱叫、不叫是属于叫的一组。而飞那一组,可以低空飞、高空飞、跳着飞、不飞等等形成了另一组。
那么,我们看看飞这一组和叫这一组的具体实现办法,如下图所示,叫这一组:有一个接口,然后又三个实现类,三个实现类分别代表了不同的叫的方法,那么也必须具体实现。飞类似。
飞的代码如下:
public interface FlyBehavior {
void fly();
}
public class FlyNoWay implements FlyBehavior {
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println("不能飞");
}
}
public class FlywithWings implements FlyBehavior {
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println("飞啊飞");
}
}
叫的代码如下:
public interface QuackBehavior {
void quack();
}
public class Quack implements QuackBehavior {
@Override
public void quack() {
// TODO Auto-generated method stub
System.out.println("嘎嘎叫");
}
}
public class MuteQuack implements QuackBehavior {
@Override
public void quack() {
// TODO Auto-generated method stub
System.out.println("不能叫");
}
}
public class Squeak implements QuackBehavior {
@Override
public void quack() {
// TODO Auto-generated method stub
System.out.println("吱吱叫");
}
}
新方式生成的鸭子
抽象类,鸭子,必须拥有两个成员属性,一个飞的接口(FlyBehavior),另一个是叫的接口(FlyBehavior),如下图所示:代码如下:
public abstract class Duck {
public Duck(){
}
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public abstract void display();
public void performFly(){
flyBehavior.fly();
}
public void performquack(){
quackBehavior.quack();
}
public void swim(){
System.out.println("鸭子游啊游!!!!");
}
public void setFlyBehavior(FlyBehavior fb){//动态绑定,用处见下
flyBehavior=fb;
}
public void setQuackBehavior(QuackBehavior qb){//动态绑定,用处见下
quackBehavior=qb;
}
}
那么来一个子类,我们让看看其如何实现飞和叫:
public class ChinaDuck extends Duck {
public ChinaDuck(){
this.flyBehavior=new FlywithWings();
this.quackBehavior=new Squeak();
}
@Override
public void display() {
// TODO Auto-generated method stub
System.out.println("我是一个中国鸭子");
}
}
测试main函数:
public class testMain {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Duck A = new ChinaDuck();
A.display();
A.performFly();
A.performquack();
System.out.println("-----------动态改变后--------------");
A.setFlyBehavior(new FlyNoWay());
A.setQuackBehavior(new MuteQuack());
A.display();
A.performFly();
A.performquack();
}
}
动态设定行为
我们可以随意改变一个鸭子的飞或者叫的行为,如下代码所示: public void setFlyBehavior(FlyBehavior fb){
flyBehavior=fb;
}
public void setQuackBehavior(QuackBehavior qb){
quackBehavior=qb;
}
测试已经写在上面的main函数里面了
运行结果:
我是一个中国鸭子
飞啊飞
吱吱叫
-----------动态改变后--------------
我是一个中国鸭子
不能飞
不能叫
总览图
pdf版本的head first上的图有点不太清晰,所以让我们来看看那位博客的画的图,其实和书上是一样:面向接口编程
在本例中,我们也可以看到面向接口编程的好处,那就是
Duck类中的:
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
我们可以看到,我在声明flyBehavior,指定其为FlyBehavior,但是其实例却为:new FlyNoWay()。FlyNoWay是FlyBehavior 的子类。好处就是我们可以将flyBehavior轻松改为FlyBehavior的任意一个子类,改变了flyBehavior的具体实例,但是代码改动的却非常少。书中所举例子更为直观一些如下图:
总结
到这里,我想对这个设计模式有一定的了解。所谓策略模式:- The Strategy Pattern defines a family of algorithms,encapsulates each one,and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
- (策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。)
源代码
- 打包上传至网盘:策略模式.rar