开篇:
最近在看 <Head First Design Patterns>, 作为自我总结写下这一系列博客,此篇为开篇。
正文:
第一个简单的设计:
我们的主角Joe,他工作的公司制作了一个非常成功的 鸭子 游戏,这个游戏能显示 很多不同种类的鸭子,并且这些鸭子有游泳和呱呱叫的能力,现在Joe要设计一个系统来实现我们描述的需求。
我的第一印象:这不很简单吗,看下面类图. Duck 类 实现: Swim() 和 Quck() 方法;RedDuck 和 BlackDuck分别继承Duck类然后实现Display() 方法 - 因为redduck和blackduck的显示是不一样的嘛所以分别实现(方法的重写 override - 除了方法内部实现,其他必须和父类中相同.)
第二天,:“为了打败咱们的竞争对手,我们决定给我们的鸭子们加一个会飞的能力,相信你你能做出来!”. 产品经理信誓旦旦的对Joe说。
没办法只能硬上了,然后Joe就简单的在Duck类中加了fly()方法。
嗯,看起来不错! 但是第二天Joe还是被骂了,为啥? 因为产品展示的时候一个橡皮鸭子在飞,所以Joe意识到不是原来鸭子的子类中不仅仅只有两个鸭子,还有其他很多种鸭子,并且子类的鸭子的能力也不尽相同,如下
第二个简单的设计
Joe想,既然不是所有的鸭子都有Fly() 并且它们的Quack()的声音也不相同,那得用接口啊, 把所有鸭子都相通的能力放在Duck里面其他的Fly 和 Quack分别放在两个接口中,然后对相对应的鸭子实现这个接口!完美!得出如下.
对上图解释: 四个不同的鸭子类继承来自Duck的方法Swim() 和 Display(). 每个鸭子根据自己的需求对Display进行override,然后根据自己的需求实现 Flyable 和 Quackable接口. 因为每个鸭子的飞行方法或者叫声都不一样,甚至有的比如香橡胶鸭子 RubberDuck都不能飞那么就不需要实现Fly类.
Joe 觉的这样很好能满足需求,但是呢,问题来了。 我们实现的代码出现了非常多的 重复,代码重用率非常低,对日后的维护埋下了很大的隐患。
最终的完美的设计:
系统设计不仅仅是满足需求,更是在满足需求的前提下最大程度的让系统能 继续保持 flexible,maintable ,less code duplicate 等的目标。那么对于以上鸭子类的需求我们需要考虑是不是应该把 可以重用的代码给归类呢?
因为不同鸭子都有不同的fly 和 quack 行为那我们可以把这两个行为单独拿出来进行封装,在父类鸭子中我们使用fly 和 quack 接口作为 类的 属性,这样我们能根据需求相对应的去实例化它们,在各个子类鸭子中我们我们只需要去实现display这个方法就可以。见如下代码
package OOD; /** * FlyBehavior interface. * @author huazhe * */ public interface FlyBehavior { public void Fly(); }
package OOD; public class FlyNoWay implements FlyBehavior{ @Override public void Fly() { System.out.println("I can't fly..."); } } package OOD; public class FlyWithWings implements FlyBehavior{ @Override public void Fly() { System.out.println("I can fly with wings!"); } }
package OOD; public interface QuackBehavior { public void Quack(); }
package OOD; public class Quack implements QuackBehavior{ @Override public void Quack() { System.out.println("I can quack quack quack..."); } } package OOD; public class Squeak implements QuackBehavior{ @Override public void Quack() { System.out.println("I can Squeak Squeak Squeak..."); } } package OOD; public class MuteNoQuack implements QuackBehavior{ @Override public void Quack() { System.out.println("I am mute..."); } }
package OOD; public class Duck { public FlyBehavior flyBehavior; public QuackBehavior quackBehavior; public void Swim() { System.out.println("I can swim..."); } public void Display() { // } public void PerformFly() { flyBehavior.Fly(); } public void PerformQuack() { quackBehavior.Quack(); } public void SetFlyBebavior(FlyBehavior flyBehavior) { this.flyBehavior = flyBehavior; } public void SetQuackBehavior(QuackBehavior quackBehavior) { this.quackBehavior = quackBehavior; } }
package OOD; public class RedDuck extends Duck{ @Override public void Display() { System.out.println("I am red duck..."); } }
好了,那我们现在有一个redduck,那我们怎么能通过我们的设计给他简单且随意的赋予 如何fly 或者如何quack呢?
package OOD; public class main { public static void main(String[] args) { Duck redDuck = new RedDuck(); redDuck.SetFlyBebavior(new FlyWithWings()); redDuck.SetQuackBehavior(new Quack()); redDuck.Display(); redDuck.Swim(); redDuck.PerformFly(); redDuck.PerformQuack(); } }
输出如下
I am red duck... I can swim... I can fly with wings! I can quack quack quack...
结语:
通过这个简单的OOD,复习了override 和 overload的区别, 接口的使用,深刻了java 多态 和 继承, 封装的使用。
OOD 的原则:
1. 偏爱 composition 胜过 inheritance. composition的意思可以理解为我们在Duck中使用 Flybehavior作为属性而不是继承;
2. 为了增加系统的灵活性等我们应在恰当的时候多使用接口,而不是 实现。Program to an interface, not an implementation.
3. 抽象出系统中什么是改变的,然后把它们分开并封装。