策略模式 ——解决继承带来的苦恼
在最近的维护版本的开发中,经常碰到的一个问题:接手的项目中使用了OO的观念,可是大量使用继承,造成对子类的行为约束的过紧,子类很难扩展,此时如果修改父类的话,又容易误伤子类(因为一些子类的行为修改,而另一些又不需要修改)。被百般折磨后发现了策率模式,故在此记录下。
为了表示结构,使用了下面的类图,很简单。DuckA,DuckB,DuckC均继承自Duck,并在Duck中实现了Swim()Quack(),所以现在的所有的鸭子都会游泳,都会叫,因为鸭子长的可以不一样,所以在子类中分别实现了Display()。
这个结构没什么问题,也很不错,但是有一天突出需求变更了,要求鸭子们的叫声并不相同,但其中DuckA和DuckB叫声是相同,并且duckD不会叫;此时最容易想到一个办法是重写鸭子们的Quack()方法,并且DuckA和DuckB的实现是一样的,同时在DuckD中Quack()方法做一个空实现。这样实现虽然简单,但是问题很多,首先,DuckA和DuckB中的Quack代码重复的。当A,B的叫声发生变更时,将改两份代码(如果鸭子数量巨大的话,重复代码将更多);其次,如果有一天鸭子的叫声再次发生变化,那么你又要修改这些Qucak()方法。最后DuckD实际上不会叫,再将Quack()方法放到父类中感觉也并不合适。
此时,我们可能想到第二个方案是,将变化剥离出来,即将Quack()作成接口,会叫的鸭子实现该接口。这样看似很合适,因为解决了DuckD不会叫的问题,而且更加符合OO的设计。但是这并不能解决代码重复的问题,即DuckA,DuckB中Quack()的实现是一样的。继承了接口,但是依然无法达到代码的复用。
此时我们该怎么办呢?
这个入很不专业,仅作示意吧。我们可以将Quack抽象成接口,并且在父类中定义一个IQuack变量,但这个变量在子类中进行赋值。这样父类便可以操作IQuack接口。同时我们将不同的叫声分别实现出来,即QuackA,QuackB,QuackC.这样也解决了代码复用的问题。这样以后即便DuckC也更换叫声时,我们也可以快速进行更换。
简单写了些代码:
public interface IQuack { void MakeQuack(); } public class QuackA :IQuack { public void MakeQuack() { //QuackA } }
public abstract class Duck { public IQuack MyQuack; protected void Swim() { } protected virtual void Display() { } public void Quack() { MyQuack.MakeQuack(); } } class DuckA :Duck { public DuckA() { MyQuack = new QuackA(); } protected override void Display() { // DuckA } }