我们先来看看问题 —— 现在我们需要实现一个模拟鸭子的游戏,游戏中会出现各种各样的鸭子,他们会有不同的飞行方式,同样有不同的鸣叫方式,同时我们要考虑到以后还可能出现更多的各种各样新式的鸭子,那我们该如何来实现呢?
1>我们来试试继承
这是我们的Duck类
?
1
2
3
4
5
6
7
8
9
package
my.oschina.net.design.strategy;
public
class
Duck {
public
void
fly(){}
public
void
quack(){}
}
现在我们可以通过继承很简单的来完成各种鸭子的实现,我们所要做的就是简单的继承Duck类,实现符合各自特征的fly()方法和quack()方法,像这样写:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class
Rubber
extends
Duck {
public
void
fly(){
System.out.println(
"I can't fly!"
);
}
public
void
quack(){
System.out.println(
"Quack"
);
}
}
class
ModelDuck
extends
Duck {
public
void
fly(){
System.out.println(
"I can fly with rocket!"
);
}
public
void
quack(){
System.out.println(
"<<silience>>"
);
}
}
class
XX
extends
Duck{
public
void
fly(){
public
void
quack(
}
class
YY
extends
Duck{
public
void
fly(){
public
void
quack(){
}
class
ZZ
extends
Duck{
public
void
fly(){
public
void
quack(){
}
.
.
.
大家看到问题之所在了吗?每当我们增加一个新的子类(软件的可扩展性!软件升级的过程中这些可是必不可少的哦!),我们必须要被迫检查每一个可能需要覆盖的方法(尽管有的鸭子根本就不会飞,也根本就不会叫,这真是很头疼)。。。你确定这样你可以忍受?不管你能不能,反正我不能!
2>要不我们试试接口吧!
我们定义两个接口——flyable接口和quackable接口,这样会飞的会叫的鸭子可以实现这两个接口,不会的不需要实现这两个接口。看起来不错哦!真的不错吗?不要被忽悠!NO!错,很错!有没有发现这和继承都有一个通病——代码根本是无法复用的存在!一旦代码无法复用就意味着我们要做大量重复无意义的ctrl+c,ctrl+v工作。
那我们到底该肿么做呢?
来,我们先来看看我们在写代码的时候需要尽力遵循的两个设计原则:
a) 原则一:找出代码中可能需要变化的代码,把他们独立出来,不要和不变化的或者变化极小的代码混在一起。
b) 原则二:针对接口编程,不要针对实现编程。
关于上述两个原则的解释:
a)一旦我们将变化的和不变化的代码分割开来,以后我们可以轻易的改动或者扩展这部分的代码而不用担心其他不需要变化的代码会受到影响。
b)上面的两种方法中我们都是在针对实现来编程,一旦我们将代码写下了我们就很难来改变这些行为,我们被实现绑定的死死的,这就导致当我们要修改这些行为的时候我们不得不来这个类中大量修改这些源代码!这对于后期软件的维护工作简直不能忍!(ps.利用多态的原理,我们针对超类型来编程,执行的时候代码会自己选择适当的行为!)
下面我们再看第三个设计原则
c)多用组合少用继承,这样我们可以在运行时动态改变类的各种行为,这听起来真的很不错哦!
3>使用策略模式
好吧!入正题吧!废话多了点。现在我们来看看使用策略模式的实现方法。
策略模式——定义算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的改变独立于使用算法的客户。
一切看代码:
这是Duck类
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package
my.oschina.net.design.strategy;
/**
* 我们不变的或者是变动可能性不大的方法写在父类中(display)
* 把以后可能会变化的方法抽取出来例如这里的fly与quack
*
* @author Eswin
*
*/
public
class
Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
/**
* 鸭子外观
* 这个是不会变的就写在父类中,由子类来实现
*/
public
void
display()
{
}
/**
* 动态的改变运行时特定行为的引用fly
* @param flyBehavior
* @return void
*/
public
void
setFly(FlyBehavior flyBehavior)
{
this
.flyBehavior = flyBehavior;
}
/**
* 动态的改变运行时特定行为的引用quack
* @param quackBehavior
* @return void
*/
public
void
setQuack(QuackBehavior quackBehavior)
{
this
.quackBehavior = quackBehavior;
}
public
void
performFly()
{
flyBehavior.fly();
}
public
void
performQuack()
{
quackBehavior.quack();
}
}
两个接口FlyBehavior,QuackBehavior
?
1
2
3
4
5
6
7
8
9
package
my.oschina.net.design.strategy;
public
interface
FlyBehavior {
public
void
fly();
}
public
interface
QuackBehavior {
public
void
quack();
}
实现这两个接口
FlyBehavior实现:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package
my.oschina.net.design.strategy;
/**
* @author Eswin
*/
public
class
FlyNoWay
implements
FlyBehavior {
@Override
public
void
fly() {
System.out.println(
"I have No way to fly"
);
}
}
public
class
FlyWithWings
implements
FlyBehavior{
@Override
public
void
fly() {
System.out.println(
"I can fly with wing!"
);
}
}
QuackBehavior实现:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package
my.oschina.net.design.strategy;
/**
* @author Eswin
*/
public
class
Squeak
implements
QuackBehavior{
@Override
public
void
quack()
{
System.out.println(
"Squeak"
);
}
}
public
class
MuteQuack
implements
QuackBehavior {
@Override
public
void
quack()
{
System.out.println(
"I can not quack"
);
}
}
好了,现在我们来生成一个子类试试效果吧!
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package
my.oschina.net.design.strategy;
public
class
ModelDuck
extends
Duck{
public
ModelDuck()
{
flyBehavior =
new
FlyNoWay();
quackBehavior =
new
Quack();
}
public
void
display()
{
System.out.println(
"I am a model duck!"
);
}
}
来Test一下
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package
my.oschina.net.design.strategy;
public
class
TestModelDuck {
public
static
void
main(String[] args) {
Duck ModelDuck =
new
ModelDuck();
System.out.println(
"现在的模型鸭还不可以飞"
);
ModelDuck.performFly();
ModelDuck.performQuack();
ModelDuck.setFly(
new
FlyWithWings());
System.out.println(
"现在的模型鸭可以飞了"
);
ModelDuck.performFly();
ModelDuck.performQuack();
}
}
看看结果吧!O了!
联想 大家都玩过网游吧!我们在升级打怪买装备的时候,你的各项属性是否发生了变化?你换武器的时候算不算是动态的改变了自己的攻击行为?同样的攻击,我们可是造成了不同的伤害哦,对吧?
适用情况 1)系统中许多相关的类仅仅是行为有异;
2)系统需要能够在几种算法中快速动态的切换;
3)类中定义了多种行为 , 并且这些行为在这个类的操作中以多个条件选择语句的形式出现。
下面,我们分析Android中的动画是如何使用策略模式的。
1. 意图
定义一系列的算法,把它们一个个封装起来,并且使它们可互相替换。
策略模式使得算法可独立于使用它的客户而变化。
2. 结构图和代码
Animation不同动画的实现,主要是依靠Interpolator的不同实现而变。