开篇:
最近在看 , 作为自我总结写下这一系列博客,此篇为开篇。
正文:
第一个简单的设计:
我们的主角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. 抽象出系统中什么是改变的,然后把它们分开并封装。