第二篇:策略模式

场景:你的公司最近要做一个飞机模型展览项目,该项目可以向客户展示各种各样的飞机,项目第一期可以确定的是这些飞机都有不同的外观,都可以发出声波,都可以飞行;好了,现在我们就知道这些,让我们开干吧!
(你该不会去纠结模型展览的飞机为什么会发声波还会飞行吧?...)
首先,用OO思维,我们可以先总结出一个父类:

/**所有飞机模型的父类*/
public abstract class Flyable {
	
	/**发声波...*/
	protected void voice(){
		System.out.println("嗡...嗡...嗡...");
	}

	protected void fly(){
		System.out.println("我先直线加速...");
		System.out.println("我再拉起操纵杆...");
		System.out.println("然后...飞呀飞呀 我的骄傲放纵...");
	}
	/**子类必须自己去绘制自己的外观*/
	protected abstract void facade();
}

接下来让我们创造两架飞机:

/**一架喷气机*/
class GasPlane extends Flyable{
	@Override
	protected void facade() {
		System.out.println("我是一家绿绿的喷气机");
	}	
}

/**一架客运机*/
class GuestPlane extends Flyable{
	@Override
	protected void facade() {
		System.out.println("白白的长长的...");
	}	
}

好啦,现在看起来一切都那么的完美,两架飞机只需要简单的绘制自己的外观,至于飞行和声波的功能,父类都给我们啦!瞧,代码多省事儿!

过了一个礼拜...新的需求还需要加入一架直升机,你马上开始写代码!

/**一架直升机*/
class CopterPlane extends Flyable{
	@Override
	protected void facade() {
		System.out.println("很炫的迷彩...");
	}
	
	/**注意,这里必须覆盖父类的飞行方法,因为直升机是螺旋桨飞行,不是跑道提速...*/
	@Override
	protected void fly() {
		System.out.println("我先启动引擎");
		System.out.println("然后我再让我的小翅膀转呀转...");
		System.out.println("飞呀飞呀...");
	}
}


好吧,这里覆盖了父类的方法,但看起来好像也没事儿...但是,我们要记住一点,当你的大多数子类都要去完全覆盖父类的方法时,你就要警觉了,你的设计很可能有问题!比较父类的方法它更多的是要达到统一复用的目的!


又过了几天...需求还要加入一个木头做的模型飞机!”

...刷刷刷...代码写好了...”

class WoodPlane extends Flyable{

	@Override
	protected void facade() {
		System.out.println("红色的木...");
	}
	
}

这个代码有问题吗??当然有!木头做的模型机是不能飞的!但是它通过继承了Flyable,却拥有了飞行的能力!OK,那赶紧改代码吧!

class WoodPlane extends Flyable{

	@Override
	protected void facade() {
		System.out.println("红色的木...");
	}
	
	/**注意:这里覆盖父类的飞行方法,并且什么都不做,因为木头模型飞机不能飞!
	 * 我们不去关注木头飞机不能发出声波这个问题,如果关注这个点,后面我将要写更多的类...*/
	@Override
	protected void fly() {
		
	}
	
}

好了,是时候说点什么了!你看出这个设计有什么问题了吗???没有的话我来给你讲讲吧!

首先,父类拥有飞行方法,所有的子类理论上都应该能飞行,但是,我们看到,直升机完全覆盖父类的方法,虽然在使用这个类的客户端看来它应该是像父类所描述的那样,在跑道加速,然后拉起操纵杆,然后起飞...但它实际上却完全不是这样走的。然后,我们的WoodPlane模型飞机看起来是能够飞的,但它实际上却直接覆盖父类方法,从而使自己失去飞行能力,这在使用这个类的人来是很诡异的;

好吧,那我们怎么来改进这个程序的设计呢?要不,既然飞行方法会随着子类的变化而一直变,有的会飞有的还不会飞,那我们就设计一个接口,如果会飞的就实现这个接口,否则就不实现,这样对客户端来说不就一目了然了吗?

注:接口就是用来规划和描述一个对象应该会具有怎样的能力!面向对象设计原则,依赖抽象(接口或抽象类),不依赖具体实现;

如果用接口设计的话,又有什么问题呢?好吧,首先,我们得把父类Flyable的fly()方法去掉,这样所有子类都失去了飞行的能力,然后让我们的“喷气机”,“客运机”,“直升机” 分别去实现我们的接口,而“木头模型机”因为不具备飞行的能力,所以不去实现该接口;但是!问题来了,我们的喷气机辉和客运机它们的飞行方法都是一致的...或者说后期我们还要加入各种各样的飞机,它们的飞行方法都是一致的,但是呢!他们却都要去实现飞行方法,造成我们的代码不能复用!多处复制黏贴!!!

那我们有什么办法既能将飞行方法分离出来,又能让代码复用呢???当当当当!策略模式闪亮登场!

策略模式核心:将会变化的部分抽取出来,这些部分我们可以看成是一个行为,比如飞行就是一个行为 ,抽取出来后形成一个个的策略对象,所有需要的能力都委托给策略对象去执行!

