Head First 设计模式:(一)策略模式

本文通过设计一个鸭子模拟游戏,探讨了在面临鸭子行为变化时,如何避免过度使用继承并引入策略模式。文章介绍了如何使用Flyable和Quackable接口解决部分问题,但指出这会导致代码复用性降低。接着,文章遵循设计原则,将行为从Duck类中分离,创建FlyBehavior和QuackBehavior接口,并实现具体行为类。最后,通过Duck类的构造器和set方法动态改变鸭子的行为,展示了策略模式如何允许算法在运行时互换,强调了组合优于继承的设计理念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

设计一个名为SimUDuck的鸭子模拟游戏,游戏中会出现各种鸭子,有红头鸭,野鸭,橡皮鸭,分析这些鸭子都会叫,会游泳。

此游戏内部使用标准OO技术,设计一个鸭子超类,并让各种鸭子继承此超类。

简单的实现为:

package com.lissdy;

public class SimUDuck {
   public static void main(String[] args)
   {
	   MallardDuck duck1=new MallardDuck();
	   RedheadDuck duck2=new RedheadDuck();
	   System.out.println("野鸭子出场-----------------");
	   System.out.println(duck1.quack());
	   System.out.println(duck1.swim());
	   System.out.println(duck1.display());
	   System.out.println("红鸭子出场-----------------");
	   System.out.println(duck2.quack());
	   System.out.println(duck2.swim());
	   System.out.println(duck2.display());
   }
}
abstract class Duck
{
	public String quack()
	{
		return "呱呱叫";
	}
	public String swim()
	{
		return "会游泳";
	}
	public abstract String display();
}
class MallardDuck extends Duck
{
	public String display()
	{
		return "外观是绿头";
	}
}
class RedheadDuck extends Duck
{
	public String display()
	{
		return "外观是红头";
	}
}

现在程序增加需求,需要加入会飞的鸭子。而如果在超类中加上fly()就会导致所有鸭子都具备fly(),连同玩具橡皮鸭也无法免除。

如果继续使用继承的话需要将超类中不符合的行为覆盖,例如:

package com.lissdy;

public class SimUDuck {
   public static void main(String[] args)
   {
	   MallardDuck duck1=new MallardDuck();
	   RedheadDuck duck2=new RedheadDuck();
	   RubberDuck duck3=new RubberDuck();
	   System.out.println("野鸭子出场-----------------");
	   System.out.println(duck1.quack());
	   System.out.println(duck1.swim());
	   System.out.println(duck1.display()+" "+duck1.fly());
	   System.out.println("红鸭子出场-----------------");
	   System.out.println(duck2.quack());
	   System.out.println(duck2.swim());
	   System.out.println(duck2.display()+" "+duck2.fly());
	   System.out.println("橡皮鸭出场-----------------");
	   System.out.println(duck3.quack());
	   System.out.println(duck3.swim());
	   System.out.println(duck3.display()+" "+duck3.fly());
	   
   }
}
abstract class Duck
{
	public String quack()
	{
		return "呱呱叫";
	}
	public String swim()
	{
		return "会游泳";
	}
	public String fly()
	{
		return "会飞";
	}
	public abstract String display();
}
class MallardDuck extends Duck
{
	public String display()
	{
		return "外观是绿头";
	}
}
class RedheadDuck extends Duck
{
	public String display()
	{
		return "外观是红头";
	}
}
class RubberDuck extends Duck
{
	public String quack()
	{
		return "吱吱叫";
	}
	public String fly()
	{
		return "不会飞";
	}
	public String display()
	{
		return "橡皮鸭";
	}
}

使用继承的缺点在于每当有新的鸭子子类出现时,就要被迫检查可能需要覆盖的fly()和quark()。

试着利用接口来解决问题,把fly()从超类中取出来,放进一个“Flyable接口”中,这么一来,只有会飞的鸭子实现此接口。同样的方式,也可以设计一个“Quackable接口”,因为不是所有的鸭子都会叫。

 Flyable接口:

package com.lissdy;
public interface Flyable {
   String fly();
}

Quackable接口:

package com.lissdy;
public interface Quackable {
   String quack();
}

代码实现:

package com.lissdy;

public class SimUDuck{
   public static void main(String[] args)
   {
	   MallardDuck duck1=new MallardDuck();
	   RedheadDuck duck2=new RedheadDuck();
	   RubberDuck duck3=new RubberDuck();
	   DecoyDuck duck4=new DecoyDuck();
	   System.out.println("野鸭子出场-----------------");
	   System.out.println(duck1.quack());
	   System.out.println(duck1.swim());
	   System.out.println(duck1.display()+" "+duck1.fly());
	   System.out.println("红鸭子出场-----------------");
	   System.out.println(duck2.quack());
	   System.out.println(duck2.swim());
	   System.out.println(duck2.display()+" "+duck2.fly());
	   System.out.println("橡皮鸭出场-----------------");
	   System.out.println(duck3.quack());
	   System.out.println(duck3.swim());
	   System.out.println(duck3.display());
	   System.out.println("木头鸭出场-----------------");
	   System.out.println(duck4.swim());
	   System.out.println(duck4.display());
	   
   }
}
abstract class Duck
{
	public String swim()
	{
		return "会游泳";
	}
	public abstract String display();
}

