###使用场景 在小的系统设计中,想要对现有的系统进行拓展的时候(比如一个动物类,随着时间的发展,要在该类上加上特定的表现行为出现),我们一般会使用继承,但是当系统日益庞大的时候(有越来越多的行为出现),对超类的设计的改变就会导致所有的子类都需要去跟着改变,当有非常的子类时,那么这个改动量就会无比的蛋疼。下面用代码来进行演示。
现在有一个鸭子超类,系统中所有的鸭子都继承自该类。鸭子有很多功能:叫的功能,游泳功能,显示外观功能。
比如我们现在需要给 Duck 类新增一个飞行功能即在超类中增加一个飞行功能,但是问题来了,并不是所有的鸭子都会飞,比如说木鸭子,那么在木鸭子的子类当中,我们就要覆盖一个空动作的 fly() 方法,并且我们要检查所有的子类,在每个子类当中分别去实现不同的 fly() 方法。
这时我们想到,是不是可以把 fly() 方法设计成一个接口,在接口里面定义一个抽象方法 fly() ,但是这样做,在实现此接口的类中我们还是要检查每个鸭子的飞行方式然后去重写这个方法。
设计模式的思想是:将系统中经常变化的部分抽取出来,然后封装起来,尽量让其他部分不受影响。
按照此思想,我们将 Duck 类经常改变的行为( 飞行行为,叫行为 )抽取出来做成接口,由具体的飞行类去实现此接口而不是由鸭子子类去实现这个接口里面的行为,避免了向上面由鸭子子类直接实现接口那种情况下每个鸭子子类都要去重写方法的麻烦,而鸭子子类只需要考虑有这两个行为就行了。下面是 UML 图。
重新设计超类:
飞行行为接口:
###代码演示 鸭子超类
public abstract class Duck{
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public void performFly(){
flyBehavior.fly();
}
public void performQuack(){
quackBehavior.quack();
}
public void setFlyBehavior(FlyBehavior flyBehavior){
this.flyBehavior=flyBehavior;
}
public void setQuackBehavior(QuackBehavior quackBehavior){
this.quackBehavior=quackBehavior;
}
public abstract void display();
}
复制代码
飞行行为接口
public interface FlyBehavior{
public void fly();
}
复制代码
叫行为接口
public interface QuackBehavior{
public void quack();
}
复制代码
实现飞行行为的类1(用翅膀飞)
public class FlyWithWings implements FlyBehavior{
public void fly(){
System.out.println("I'm fly with wings!");
}
}
复制代码
实现飞行行为的类2(插火箭飞)
public class FlyWithRock implements FlyBehavior{
public void fly(){
System.out.println("I'm fly with Rock!");
}
}
复制代码
实现叫的行为的类1(Gaga叫)
public class Gaga implements QuackBehavior{
public void quack(){
System.out.println("Gaga!");
}
}
复制代码
实现叫的行为的类2(不会叫)
public class Mute implements QuackBehavior{
public void quack(){
System.out.println("Silence!");
}
}
复制代码
实例化一个鸭子类(用翅膀飞会gaga叫)
public class DuckGagaWings extends Duck{
public DuckGagaWings(){
this.flyBehavior=new FlyWithWings();
this.quackBehavior=new Gaga();
}
public void display(){
System.out.println("I'm a duck with Gaga and Wings ");
}
}
复制代码
编写测试类
public class DesignModel{
public static void main(String[] args) {
Duck duckGagaWings=new DuckGagaWings();
duckGagaWings.display();
duckGagaWings.performFly();
duckGagaWings.performQuack();
}
}
复制代码
输出结果:
I'm a duck with Gaga and Wings
I'm fly with wings!
Gaga!
复制代码
同时我们可以发现在运行的时候如果想动态的改变鸭子的飞行和叫的行为,我们可以在测试类中去调用setFlyBehavior()
方法,当有不同的飞行方法时,我们只需要去新增一个实现该行为接口的类即可。