引入
我们想设计一个小鸭子的类型,但是有些鸭子比如浴池里的小黄鸭它不会飞,纪念品店的小木鸭不会叫。各种各样的小鸭子们也有相似之处,如外形。
如果我们把这些行为都放在父类或都声明接口,很明显会增加代码量,很多代码无法复用,鸭子们虽然可能会叫得千姿百态,但明显不会飞得千姿百态。
解决办法就是使用良好的OO软件设计原则,针对有些行为会变有些行为固定的需求就采用针对接口编程。
设计原则
- 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
- 针对接口编程,而不是针对实现编程。
- 多用组合,少用继承。
我们可以设计FlyBehavior与QuackBehavior接口表示行为,再用类去具体实现。这样可以复用,甚至鸟类的飞行行为我们也可以实现了,这些行为与鸭子类无关。
针对接口编程
其实其真正意义是针对超类型编程,其关键在于多态。
利用多态,程序可以针对超类型编程,执行时会根据实际情况执行到真正的行为,不会被绑死在超类型的行为上。
变量的声明类型应该是超类型,通常是一个抽象类或者是一个接口,如此,只要是具体实现此超类型的类所产生的对象,都可以指定给这个变量。这意味着声明类时不用理会以后执行时真正对象类型
具体实现
- 如下要求我们必须实现具体编码
Dog d = new Dog();
d.bark();
- 使用针对接口/超类型编程
Animal animal = new Dog();
animal.makeSound();
子类实例化的动作不需要在代码中硬编码。而是在运行时才指定具体实现的对象
a = getAnimal();
a.makeSound();
- 整合 鸭子父类
public class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public void performQuack() {
quackBehavior.quack();
}
}
具体的Mallard鸭子
public class MallardDuck extends Duck {
public MallardDuck() {
quackBehavior = new Quack();
flyBehaivor = new FlyWithWings();
}
}
虽然这里创建了Quack和FlyBehavior的实例,想要不具体实现就需要后续学习更多设计模式原理。
完整实例
- 抽象父类——
Duck类
public abstract class Duck {
FlyBehavior flyBehavior;
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 duck float, even decoys");
}
}
FlyBehavior接口及其两个实现类
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");
}
}
QuackBehavior接口及其三个实现类
public interface QuackBehavior {
public void quack();
}
public class Quack implements QuackBehavior {
public void quack() {
System.out.println("Quack");
}
}
public class MuteQuack implements QuackBehavior {
public void quack() {
System.out.println("<< Silence >>");
}
}
public class Squeak implements QuackBehavior {
public void quack() {
System.out.println("Squeak");
}
}
- 测试
public class MiniDuckSimulator {
public static void main (String[] args) {
Duck mallard = new MallardDuck();
mallard.performFly();
mallard.performQuack();
}
}
动态设定行为
(我想到了setter注入)\
- 在
Duck类里引入两个新方法
public void setFlyBehavior(FlyBehavior fb) {
flyBehavior = fb;
}
public void setQuackBehavior(QuackBehavior qb) {
quackBehavior = qb;
}
- 构造一个模型鸭类
public class ModelDuck extends Duck {
public ModelDuck() {
flyBehavior = new FlyNoWay();
quackBehavior = new Quack();
}
public void display() {
System.out.println("I'm a model duck");
}
}
- 创建新的
FlyBehavior类
public class FlyRocketPowered implements FlyBehavior {
public void fly() {
System.out.println("I'm flying with a rock!");
}
}
- 测试
Duck model = new ModelDuck();
model.performFly();
model.setFlyBehavior(new FlyRocketPowered());
model.performFly();
利用setter方法改造鸭子
小结
根据这个实例可以再次理解一下IS-A、HAS-A和IMPLEMENTS这三个关系
组合建立系统具有很多的弹性,不仅可以将算法封装成类,更可以动态地改变行为,只要组合行为对象符合正确的接口标准即可。
致力于系统维护和变化,提高可维护性和可扩展性。