文章内容是对《Head First 设计模式》的阅读与总结。
OO基础:
- 抽象
- 封装
- 多态
- 继承
OO基础只是一个入门,了解OO基础不意味着你可以设计出好的代码
OO原则:
- 封装变化
- 优先使用组合而不是继承
- 针对接口编程,而不是实现编程
举一个例子:比如有一个超类Duck,这个类又fly, quark, display等等方法,但是每个fly和quark方法又有些不同。比如塑料的鸭子不会叫不会飞等等。假设,我们有几十个不同的子类,这时候我们需要对不同的子类的方法进行覆盖。这样会耗费难以想象的精力。为此,我们可以将这部分“变化”的方法分离出来,而不在超类中实现。例如:实现FlyBehavior和QuarkBhavior两个接口,通过两组行为类实现接口。比如:FlyWithWthings类和NotFly类分别实现不同的飞翔方式。于是,对于超类的代码实现我们可以这样写:
class Duck{
private flyBehavior;
private quarkBehavior;
public void setFlyBehavior(FlyBehavior flybehavior){
this.flyBehavior = flyBehavior;
}
public void setQuarkBehavior(QuarkBehavior quarkBehavior){
this.quarkBehavior = quarkBehavior;
}
//此为针对接口编程(针对实现编程的意思是,对于每个子类“变化部分”的方法都要具体编程实现)
public void fly(){
this.flyBehavior.fly();
}
public void quark(){
this.quarkBehavior.quark();
}
public void display(){
//输出"i am a duck";
}
}
显然每种鸭子的叫声、飞翔不一样。如果仅仅使用继承赋予鸭子叫声与飞翔的行为,我们很难区分不同鸭子不同的行为,这时候需要使用覆盖。而这将导致代码量提升且代码没有弹性,也可能引进Bug。因此,我们优先使用组合而不是继承。我们可以将变化的行为独立出来用一组行为类替代。
public interface FlyBehavior{
fly();
}
public interface QuarkBehavior{
quark();
}
public class FlyWithWithings implements FlyBehavior{
public fly(){
//FlyWithWithings
}
}
public class FlyWithWNothing implements FlyBehavior{
public fly(){
//FlyWithWNothing
}
}
假设,我们需要写一个假鸭子的类
public FakeDuck extends Duck{
public FakeDuck(FlyBehavior flybehavior, QuarkBehavior quarkBehavior){
if (flybehavior==null)
this.flybehavior = new FlyWithWNothing();
if (quarkBehavior==null)
this,quarkBehavior = new Q....();//类似的行为类
}
}
于是使用假鸭子叫声等等行为委托的是行为类,鸭子子类本身不需要为具体的行为去实现相应的方法。
这种模式也称为策略模式。
策略模式:定义一个算法家族,分别封装起来,让他们之间可以相互替换。策略模式让算法的变化独立于算法的使用客户。