编写鸭子项目,具体要求如下 :
1)有各种鸭子(比如 野鸭、北京鸭、水鸭等)鸭子有各种行为,比如叫、飞行等等。
2)显示鸭子的信息
项目进度1:
传统方案解决鸭子问题的分析和代码实现
1)传统的设计方案
public abstract class Duck {
public Duck() {}
// 显示鸭子信息
public abstract void display();
public void quack() {
System.out.println("鸭子嘎嘎叫~~~");
}
public void swim() {
System.out.println("鸭子会游泳~~~");
}
}
public class ReadHeadDuck extends Duck {
@Override
public void display() {
System.out.println("~~ 红头鸭 ~~");
}
}
public class MallardDuck extends Duck {
@Override
public void display() {
System.out.println("绿头鸭");
}
}
项目进度2:
现在,项目做到一半,领导要加需求,要让鸭子可以飞。
没办法,我们得老老实实干活,那么我们在Duck的超类里面加上fly()方法。
public abstract class Duck {
public Duck() {}
// 显示鸭子信息
public abstract void display();
public void quack() {
System.out.println("鸭子嘎嘎叫~~~");
}
public void swim() {
System.out.println("鸭子会游泳~~~");
}
public void fly() {
System.out.println("鸭子会飞~~~");
}
}
但是,刚这样更改完成之后,项目就出现了问题,原因是有一个橡皮鸭子,居然也可以飞了,这显然是不合道理的。出现这个问题的原因是在超类上面添加fly()方法,会导致所有的子类都具备fly(),连那些不应该具备的(例如橡皮鸭子)都可以飞了,我们想想有什么好的办法。
首先我们想到可以重载父类的fly()方法,让其实现为空就可以解决了。
可是,我们又加上了诱饵鸭子,它不会飞也不会叫,如果Duck里面的方法太多了,我们就得一个一个覆盖,太麻烦了。
到此为止,我们发现了继承来提供Duck的行为,会出现很多问题:1.代码在多个子类中重复2.运行时候的行为不容易改变3.很难知道所有鸭子的行为4。改变会牵一发而动全身,造成其它鸭子不想要的改变。
这个时候,我们又想到一个新的办法,将fly()方法抽取成一个接口,类图如下
但是这样以来,重复的代码会变得相当的多,对于相同的fly()行为,我们每一个新的会飞的鸭子都要自己实现,这将相当麻烦。
项目进度3:
设计原则:
- 找出应用中可以需要变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起。
- 针对接口编程,而不是针对实现编程。
现在,我们的策略模式登场,我们将使用我们的设计模式来指导我们来改造我们的系统,来适应这些变化的需求。
策略模式(strategy pattern):
分开变化和不会变化的部分,Duck类的fly和quack()会随着鸭子的不同而改变,为了把这两个行为从鸭子类中分开,我们将其取出,建立一组新的类来代表每个行为。
这样设计的好处就是,可以让飞行和咕咕叫的动作被其它对象复用,因为这些动作已经与鸭子类无关了。而且我们也可以新增加一些行为类,也不会影响到使用飞行行为的鸭子类。,现在,我们可以按照策略模式来整合我们的代码了。
行为接口和部分实现类:
//咕咕叫行为接口
@FunctionalInterface
public interface QuackBehavior {
public void quack();
}
public class FakeQuack implements QuackBehavior {
public void quack() {
System.out.println("Qwak");
}
}
public class MuteQuack implements QuackBehavior {
public void quack() {
System.out.println("<< Silence >>");
}
}
public class Quack implements QuackBehavior {
public void quack() {
System.out.println("Quack");
}
}
public class Squeak implements QuackBehavior {
public void quack() {
System.out.println("Squeak");
}
}
//飞行类行为接口
@FunctionalInterface
public interface FlyBehavior {
public void fly();
}
public class FlyNoWay implements FlyBehavior {
public void fly() {
System.out.println("I can't fly");
}
}
public class FlyRocketPowered implements FlyBehavior {
public void fly() {
System.out.println("I'm flying with a rocket");
}
}
public class FlyWithWings implements FlyBehavior {
public void fly() {
System.out.println("I'm flying!!");
}
}
鸭子超类:
public abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public Duck() {
}
public void setFlyBehavior(FlyBehavior fb) {
flyBehavior = fb;
}
public void setQuackBehavior(QuackBehavior qb) {
quackBehavior = qb;
}
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 class DecoyDuck extends Duck {
public DecoyDuck() {
setFlyBehavior(new FlyNoWay());
setQuackBehavior(new MuteQuack());
}
public void display() {
System.out.println("I'm a duck Decoy");
}
}
/**
* 绿头鸭,可以叫也可以叫
*/
public class MallardDuck extends Duck {
public MallardDuck() {
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
public void display() {
System.out.println("I'm a real Mallard duck");
}
}
/**
* 模型鸭,不能飞但是可以叫
*/
public class ModelDuck extends Duck {
public ModelDuck() {
flyBehavior = new FlyNoWay();
quackBehavior = new Quack();
}
public void display() {
System.out.println("I'm a model duck");
}
}
/**
* 红头鸭,可以飞,也可以叫
*/
public class RedHeadDuck extends Duck {
public RedHeadDuck() {
flyBehavior = new FlyWithWings();
quackBehavior = new Quack();
}
public void display() {
System.out.println("I'm a real Red Headed duck");
}
}
/**
* 橡皮鸭,不能飞,吱吱叫
*/
public class RubberDuck extends Duck {
public RubberDuck() {
flyBehavior = new FlyNoWay();
//quackBehavior = new Squeak();
quackBehavior = () -> System.out.println("Squeak");
}
public RubberDuck(FlyBehavior flyBehavior, QuackBehavior quackBehavior) {
this.flyBehavior = flyBehavior;
this.quackBehavior = quackBehavior;
}
public void display() {
System.out.println("I'm a rubber duckie");
}
}
下面给出测试类:
public class MiniDuckSimulator {
public static void main(String[] args) {
MallardDuck mallard = new MallardDuck();
FlyBehavior cantFly = () -> System.out.println("I can't fly");
QuackBehavior squeak = () -> System.out.println("Squeak");
RubberDuck rubberDuckie = new RubberDuck(cantFly, squeak);
DecoyDuck decoy = new DecoyDuck();
Duck model = new ModelDuck();
mallard.performQuack();
rubberDuckie.performQuack();
decoy.performQuack();
model.performFly();
model.setFlyBehavior(new FlyRocketPowered());
model.performFly();
}
}
注:@FunctionalInterface是函数式接口的注解,表明这个接口有且仅有一个抽象方法
在运行的时候想要改变鸭子的行为,只需要调用鸭子的setter方法即可。在整个新的设计中,我们已经可以动态的设定新的鸭子行为了,在鸭子的设计中,我们将两个行为类结合起来使用,这就是组合。这种做法和“继承”不同的地方在于,鸭子的行为不是继承来的,而是适当的和行为对象“组合”来的。
设计原则:
- 多用组合,少用继承。
设计模式出炉:
概念:定义了算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。
在类结构设计中,我们可以换一个说法,我们将“一组行为”换成“一族算法”,算法代表鸭子可以做的事情,这样我们就可以理解策略模式的概念了,鸭子的行为的变化是可以独立于鸭子类的,完全可以动态设定。
下面是类图:
context:上下文对象,定义了各种算法策略
strategy:定义策略的接口
concreteStrategy:策略的具体实现