都是被产品逼的!
目前广为流传的设计模式都是前辈们的重要经验,都是在实践中检验的真理。
鸭子游戏是目前最为广泛的策略模式入门。
为什么说策略模式是被产品逼出来的呢?
需求
鸭子可以有外型(display),可以叫(quack),可以游泳(swim)有各种各样的鸭子, eg:绿头鸭(MallardDuck),红头鸭
(RedHeadDuck),橡皮鸭(RubberDuck)…要求采用OO的设计方式
实现这个需求还是很简单的
一、我们先来一个鸭子父类 里面写抽象方法外型(display)和行为 叫(quack),可以游泳(swim)
二、各种鸭子继承父类
三、实现各自的外形
好了 你需求完成了真棒
产品现在来个另外一个需求 我们要鸭子会飞!
没办法 改呗!
Duck父类继续加入一个飞的行为
完工!下班!
产品又来需求了,不过这次来了个比较奇葩的需求。他说橡皮鸭子不能飞!
啊这!这也简单!
子类中将飞的方法重写不就解决了!
产品又双叒叕来需求了,各种鸭子都来了,能飞不能叫,能叫不能飞,不能叫不能飞等等
真实烦死了!!!!!!!!!
总不能一直修改下去!这样继承的意义何在呢?
改用接口?这样所有的代码都无法复用了!简直就是噩梦!!!
因此前辈们有了前车之鉴!换了一种新的方式来实现次功能!!!
策略模式
以鸭子游戏为例
我们先写一个行为的接口
飞
public interface FlyBav {
public void fly();
}
叫
public interface QuackBav {
public void quack();
}
然后实现各种行为
能飞
public class CanFly implements FlyBav{
@Override
public void fly() {
// TODO 自动生成的方法存根
System.out.println("我不能飞!");
}
}
不能飞
public class CanNotFly implements FlyBav{
@Override
public void fly() {
// TODO 自动生成的方法存根
System.out.println("我能飞!");
}
}
能叫
public class CanQuack implements QuackBav{
@Override
public void quack() {
// TODO 自动生成的方法存根
System.out.println("我能叫!");
}
}
不能叫
public class CanNotQuack implements QuackBav{
@Override
public void quack() {
// TODO 自动生成的方法存根
System.out.println("我不能叫!");
}
}
我们会改变的前置需求解决了
我们来写鸭子的父类
父类里声明会变的行为变量
使用构造函数进行初始化 因此我便无需关系鸭子具体能不能飞能不能叫 一切由需求觉定
public abstract class Duck {
//鸭子这两种行为具体是什么我不关心 能不能飞 能不能叫由用户本身决定
//飞的行为
private FlyBav flyBav;
//叫的行为
private QuackBav quackBav;
//构造函数 并且不声明无参构造 即使用次对象必须对确定对象是否能飞能叫
public Duck(FlyBav flyBav, QuackBav quackBav) {
this.flyBav = flyBav;
this.quackBav = quackBav;
}
//子类需要实现dispaly方法
public abstract void display();
//飞的方法 能不能飞委托给FlyBav 由传入的值确定能不能飞
public void fly() {
flyBav.fly();
}
//叫的方法 能不能叫委托给QuackBav 由传入的值确定能不能叫
public void quack() {
quackBav.quack();
}
//鸭子游戏
public void play() {
this.display();
this.fly();
this.quack();
}
}
各种鸭子的子类
绿鸭子
public class GreenDuck extends Duck{
public GreenDuck(FlyBav flyBav, QuackBav quackBav) {
super(flyBav, quackBav);
// TODO 自动生成的构造函数存根
}
@Override
public void display() {
// TODO 自动生成的方法存根
System.out.println("我是一只绿鸭子!");
}
}
橡皮鸭子
public class RubberDuck extends Duck{
public RubberDuck(FlyBav flyBav, QuackBav quackBav) {
super(flyBav, quackBav);
// TODO 自动生成的构造函数存根
}
@Override
public void display() {
// TODO 自动生成的方法存根
System.out.println("我是一只橡皮鸭子!");
}
}
测试类
public class DuckTest {
public static void main(String[] args) {
GreenDuck greenDuck = new GreenDuck(new CanFly(), new CanQuack());
RubberDuck rubberDuck = new RubberDuck(new CanNotFly(), new CanQuack());
greenDuck.play();
rubberDuck.play();
}
}
大功告成!
策略模式的优点
那么策略模式有什么优点呢?
别急!
新的需求来了,产品要一个北京烤鸭!不能飞,不能叫!
怎么办?
简单!直接来一个北京烤鸭的子类就解决了!其余代码完全不需要改动!
最大的优点
有新的需求不需要改动原有的代码,因为只要代码改动了何其相关的一切都需要重新测试!不改动原有代码就是很棒的设计!
实现
北京烤鸭 新的需求来了原有代码完全不需要改动
public class BJDuck extends Duck{
public BJDuck(FlyBav flyBav, QuackBav quackBav) {
super(flyBav, quackBav);
// TODO 自动生成的构造函数存根
}
@Override
public void display() {
// TODO 自动生成的方法存根
System.out.println("我是北京烤鸭!");
}
}
测试类
public class DuckTest {
public static void main(String[] args) {
GreenDuck greenDuck = new GreenDuck(new CanFly(), new CanQuack());
RubberDuck rubberDuck = new RubberDuck(new CanNotFly(), new CanQuack());
BJDuck bjDuck = new BJDuck(new CanNotFly(), new CanNotQuack());
greenDuck.play();
rubberDuck.play();
bjDuck.play();
}
}