设计模式之策略模式

在写策略模式时,这里就不采用先提出概念,然后一步一步论述的写法,我们先抛出问题,看看使用面向对象编程思想是怎么解决问题的。

在这里我们使用鸭子(Duck)这个项目来引出策略模式。我们接到这样一种需求,做一个鸭子类的游戏,作为项目负责人,我们首先应该怎么考虑了?

我们先使用面向对象的设计思想来设计,首先,鸭子都会叫、游泳,所以把这些动作设计在超类里面,然后子类继承父类,在这里超类设计成抽象类。代码如下:

public abstract class Duck {

	public Duck() {
	}

	public void Quack() {
		System.out.println("~~~ gagaga ~~~");
	}

	public void swim() {
		System.out.println("~~~ duck swim ~~~");
	}
}
每种鸭子都有自己的“显示方式”,也就是外表,在超类里面再添加下面抽象方法。

public abstract void display();
构造完超类之后,我们就来做具体的鸭子,假如有绿头鸭,红头鸭,子类设计如下:

/**
 * 绿头鸭子类
 *
 */
public class GreenHeadDuck extends Duck {

	@Override
	public void display() {
		System.out.println("~~~ greenHeadDuc ~~~");
	}
	
}
/**
 * 红头鸭子类实现
 *
 */
public class RedHeadDuck extends Duck{

	@Override
	public void display() {
		System.out.println("~~~ redHead duck ~~~");
	}
}
写一个主方法执行上述类:

public class StimulateDuck {
	public static void main(String[] args) {
		
		//绿头鸭红头鸭实例化对象
		GreenHeadDuck greenHeadDuck = new GreenHeadDuck();
		RedHeadDuck redHeadDuck = new RedHeadDuck();
		
		//绿头鸭方法调用
		greenHeadDuck.display();
		greenHeadDuck.Quack();
		greenHeadDuck.swim();
		greenHeadDuck.Fly();
		
		//红头鸭方法调用
		redHeadDuck.display();
		redHeadDuck.Quack();
		redHeadDuck.swim();
		redHeadDuck.Fly();
		
	}
}
结果如下:
~~~ greenHeadDuc ~~~
~~~ gagaga ~~~
~~~ duck swim ~~~
~~~ no fly ~~~
~~~ redHead duck ~~~
~~~ gagaga ~~~
~~~ duck swim ~~~
~~~ duck fly ~~~

按照上面面向对象的思想,我们成功的解决了需求。但是,后来有新的需求,提出有会飞的鸭子,一种解决办法还是使用面向对象思想,在超类中提供一种方法,如下:

public void Fly() {
		System.out.println("~~~ duck fly ~~~");
	}
但是这种方法不是很好,比如说已经有了几十种鸭子,现在在超类中加了会飞的方法,后面的子类中都会出现会飞的鸭子。 事实上,有很多鸭子是不会飞的,这样的话解决办法就是在子类中重写Fly()方法,但是这种办法太麻烦,需要在不会飞的鸭子子类中都添加重写。

基于上面的问题,有人会提出另一种面向对象的解决思路,那就是在超类中添加一个Fly抽象方法,如下

public abstract void Fly();
这种解决办法的弊端和上面的基本一样,那就是要在会飞的鸭子子类中添加Fly方法的实现。这样的做法就是代码的复用性降低了,如果有十种会飞的鸭子,那么在每一个子类中都要实现此抽象方法,而且是相同的代码。

所以,使用继承的方式,虽然在某些子类上方便了,但是影响了其他的子类,所以代码的复用性降低了,有益处效应。再比如说,后来又有了新的需求,石头鸭子,这就又要覆盖所有的方法 所以,超类挖的一个坑,每一个子类都要填,增加工作量,复杂度为O(N^2)。不是好的设计方式。

那么,我们遇到软件工程问题怎么去分析?

这是一个方法论,并不是策略模式固有的,适用于我们遇到的所有软件工程问题。

1. 首先分析问题里面的变化部分和不变化部分,提取变化部分,抽象成接口+实现

2. 鸭子哪些功能是根据需求变化的。上面的鸭子项目,叫声、飞行都是变化的。


下面我们就按照上面的方法论来重新设计鸭子项目。

新的会飞的需求我们设计成接口+实现的方式,代码如下:

public interface FlyBehavior {
	void fly();
}
写接口的好处,新增行为简单,行为类更好的复用,行为的组合更方便。既有继承带来的复用好处,也没有挖坑。对上面接口实现:

public class GoodFlyBehavior implements FlyBehavior {

	@Override
	public void fly() {
		System.out.println("~~~ good fly ~~~");
	}

}
public class BadFlyBehavoir implements FlyBehavior {

	@Override
	public void fly() {
		System.out.println("~~~ bad fly ~~~");
	}

}
public class NoFlyBehavior implements FlyBehavior {

	@Override
	public void fly() {
		System.out.println("~~~ not fly ~~~");
	}

}

