一、前言
在面向对象编程中,设计模式是解决常见问题的可重用方案。今天,我将通过经典的SimUDuck问题,向大家展示如何使用策略模式(Strategy Pattern)来设计灵活、可扩展的鸭子模拟程序。
二、问题描述
SimUDuck是一个模拟鸭子行为的程序。最初的设计是有一个Duck超类,各种鸭子(如绿头鸭、红头鸭等)继承这个超类。但随着需求变化,需要让某些鸭子能飞,某些不能飞,某些鸭子发出"嘎嘎"叫,某些发出"吱吱"叫,甚至有些根本不叫。如果简单使用继承来实现这些行为,会导致代码难以维护和扩展。
三、解决方案:策略模式
策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户。
UML类图分析
根据提供的UML图,我们可以看到以下结构:
Duck类:作为所有鸭子的基类,包含两个行为接口的引用(FlyBehavior和QuackBehavior)
具体鸭子类:MallardDuck、RedheadDuck、RubberDuck、DecoyDuck等
行为接口:
FlyBehavior:飞行行为接口
QuackBehavior:叫声行为接口
具体行为实现类:
FlyWithWings、FlyNoWay:飞行行为实现
Quack、Squeak、MuteQuack:叫声行为实现
四、Java代码实现
// 飞行行为接口
public interface FlyBehavior {
void fly();
}
// 具体飞行行为实现
public class FlyWithWings implements FlyBehavior {
@Override
public void fly() {
System.out.println("I'm flying with wings!");
}
}
public class FlyNoWay implements FlyBehavior {
@Override
public void fly() {
System.out.println("I can't fly!");
}
}
// 叫声行为接口
public interface QuackBehavior {
void quack();
}
// 具体叫声行为实现
public class Quack implements QuackBehavior {
@Override
public void quack() {
System.out.println("Quack!");
}
}
public class Squeak implements QuackBehavior {
@Override
public void quack() {
System.out.println("Squeak!");
}
}
public class MuteQuack implements QuackBehavior {
@Override
public void quack() {
System.out.println("<< Silence >>");
}
}
// 鸭子基类
public abstract class Duck {
protected FlyBehavior flyBehavior;
protected QuackBehavior quackBehavior;
public Duck() {}
public abstract void display();
public void performFly() {
flyBehavior.fly();
}
public void performQuack() {
quackBehavior.quack();
}
public void swim() {
System.out.println("All ducks float, even decoys!");
}
// 动态设置行为
public void setFlyBehavior(FlyBehavior fb) {
flyBehavior = fb;
}
public void setQuackBehavior(QuackBehavior qb) {
quackBehavior = qb;
}
}
// 具体鸭子实现
public class MallardDuck extends Duck {
public MallardDuck() {
flyBehavior = new FlyWithWings();
quackBehavior = new Quack();
}
@Override
public void display() {
System.out.println("I'm a real Mallard duck");
}
}
public class RedheadDuck extends Duck {
public RedheadDuck() {
flyBehavior = new FlyWithWings();
quackBehavior = new Quack();
}
@Override
public void display() {
System.out.println("I'm a real Redhead duck");
}
}
public class RubberDuck extends Duck {
public RubberDuck() {
flyBehavior = new FlyNoWay();
quackBehavior = new Squeak();
}
@Override
public void display() {
System.out.println("I'm a rubber duck");
}
}
public class DecoyDuck extends Duck {
public DecoyDuck() {
flyBehavior = new FlyNoWay();
quackBehavior = new MuteQuack();
}
@Override
public void display() {
System.out.println("I'm a decoy duck");
}
}
// 测试类
public class MiniDuckSimulator {
public static void main(String[] args) {
Duck mallard = new MallardDuck();
mallard.performQuack();
mallard.performFly();
Duck model = new DecoyDuck();
model.performFly();
// 动态改变行为
model.setFlyBehavior(new FlyWithWings());
model.performFly();
}
}
五、设计优势
封装变化:将易变的行为(飞行和叫声)封装到单独的类中
多用组合,少用继承:通过组合方式使用行为,而不是继承
针对接口编程:依赖于抽象(接口),而不是具体实现
运行时动态改变行为:通过setter方法可以在运行时改变鸭子的行为
六、总结
策略模式让我们能够定义一系列算法,并将每个算法封装起来,使它们可以互相替换。这种模式让算法的变化独立于使用算法的客户,符合"开闭原则"(对扩展开放,对修改关闭)。在SimUDuck问题中,策略模式完美解决了鸭子行为多样化的问题,使系统更加灵活和可扩展。