@linux1s1s
2017-01-05T17:36:37.000000Z
字数 4499
阅读 1490
Base
2017-01
MallardDuck.java
public class MallardDuck {
/**
* 鸭子叫的方法
*/
public void quack() {
//TODO
}
/**
* 游泳方法
*/
public void swim() {
//TODO
}
/**
* 外形方法,比如鸭子的颜色
*/
public void display() {
//TODO
}
}
RedheadDuck.java
public class RedheadDuck {
/**
* 鸭子叫的方法
*/
public void quack() {
//TODO
}
/**
* 游泳方法
*/
public void swim() {
//TODO
}
/**
* 外形方法,比如鸭子的颜色
*/
public void display() {
//TODO
}
}
我们发现如果quack()
和swim()
相同,那么就重复写了两遍相同的代码,增加维护成本的同时也丝毫没有体现出面向对象的思想,所以很自然的将相同的方法抽取到基类里面去。
先给个UML图直观表达一下:
然后看看类的实现:
基类Duck.java
public abstract class Duck {
/**
* 鸭子叫的方法
*/
public void quack() {
//TODO
}
/**
* 游泳方法
*/
public void swim() {
//TODO
}
/**
* 外形方法,比如鸭子的颜色,交给基类去实现
*/
public abstract void display();
}
子类MallardDuck.java
public class MallardDuck extends Duck {
/**
* 外形方法,比如鸭子的颜色
*/
@Override
public void display() {
//MallardDuck TODO
}
}
子类RedheadDuck.java
public class RedheadDuck extends Duck {
/**
* 外形方法,比如鸭子的颜色
*/
@Override
public void display() {
//RedheadDuck TODO
}
}
现在需求突然改动,需要增加一个鸭子的飞翔功能,那么这时我们很庆幸抽取了基类,我们在基类里面增加一个飞翔的方法即可如下:
基类Duck.java
public abstract class Duck {
/**
* 鸭子叫的方法
*/
public void quack() {
//TODO
}
/**
* 游泳方法
*/
public void swim() {
//TODO
}
/**
* 飞翔方法
*/
public void fly(){
//TODO
}
/**
* 外形方法,比如鸭子的颜色,交给基类去实现
*/
public abstract void display();
}
如果产品经理又改动了,红嘴鸭子不能飞,这个时候突然意识到自己在基类里面做多了事情,只能在子类中重写基类的fly()方法来补救。
对于上面的设计,你可能发现一些弊端,如果基类有新的特性,子类都必须变动,这是我们开发最不喜欢看到的,一个类变让另一个类也跟着变,这有点不符合OO设计了。这样很显然的耦合了一起。利用继承-->耦合度太高了.
针对上面的问题,我们把容易引起变化的部分提取出来并封装之,来应付以后的变化。虽然代码量加大了,但可用性提高了,耦合度也降低了。
容易变化的部分是quack()
和fly()
方法,需求很可能从这两个功能变化,所以提取出来两个接口,分别是Flyable.java
和Quackable.java
,然后基类Duck.java
也需要相应的改变,如下所示:
Flyable.java
public interface Flyable {
void fly();
}
Quackable.java
public interface Quackable {
void quack();
}
基类Duck.java
public abstract class Duck {
/**
* 游泳方法
*/
public void swim() {
//TODO
}
/**
* 外形方法,比如鸭子的颜色,交给基类去实现
*/
public abstract void display();
}
子类MallardDuck.java
public class MallardDuck extends Duck implements Quackable {
@Override
public void display() {
//TODO MallardDuck display
}
@Override
public void quack() {
//TODO MallardDuck quack
}
}
子类RedheadDuck.java
public class RedheadDuck extends Duck implements Flyable, Quackable {
@Override
public void display() {
//TODO RedheadDuck display
}
@Override
public void fly() {
//TODO RedheadDuck fly
}
@Override
public void quack() {
//TODO RedheadDuck quack
}
}
对上面各方式的总结:
- 继承的好处:让共同部分,可以复用.避免重复编程.
- 继承的不好:耦合性高.一旦超类添加一个新方法,子类都继承,拥有此方法,若子类相当部分不实现此方法,则要进行大批量修改.继承时,子类就不可继承其它类了.
- 接口的好处:解决了继承耦合性高的问题.且可让实现类,继承或实现其它类或接口.
- 接口的不好:不能真正实现代码的复用.可用以下的策略模式来解决.
我们有一个设计原则:
找出应用中相同之处,且不容易发生变化的东西,把它们抽取到抽象类中,让子类去继承它们;
找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
现在,为了要分开“变化和不变化的部分”,我们准备建立两组类(完全远离Duck类),一个是"fly"相关的,另一个
是“quack”相关的,每一组类将实现各自的动作。比方说,我们可能有一个类实现“呱呱叫”,另一个类实现“吱吱
叫”,还有一个类实现“安静”。
先来看看UML图:
接下来看源码:
FlyBehavior.java
public interface FlyBehavior {
void fly();
}
QuackBehavior.java
public interface QuackBehavior {
void quack();
}
接着是一组实现FlyBehavior
接口的具体实现类
FlyWithWings.java
public class FlyWithWings implements FlyBehavior {
@Override
public void fly() {
//有翅膀的飞行
}
}
FlyWithNoWings.java
public class FlyWithNoWings implements FlyBehavior {
@Override
public void fly() {
//没有翅膀的飞行
}
}
然后是一组实现QuackBehavior
接口的具体实现类
CroaksQuack.java
public class CroaksQuack implements QuackBehavior {
@Override
public void quack() {
//实现呱呱叫的鸭子
}
}
SqueakQuack.java
public class SqueakQuack implements QuackBehavior {
@Override
public void quack() {
//实现吱吱叫的鸭子
}
}
MuteQuack.java
public class MuteQuack implements QuackBehavior {
@Override
public void quack() {
//实现不会叫的鸭子
}
}
基类聚合了上面的两个接口,成员变量的实例化可以通过构造器,也可以通过Set方法实现。通过这种聚合方式,我们发现不仅大大降低了耦合度,还最大化复用了代码。
Duck.java
public abstract class Duck {
protected FlyBehavior mFly;
protected QuackBehavior mQuack;
public Duck() {
}
public Duck(FlyBehavior fly, QuackBehavior quack) {
mFly = fly;
mQuack = quack;
}
/**
* 鸭子叫的方法
*/
public void quack() {
if (mQuack != null) {
mQuack.quack();
}
}
/**
* 飞翔方法
*/
public void fly() {
if (mFly != null) {
mFly.fly();
}
}
/**
* 游泳方法
*/
public void swim() {
//TODO
}
/**
* 外形方法,比如鸭子的颜色,交给基类去实现
*/
public abstract void display();
/**
* Get Set 方法
*
* @return
*/
public FlyBehavior getFly() {
return mFly;
}
public void setFly(FlyBehavior fly) {
mFly = fly;
}
public QuackBehavior getQuack() {
return mQuack;
}
public void setQuack(QuackBehavior quack) {
mQuack = quack;
}
}
然后是两个子类
MallardDuck.java
public class MallardDuck extends Duck {
@Override
public void display() {
//TODO MallardDuck display
}
}
RedheadDuck.java
public class RedheadDuck extends Duck {
@Override
public void display() {
//TODO RedheadDuck display
}
}
最后看一下TestCase
public class TestCase {
public static void main(String[] args) {
Duck duckFly = new MallardDuck();
duckFly.setFly(new FlyWithWings());
duckFly.setQuack(new CroaksQuack());
Duck duckNoFly = new RedheadDuck();
duckNoFly.setFly(new FlyWithNoWings());
duckNoFly.setQuack(new SqueakQuack());
duckFly.fly();
duckFly.quack();
duckNoFly.fly();
duckNoFly.quack();
}
}
参考博文
java常用设计模式 本文做了适当编辑,特别说明。