面向对象设计 - OOD (开篇)

开篇:

最近在看 <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. 抽象出系统中什么是改变的,然后把它们分开并封装。

猜你喜欢

转载自blog.csdn.net/ytdxyhz/article/details/79258870
OOD