首页 > 学院 > 开发设计 > 正文

策略模式学习2

2019-11-08 02:09:37
字体:
来源:转载
供稿:网友

接着我的上一篇文章策略模式学习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,当你将两个类结合起来使用,如通本例一般,这就是用到了组合。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表