Design Patterns: 入门

入门

所谓设计模式,其实就是前辈们的经验总结。虽然看起来使用设计模式会使得代码量变多,但是实际上这是从平地到万丈高楼的过程,只有框架搭好了,后面需求再怎么变化,系统结构都不需要改动。

一个简单的例子

基本OOP

假如我们想要实现各种不同的鸭子。

为了实现代码复用,按照继承的思想,我们肯定会先抽象出一个超类Duck,然后把那些共有的属性和行为放到这个类里,比如swim方法。

为了实现各种不同的鸭子,按照多态思想,只需要对其进行派生各种子类,然后在各个子类中覆写需要的方法,比如实现各种swim()

使用接口

这样实现看起来没有问题,但是如果增加了需求,比如有些鸭子能飞。

这时就不能把fly方法放到超类里,因为不是所有鸭子都会飞。于是想到用一个接口Flyable,只有实现了这个接口的鸭子才会飞。

新的需求看起来解决了,但是使用接口不能复用代码。子类一多,就会有很多重复的代码。要是fly的需求又变了,就需要改所有子类里重复的fly代码!

提取行为

为了继续实现代码复用,我们需要将这样总是变化的部分单独提取出来,也就是把fly方法的具体实现放到别的类里而不是写在每个鸭子类中。这些类都统一实现了一个接口FlyBehaviour

1
2
3
public interface FlyBehaviour{
public void fly();
}

以后要实现不同的飞行行为,就对这个接口进行实现。比如FlyWithWings

1
2
3
4
5
public class FlyWithWings implements FlyBehaviour{
public void fly(){
System.out.println("I'm flying with wings!");
}
}

如果是不能飞的行为,那么就把fly方法实现为什么都不做就行了。

组合实体与行为

现在已经可以自由实现各种行为了,各种鸭子类要用到飞行行为的时候就只需要绑定一个具体实现的引用即可。

这个引用是鸭子类都共有的,先在Duck超类中定义抽象行为

1
2
3
4
5
6
7
public abstract class Duck{
FlyBehaviour flyBehaviour;

public void performFly(){
flyBehaviour.fly();
}
}

然后在子类构造方法中实例化特定的行为并保存一个引用,比如WingDuck

1
2
3
4
5
public class WingDuck extends Duck{
public WingDuck(){
flyBehaviour = new FlyWithWings();
}
}

这样一来,所有种类的鸭子想要飞行的时候都只需要调用performFly()就可以了。

这里鸭子类自己的行为实际上是调用其他类里的实现,这就叫做委托

动态行为

既然上面的鸭子行为实际上只是保存了一个引用,那么通过改变这个引用就可以实现在运行时改变行为了。

1
2
3
public void setFlyBehaviour(FlyBehaviour fb){
flyBehaviour = fb;
}

总结

  1. 将可能经常改变的部分和不太变化的部分分开实现
  2. 面向接口编程,而不是面向具体实现。