学习设计模式的小随笔:)
我们为什么要采取设计模式?
在《Head First设计模式》中,通过鸭子的例子展示了出来;
我们新建Duck类,制定Duck应该有的行为,如果把所有的行为都直接写在Duck下面:
- quack() 叫
- swim() 游泳
- display() 样貌
- fly() 飞行,问题也就出在这里
问题: 各种鸭子通过继承 Duck类实现,如果我们的子类都是真正的鸭子当然没问题,如果此时我们新建一个橡皮鸭,它并不会飞,但是却继承到了fly();
如果通过接口解决,将fly( )
从超类中提取出来,放进Flyable
接口,会飞的鸭子才要去实现接口,同理Quackable
接口,因为我们会遇到木头鸭又不会叫又不会飞;
问题: 笨蛋主意,J由于ava接口是不能写函数内容的,这样一来没有fly( ), quack( )
代码可以复用了,如果我们要写48个会飞的Duck子类,就要重新写48次fly( )
;
设计原则:找出应用中可能需要变化之处,将其独立出来,与不需要变化的代码分离
fly, quack
需要从Duck中分离开来,我们将其分别新建Fly类、Quack类;
而Fly如果单独作为独立的类又涉及了怎么飞,因为但凡涉及到飞的动作都可以在这里实现,比如用翅膀飞、火箭飞行、不会飞,为了使我们的设计更具有弹性,我们将Fly设定成接口<interface> FlyBehavior
,其中含有抽象函数fly( )
,具体的实现再通过新建类FlyWithWings、FlyNoWay去实现接口。同理QuackBehavior;
设计原则:针对接口(超类型)编程,而不是针对实现编程
如果按照Duck超类内写fly( )
,或者是子类去继承某个接口再自行实现fly( )
方法,这样都过度依赖于实现,
针对接口(超类型)编程,更明确的说是变量的声明类型应该是超类型(通常是一个抽象类或者是一个接口)。如此,只要是具体实现此超类型的类所产生的对象,都可以指定给这个变量(类似向上转型,是这个意思吧);
通过猫狗实例说明:
如果针对实现编程,声明变量详细到子类,必须使用具体的实现编码,Dog类的实例只能bark()
:
Dog d = new Dog();
d.bark();
如果是针对接口/超类型编程,我们知道声明的对象是狗,但是利用了animal的多态调用,子类的实例化动作不需要再代码中硬编码,而是在运行时才指定具体的实现对象:
Animal animal = new Dog();
animal.makeSound();
这样我们就不用关心实际的子类型是“什么“了,只需要关心如何进行抽象类中的makeSound()
就好了;
鸭子总结
为了实现鸭子的Fly和Quack,我们独立了两个接口FlyBehavior和QuackBehavior,之后再通过具体的类去实现具体的行为。
这样的设计可以让飞行和呱呱叫的动作被其他类型的对象复用,既有了继承的复用好处,也没有继承所带来的包袱;
-21.7.15
Duck类中引入成员变量:声明为“接口类型”的行为Flybehavior flyBehavior, QuackBehavior quackBehavior。再加上进行行为的两个行为函数performQuack(), performFly()替代fly(), quack();
Duck类编写:
public abstract class Duck {
//接口成员
FlyBehavior flyBehavior;
Quackbehaior 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!");
}
}
MallardDuck子类编写:
public class MallardDuck extends Duck {
public MallardDuck() {
//因为MallardDuck子类继承自Duck类,具有Duck类中的flyBehavior与quackBehavior实例变量;
//在这里子类的具体特性赋值进去;
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
public void display() {
System.out.println("I'm a real Mallard duck!");
}
}
MallardDuck绿头鸭子他的叫声可能是“呱呱叫”,不是”吱吱叫“或者“不会叫”,这个是在MallardDuck实例化时,构造器函数将具体的实现类FlyWithWings赋值进去;
飞行接口及具体实现类:
public interface FlyBehavior {
public void fly();
}
public class FlyWithWings implements FlyBehavior {
public void fly() {
System.out.println("I'm flying!!");
}
}
public class FlyNoWay implements FlyBehavior {
public void fly() {
System.out.println("I can't fly");
}
}
Quark接口及具体实现类:
public interface QuackBehavior {
public void quark();
}
public class Quack implements QuackBehavior {
public void quack() {
System.out.println("Quack");
}
}
public class MuteQuak implements QuackBehavior {
public void quark() {
System.out.println("<< Silence >>");
}
}
public class Squeak implements QuackBehavior {
public void quark() {
System.out.println("Squeak!!");
}
}
测试类:
public class MiniDuckSimulator {
public static void main(String[] args) {
//声明类是超类Duck,实现类才是具体类;
//并且由于子类的构造函数,接口成员已经被具体实现类赋值:quackBehavior = new Quack(),flyBehavior = new FlyWithWings();
Duck mallard = new MallardDuck();
mallard.performQuack();
mallard.performFly();
}
}
运行起来:
调用示意图:
-21.7.16