策略模式学习2

接着我的上一篇文章策略模式学习1。

让我们来回顾一下上面设计中橡皮鸭类的代码,并提出一下问题。

/**
 * 橡皮鸭
 * @author Administrator
 *
 */
public class RubberDuck extends Duck {
	
	@Override
	void display() {
		System.out.println("橡皮鸭...");
	}
	
	/*
	 * 橡皮鸭子不会呱呱叫
	 * 但是问题来了,这个橡皮鸭子会飞?
	 * 那设计人员会说:一样覆盖掉就好
	 */
	@Override
	public void quack() {
		//覆盖超类的呱呱叫
		System.out.println("吱吱叫...");
	}
	
	@Override
	void fly() {
		//覆盖,变成什么事儿都不做
		;
	}
	
	/*
	 * 那么问题来了,以后每个月更新,常常会出现新的鸭子子类,这要被迫每次检查
	 * 并可能覆盖quack() 和 fly() 这简直就是无穷无尽的噩梦!!!!
	 * 这样设计的缺点:
	 * 1.代码在多个子类中重复
	 * 2.运行时的行为不容易被改变
	 * 3.改变会牵一发动全身,造成其他鸭子不想要的改变
	 * 4.很难知道鸭子的全部行为,因为有了不属于它本身的行为。
	 */
	
	//重复代码变多,那么你认为覆盖几个方法就算是差劲,那么对于48个Duck子类都要稍微修改一下
	//飞行的行为,你又怎么说?需求:会飞的鸭子中,飞的动作可能有多种变化.
	

}

因此我们可以看到,利用继承来提供Duck的行为,会导致以上所述的问题以及缺点。
那利用接口如何?将飞行方法和呱呱叫的方法做成一个接口,分别让具备这两个行为的鸭子实现这个接口,就能够解决不再会有会飞的鸭子。
但是缺点是:造成了代码无法复用(虽然鸭子类中实现了飞行的方法,
但是这个鸭子在某个场景下又想要其他类型的飞行方法呢?这有点类似于游戏中的升级,解锁了某技能或在某个副本中,会以新的行为出现)。这又跳进了另外一个坑。

另外,如果飞行的动作可能还有多种变化呢?比如火箭式飞行,螺旋式飞行等等。

软件开发的一个不变的真理:改变(change).不管软件设计得多好,一段时间之后,总是需要成长和改变,否则软件就会“死亡”。

利用Flyable和Quackable一开始似乎还不错,解决了只有会飞的鸭子才实现Flyable,但是java接口不具有实现代码,所以实现接口无法达到代码的复用。这就意味着无论何时你需要修改某个行为,你必须得往下追踪到并在每一个定义此行为的类中修改它,一不小心,可能会造成新的错误。

幸运的是,这里有一个设计原则:

找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。换句话说:如果每次新的需求一来,都会使某方面的代码发生变化,那么你就可以确定,这部分的代码需要被抽出来,和其他稳定的代码有所区分。对这个原则的另外一种思考方式:把会变化的部分取出并封装出来,以便以后可以轻易地改动或扩充此部分,而不影响不需要变化的其他部分。

我们知道Duck类的fly()和quack()会随着鸭子的不同而改变。因此我们把这两个行为从Duck类中分离出来,建立一组新类来代表每个行为。

如何设计鸭子的行为?我们想要产生一个新的绿头鸭实例,并指定特定的“类型”的飞行行为给它。那么干脆顺便让鸭子的行为可以动态的改变好了。换句话说:我们应该在鸭子类中包含设定行为的方法,这样就可以在“运行时”动态地“改变”绿头鸭的飞行行为。

设计原则:针对接口编程,而不是针对实现编程。

public abstract class Duck {
	FlyBehavior flyBehavior;
	QuackBehavior quackBehavior;
	// 行为变量被声明为行为 "接口" 类型
	public void swim() {
		System.out.println("游来游去...");
	}
	
	abstract void display();
	//鸭子对象不亲自处理呱呱叫行为,而是委托给quackBehavior引用的对象去执行
	void performQuack() {
		quackBehavior.quack();
	}