使用同样的方式对叫声进行设计:

public interface QuackBehavior {
	void quack();
}

实现上面接口:

public class GaGaQuackBehavior implements QuackBehavior {

	@Override
	public void quack() {
		System.out.println("~~~ gaga quack ~~~");
	}

}
public class GeGeQuackBehavior implements QuackBehavior{

	@Override
	public void quack() {
		System.out.println("~~~ gege quack ~~~");
	}

}
public class NoQuackBehavior implements QuackBehavior {

	@Override
	public void quack() {
		System.out.println("~~~ not quack ~~~");
	}

}

下面引出策略模式:分别封装行为接口,实现算法族,超类里面放行为接口对象,在子类里具体设定行为对象。原则就是:分离变化部分,封装接口,基于接口编程各种功能。此模式让行为算法的变化独立与算法的使用者。

按照策略模式设计鸭子项目:

public abstract class Duck {
	
	FlyBehavior mFlyBehavior;
	QuackBehavior mQuackBehavior;
	
	public Duck() {
	}
	
	public void fly(){
		mFlyBehavior.fly();
	}
	
	public void quack(){
		mQuackBehavior.quack();
	}
	
	public abstract void display();
	
	public void setQuackBehavior(QuackBehavior quackBehavior){
		mQuackBehavior = quackBehavior;
	}
	
	public void setFlyBehavior(FlyBehavior flyberBehavior){
		mFlyBehavior = flyberBehavior;
	}
	
	public void swim(){
		System.out.println("~~~ duck swim ~~~");
	}
}

注意看超类中代码的变化,我们使用到了接口对象,如果看不懂的童鞋们,需要补习一下基础知识了。然后重新设计绿头鸭:

import com.java.strategymodeduck.flybehavior.GoodFlyBehavior;
import com.java.strategymodeduck.quackbehavior.GaGaQuackBehavior;

public class GreenHeadDuck extends Duck{
	
	public GreenHeadDuck() {
		mFlyBehavior = new GoodFlyBehavior();
		mQuackBehavior = new GaGaQuackBehavior();
	}

	@Override
	public void display() {
		System.out.println("~~~ greenHead ~~~");
	}

}
重新设计红头鸭:

import com.java.strategymodeduck.flybehavior.BadFlyBehavoir;
import com.java.strategymodeduck.quackbehavior.GeGeQuackBehavior;

public class RedHeadDuck extends Duck{
	
	public RedHeadDuck() {
		mFlyBehavior = new BadFlyBehavoir();
		mQuackBehavior = new GeGeQuackBehavior();
	}

	@Override
	public void display() {
		System.out.println("~~~ redHead ~~~");
	}

}

对每种鸭子进行调用实现:

import com.java.strategymodeduck.duck.Duck;
import com.java.strategymodeduck.duck.GreenHeadDuck;
import com.java.strategymodeduck.duck.RedHeadDuck;
import com.java.strategymodeduck.flybehavior.NoFlyBehavior;
import com.java.strategymodeduck.quackbehavior.NoQuackBehavior;

public class StimulateDuck {

	public static void main(String[] args) {
	
		Duck mGreenHeadDuck = new GreenHeadDuck();
		Duck mRedHeadDuck = new RedHeadDuck();
		
		mGreenHeadDuck.display();
		mGreenHeadDuck.fly();
		mGreenHeadDuck.quack();
		mGreenHeadDuck.swim();
		
		mRedHeadDuck.display();
		mRedHeadDuck.fly();
		mRedHeadDuck.quack();
		mRedHeadDuck.swim();
		
		mRedHeadDuck.display();
		mRedHeadDuck.setFlyBehavior(new NoFlyBehavior());
		mRedHeadDuck.fly();
		mRedHeadDuck.setQuackBehavior(new NoQuackBehavior());
		mRedHeadDuck.quack();
		mRedHeadDuck.swim();
	}

}

注意上面的调用,和使用面向对象设计是不一样的。对象类型是Duck,是超类类型,这样的好处就是,我们调用了之后直接调用超类里面的方法了。调用超类里面的方法之后,就到子类里面的行为对象调用这个方法。这样就屏蔽了子类里面的具体实现。也就是说,定义成超类类型的话,他只调用超类的方法的方式,而至于子类怎么实现,他不管。这样就屏蔽了上层的差别性。

策略模式的注意点:
1. 分析项目中变化部分和不变化部分
2. 多用组合少用继承,用行为来组合,而不是行为的继承,更有弹性(变化部分做成行为族之后,要考虑到这些行为之间,相互之间是不是可以替换的,这就是策略模式的特点)
3. 设计模式有没有相应的库直接使用? 有些库或框架本身就用某种设计模式设计的。
没有相应的库直接使用。比如Android中的列表对象,适配器,就是使用模板模式来设计的。
4. 如果找不到适用的模式怎么办?
  1) 对项目分析的还不够透彻
2) 确实遇到了比较奇怪的项目








评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程之艺术

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值