在这里,我们会变得地方是fly()飞行方法,那么OK,进行代码改造吧!

首先,增加一个策略接口,它表示具有飞行这个行为,再造几个拥有不同飞行方式的策略类;

/**飞行行为接口*/
interface FlyBehavioer{
	void fly();
}

/**拥有直线跑道加速...这种飞行能力的类*/
class LineFlyBehavioer implements FlyBehavioer{

	@Override
	public void fly() {
		System.out.println("我先直线加速....");
		System.out.println("我再拉起操纵杆...");
		System.out.println("然后...飞呀飞呀  我的骄傲放纵...");
	}
	
}

/**拥有螺旋桨盘旋飞行...这种飞行能力的类*/
class CircleFlyBehavioer implements FlyBehavioer{

	@Override
	public void fly() {
		System.out.println("我先启动引擎");
		System.out.println("然后我再让我的小翅膀转呀转...");
		System.out.println("飞呀飞呀...");
	}

}

/**不会飞的...所以是一个空实现,什么都不做*/
class NonFlyBehavioer implements FlyBehavioer{
	
	@Override
	public void fly() {
		
	}
	
}

然后,改造我们的Flyable父类

public abstract class Flyable {
	//依赖于抽象
	protected FlyBehavioer flyBehavioer;
	
	//默认是不具备飞行能力的
	public Flyable() {
		this(new NonFlyBehavioer());
	}
	
	public Flyable( FlyBehavioer flyBehavioer ) {
		this.flyBehavioer = flyBehavioer;
	}
	
	protected void voice(){
		System.out.println("嗡...嗡...嗡...");
	}
	
	/**注意,这里委托给策略类去进行具体的飞行实现*/
	protected void toFly(){
		flyBehavioer.fly();
	}
	
	protected abstract void facade();
	
	/**可以在运行时动态改变飞行行为*/
	public void setFlyBehavioer( FlyBehavioer flyBehavioer ){
		this.flyBehavioer = flyBehavioer;
	}
	
}

然后对我们的“喷气机”,“客运机”,"直升机","木头模型机" 进行改造:

/**一架喷气机*/
class GasPlane extends Flyable{
	
	public GasPlane() {
		//初始化具备直线飞行能力
		super( new LineFlyBehavioer() );
	}
	
	@Override
	protected void facade() {
		System.out.println("我是一家绿绿的喷气机");
	}
	
}

/**一架客运机*/
class GuestPlane extends Flyable{
	
	public GuestPlane() {
		//初始化具备直线飞行能力
		super( new LineFlyBehavioer() );
	}
	
	@Override
	protected void facade() {
		System.out.println("白白的长长的...");
	}
	
}

/**一架直升机*/
class CopterPlane extends Flyable{

	public CopterPlane() {
		//初始化具备盘旋飞行能力
		super( new CircleFlyBehavioer() );
	}
	
	@Override
	protected void facade() {
		System.out.println("很炫的迷彩...");
	}

}

//一架木头模型机
class WoodPlane extends Flyable{

	@Override
	protected void facade() {
		System.out.println("红色的木...");
	}
	
}


终于到了测试我们代码的时候了!

public static void main(String[] args) {
		GasPlane gp = new GasPlane();
		gp.toFly();
		System.out.println("****************************");
		GuestPlane gtp = new GuestPlane();
		gtp.toFly();
		System.out.println("****************************");
		CopterPlane cp = new CopterPlane();
		cp.toFly();
		System.out.println("****************************");
		WoodPlane wp = new WoodPlane();
		wp.toFly();
		//让我们动态给木头飞机配上飞行的能力吧!
		System.out.println("****************************");
		wp.setFlyBehavioer(new CircleFlyBehavioer());
		wp.toFly();
	}

看下我们的输出结果!

我先直线加速....
我再拉起操纵杆...
然后...飞呀飞呀  我的骄傲放纵...
****************************
我先直线加速....
我再拉起操纵杆...
然后...飞呀飞呀  我的骄傲放纵...
****************************
我先启动引擎
然后我再让我的小翅膀转呀转...
飞呀飞呀...
****************************
为了看效果...我还是打印一下吧!我是飞不了的!!!
****************************
我先启动引擎
然后我再让我的小翅膀转呀转...
飞呀飞呀...



Ok,改造完成,让我们看看发生了什么变化!

我们增加了接口,增加了几个接口实现,然后所有飞机的飞行行为都委托给接口实现类去进行展示,这样有什么好处呢? 后期我们想要不同的飞行能力,只需要增加新的实现类就可以做到了!我们的”飞行“这个变化维度彻底与飞机解耦且可以进行代码复用,这对我们后期的扩展有非常大的帮助,我们想要改变飞行行为,压根就不需要去碰飞机实体了,因为你只需要执行setFlyBehavioer()就可以做到!虽然我们前期看似增加了代码量,但对于后期的维护与扩展来说,都是值得的!

最后,我们来看一下运用策略模式的类图;如果有疑问之处,给我留言喔!





  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值