class MallardDuck extends Duck implements Quackable,Flyable
{
	public String display()
	{
		return "外观是绿头";
	}
	public String quack()
	{
		return "呱呱叫";
	}
	public String fly()
	{
		return "会飞";
	}
}
class RedheadDuck extends Duck implements Quackable,Flyable
{
	public String display()
	{
		return "外观是红头";
	}
	public String quack()
	{
		return "呱呱叫";
	}
	public String fly()
	{
		return "会飞";
	}
}
class RubberDuck extends Duck implements Quackable
{
	public String quack()
	{
		return "吱吱叫";
	}
	public String display()
	{
		return "橡皮鸭";
	}
}
class DecoyDuck extends Duck
{
	public String display()
	{
		return "木头鸭";
	}
}

执行结果:


并非所有的鸭子都具有飞行和呱呱叫的行为,所以继承并不是适当的解决方式。虽然接口Quackable和Flyable可以解决一部分问题,但是却造成了代码无法复用。

幸运的是,有一种设计原则,恰好适用于此情况。

设计原则:

找出应用中可能变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

 以上设计原则的本质是分开变化和不会变化的部分。

在Duck类中fly()和quack()会随着鸭子的不同而改变,为了把这两个行为从Duck类中分开,需要把它们从Duck类中取出来,建立一组新类来代表每个行为。

 这时遵循第二个设计原则

设计原则:

针对接口编程,而不是针对实现编程。

将鸭子的fly()和quack()行为设计成FlyBehavior接口和QuackBahavior接口,制造一组“行为类”专门实现FlyBehavior接口和QuackBahavior接口。

 

整合鸭子的行为:

1、在Duck类中加入两个实例变量,分别为FlyBehavior接口和QuackBahavior接口。

2、用两个相似的方法performQuack()和performFly()取代Duck类中的fly()和quack()

FlyBehavior接口和QuackBahavior接口:

public interface QuackBehavior {
     String quack();
}
public interface FlyBehavior {
   String fly();
}

改造后的Duck类

abstract class Duck
{
	QuackBehavior quackbehavior;
	FlyBehavior flybehavior;
	public String swim()
	{
		return "会游泳";
	}
	public abstract String display();
	
	public void performFly()
	{
	   flybehavior.fly();
	}
	public void performQuack()
	{
	   quackbehavior.quack();
	}
}

3、用“行为”类实现接口

class Quack implements QuackBehavior
{
	public String quack()
	{
		return "呱呱叫";
	}
}

class FlyWithWings implements FlyBehavior
{
	public String fly()
	{
		return "展翅飞";
	}
}

更多的整合。。。

package com.lissdy;

public class SimUDuck{
   public static void main(String[] args)
   {
	   MallardDuck duck1=new MallardDuck();
	   System.out.println("野鸭子出场-----------------");
	   System.out.println(duck1.performQuack());
	   System.out.println(duck1.swim());
	   System.out.println(duck1.display());
	   System.out.println(duck1.performFly());
   }
}
abstract class Duck
{
	QuackBehavior quackbehavior;
	FlyBehavior flybehavior;
	public String swim()
	{
		return "会游泳";
	}
	public abstract String display();
	public String performFly()
	{
	   return flybehavior.fly();
	}
	public String performQuack()
	{
	   return quackbehavior.quack();
	}

}
class Quack implements QuackBehavior
{
	public String quack()
	{
		return "呱呱叫";
	}
}

class FlyWithWings implements FlyBehavior
{
	public String fly()
	{
		return "展翅飞";
	}
}
class MallardDuck extends Duck 
{
	public MallardDuck()
	{
		quackbehavior=new Quack();
		flybehavior=new FlyWithWings();
	}
	public String display()
	{
		return "外观是绿头";
	}
}


当MallardDuck实例化时,它的构造器会把继承来的quackBehavior实例变量初始化成Quack类型的新实例(Quack是QuackBehavior的具体实现类),但是初始化实例变量的做法依然不够弹性。如果鸭子想在运行时动态的改变自己的行为,可以通过set来设定鸭子的行为,而不是在鸭子的构造器内实例化。
 在Duck类中加入两个新方法:

         public void setFly(FlyBehavior fb)
	{
		flybehavior=fb;
	}
	public void setQuack(QuackBehavior qb)
	{
		quackbehavior=qb;
	}

可以随时调用这两个方法改变鸭子的行为。

例如:

class MallardDuck extends Duck 
{
	public MallardDuck()
	{
		quackbehavior=new Quack();   //在构造器中设定行为
		flybehavior=new FlyWithWings();
	}
	public void display()
	{
		System.out.println("外观是绿头");
	}
}

 

public class SimUDuck{
   public static void main(String[] args)
   {
	   Duck duck1=new MallardDuck();
	   System.out.println("野鸭子出场-----------------");
	   duck1.performQuack();
	   duck1.swim();
	   duck1.display();
	   duck1.performFly();
        	   duck1.setFly(new FlyNoWay());  //动态的改变飞行行为
	   duck1.performFly();
   }
}

野鸭子由展翅飞到不会飞,动态的改变了它的飞行行为。在运行时想改变鸭子的行为,只需要调用set方法就可以了。
可以把鸭子的“一组行为”想象成“一族算法”,算法代表着鸭子能做的事。

将鸭子类和行为类结合起来使用,就是组合。这种做法与继承的不同地方在于鸭子的行为不是继承来的,而是和适当的行为对象“组合”来的。

设计原则:

多用组合,少用继承。

本例使用的设计模式为策略模式,其定义为:

策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值