	void performFly() {
		flyBehavior.fly();
	}

	public FlyBehavior getFlyBehavior() {
		return flyBehavior;
	}

	public void setFlyBehavior(FlyBehavior flyBehavior) {
		this.flyBehavior = flyBehavior;
	}

	public QuackBehavior getQuackBehavior() {
		return quackBehavior;
	}

	public void setQuackBehavior(QuackBehavior quackBehavior) {
		this.quackBehavior = quackBehavior;
	}
	
	/*public void quack() {
		System.out.println("呱呱叫...");
	}
	
	void fly(){
		System.out.println("飞来飞去...");
	}*/
	
	//鸭子的其他方法
}

public interface FlyBehavior {
	void fly();
}

public class FlyWithWings implements FlyBehavior {

	@Override
	public void fly() {
		System.out.println("这里实现了所有有翅膀的鸭子飞行动作...");
	}

}

public class FlyNoWay implements FlyBehavior {

	@Override
	public void fly() {
		//什么都不做,不会飞
		System.out.println("I can't fly!");
	}

}

public class FlyByRocky implements FlyBehavior {

	@Override
	public void fly() {
		// 新增了一个火箭式的飞行
		System.out.println("火箭式的飞行...");

	}

}

public interface QuackBehavior {
	void quack();
}

public class Quack implements QuackBehavior {

	@Override
	public void quack() {
		// 实现鸭子呱呱叫
		System.out.println("呱呱叫...");

	}

}

public class Squeak implements QuackBehavior {

	@Override
	public void quack() {
		// 橡皮鸭子吱吱叫
		System.out.println("吱吱叫...");
	}

}

public class MallardDuck extends Duck {

	@Override
	void display() {
		System.out.println("绿头鸭..");

	}

}

public class ModelDuck extends Duck {
	
	public ModelDuck(){
		this.flyBehavior = new FlyNoWay();
		this.quackBehavior = new MuteQuack();
	}
	@Override
	void display() {
		// 这是一只模型鸭
		System.out.println("模型鸭...");
	}
	
	

}

// 新增一个鸭鸣器,用来捕获其他真正鸭子
public class DuckCall implements QuackBehavior{
	QuackBehavior quack;
	
	public QuackBehavior getQuack() {
		return quack;
	}

	public void setQuack(QuackBehavior quack) {
		this.quack = quack;
	}

	@Override
	public void quack() {
		quack.quack();
	}

	

}
//测试类
public class MiniDuckSimulator {

	public static void main(String[] args) {
		/*Duck mallard = new MallardDuck();
		mallard.setFlyBehavior(new FlyWithWings());
		mallard.setQuackBehavior(new Quack());
		mallard.performFly();
		mallard.performQuack();*/
		
		/*Duck modelDuck = new ModelDuck();
		modelDuck.performFly();
		modelDuck.setFlyBehavior(new FlyByRocky());//模型鸭可以动态地改变飞行行为
		modelDuck.setQuackBehavior(new MuteQuack());
		modelDuck.performFly();*/
		
		//实现一个鸭鸣器用来诱捕野鸭
		DuckCall duckCall = new DuckCall();
		duckCall.setQuack(new Quack());
		duckCall.quack();//利用多态,可以动态的模拟各种叫声
	}

}

public class MuteQuack implements QuackBehavior {

	@Override
	public void quack() {
		// 什么都不做,不会叫
		;
	}

	/*
	 * 这样设计,可以让飞行和呱呱叫的动作被其他的对象复用,因为这样行为已经与鸭子无关了
	 * 而我们可以新增一些行为,不会影响到既有的行为类,不会影响“使用”到飞行行为的鸭子类了。
	 * 有了继承的“复用”的好处,却没有了继承所带来的包袱
	 */
}

这里也用到另外一个设计原则:多用组合,少用继承。

“有一个”可能比“是一个”更好。有一个关系相当有趣,每个鸭子都有一个FlyBehavior和QuackBehavior,当你将两个类结合起来使用,如通本例一般,这就是用到了组合。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章