[Head First设计模式]策略模式-阿里云开发者社区

开发者社区> 杰克.陈> 正文

[Head First设计模式]策略模式

简介: 原文:[Head First设计模式]策略模式 系列文章 [Head First设计模式]山西面馆中的设计模式——装饰者模式 [Head First设计模式]山西面馆中的设计模式——观察者模式 [Head First设计模式]山西面馆中的设计模式——建造者模式 [Head First...
+关注继续查看
原文:[Head First设计模式]策略模式

系列文章

引言

该过年了,总让人有点浮躁,公司就省俩人了,唉,如果坐等时间,那实在难熬,只能给自己找点事做,转移一下注意力。进入今天的主题吧策略模式。

策略模式定义

策略模式定义了算法族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。

书中鸭子的例子

模拟鸭子的简单应用

Joe上班的公司做了一套相当成功的模拟鸭子游戏SimUDuck,游戏中出现各种鸭子,一边游戏戏水,一边呱呱叫。此系统的内部设计使用了标准的OO技术,设计了一个鸭子超类,并让各种鸭子继承此超类。

让鸭子能飞

去年,公司的竞争力加剧,公司主管认为该是创新的时候了。主管认为,此模拟程序需要会飞的鸭子,将竞争者抛在后面。

 

 改进继承

Joe认识到继承可能不是一个好的解决办法,因为他刚刚拿到来自主管的备忘录,希望以后每六个月更新产品(至于更新办法,他们还没想到)。Joe知道规格会常常改变,每当有新的鸭子子类出现,他就要被迫检视并可能需要覆盖fly()和quack().....这简直是无穷尽的噩梦。所以,他需要一个更清晰的方法,让某些(而不是全部)鸭子类型可飞或可叫。

其实,并非所有的鸭子子类都具有飞行和呱呱叫的行为,所以继承并不是适当的解决方式。虽然Flyable与Quackable可以解决一部分的问题(不会再有会飞的橡皮鸭),但是却造成代码无法复用,这只能算是从一个噩梦跳进另一个噩梦。甚至,在会飞的鸭子中,飞行的动作可能还有多种变化......

现在我们知道使用继承有一些缺失,因为改变鸭子的行为会影响所有种类的鸭子,而这并不恰当。Flyable与Quackable接口一开始似乎还挺不错,解决了问题(只有会飞的鸭子才继承Flyable),但是接口不具有实现代码,所以继承接口无法达到代码的复用。这意味着:无论何时你需要修改某个行为,你被迫得往下追踪并修改每一个有定义此行为的类,一不小心,可能造成新的错误。

第一个设计原则

设计原则:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。这个概念很简单,几乎是每个设计模式背后的精神所在,所有的模式都提供了一套方法让系统中的某部分改变不会影响其它部分。

分开变化和不变化的部分

为了要分开变化和不变化的部分,我们准备建立两组类,一个是fly相关,一个是quack相关的,每一组类将实现各自的动作。比如说,我们可能有一个类实现“呱呱叫”,另一个类实现“叽叽叫”,另一个类实现“安静”。

设计鸭子行为

我们利用接口代表每个行为,比方说,IFlyBehavior与IQuackBehaivor,而行为的每个实现都必须实现这些接口之一。所以这次鸭子类不会负责实现fly与quack接口,而是由其他类专门实现IFlyBehavior与IQuackBehaivor,这就称为“行为类”。由行为类实现行为接口,而不是由Duck类实现接口。

 这样的做法迥异于以往,以前的做法是:行为是继承自Duck超类的具体实现而来,或是继承某个接口并由子类自行实现而来。这两种做法都是依赖于实现,我们被实现绑的死死,没办法更改行为(除非写更多的代码)。

第二个设计原则

针对接口编程,而不是针对实现编程。

关于接口编程和实现编程:

假设有一个抽象类Animal,有两个具体的实现(Dog与Cat)继承自Animal。

针对实现编程的作法如下:

1 Dog d = new Dog();
2 d.bark();

针对接口/超类型编程作法如下:

1 Animal animal = new Dog();
2 animal.makeSound();

子类型实例化的动作是“在运行时才指定具体实现的对象”

1 a = getAnimal();
2 a.makeSound();

实现鸭子的行为

这样的设计,可以让飞行和呱呱叫的动作被其他的对象复用,因为这些行为已经与鸭子无关了。而我们可以新增一些行为,不会影响到既有的行为类,也不会影响有使用到飞行行为的鸭子类。

集成鸭子的行为
鸭子现在会将飞行和呱呱叫的动作,委托(delegate)别人处理,而不是使用定义在自己类(或子类)内的方法。
① 首先,在鸭子中加入两个实例变量, 分别为FlyBehavior与QuackBehavior,声明为接口类型(而不是具体类实现类型),每个变量会利用多态的方式在运行时引用正确的行为类型(例如:FlyWithWings、Squeak . . . 等)。
我们也必须将Duck类与其所有子类中的fly() 与quack( ) 移除,因为这些行为已经被搬移到FlyBehavior与QuackBehavior类中了。我们用performFly()和performQuack()取代Duck类中的fly()与quack()。

 

代码测试

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Wolfy.设计模式
 8 {
 9     public abstract class Duck
10     {
11         //为行为接口类型声明两个引用变量,所有鸭子子类都继承它们。
12         public IFlyBehavior flyBehavior;
13         //每只鸭子都引用实现QuackBehavior接口的对象。
14         public IQuackBehavior quackBehavior;
15         public Duck() { }
16         public abstract void Display();
17         public void PerformFly()
18         {
19             //委托给行为类
20             flyBehavior.Fly();
21         }
22         public void PerformQuack()
23         {
24             //鸭子将呱呱叫行为委托给quackBehavior引用的对象。
25             quackBehavior.Quack();
26         }
27         public void Swim()
28         {
29             Console.WriteLine("会游泳.....");
30         }
31     }
32 }
Duck
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Wolfy.设计模式
 8 {
 9     public interface IFlyBehavior
10     {
11         //所有飞行行为必须实现的接口。
12         void Fly();
13     }
14 }
IFlyBehavior
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Wolfy.设计模式
 8 {
 9     public interface IQuackBehavior
10     {
11         void Quack();
12     }
13 }
IQuackBehavior
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Wolfy.设计模式
 8 {
 9     public class FlyWithWings : IFlyBehavior
10     {
11         #region IFlyBehavior 成员
12 
13         public void Fly()
14         {
15             Console.WriteLine("会飞......");
16         }
17 
18         #endregion
19     }
20 }
FlyWithWings
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Wolfy.设计模式
 8 {
 9     public class FlyNoWay : IFlyBehavior
10     {
11         #region IFlyBehavior 成员
12 
13         public void Fly()
14         {
15             Console.WriteLine("不会飞......");
16         }
17 
18         #endregion
19     }
20 }
FlyNoWay
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Wolfy.设计模式
 8 {
 9     public class MuteQuack : IQuackBehavior
10     {
11         #region IQuackBehavior 成员
12 
13         public void Quack()
14         {
15             Console.WriteLine("不会叫");
16         }
17 
18         #endregion
19     }
20 }
MuteQuack
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Wolfy.设计模式
 8 {
 9     public class Quack : IQuackBehavior
10     {
11         #region IQuackBehavior 成员
12 
13         void IQuackBehavior.Quack()
14         {
15             Console.WriteLine("呱呱叫......");
16         }
17 
18         #endregion
19     }
20 }
Quack
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Wolfy.设计模式
 8 {
 9     public class Squeak : IQuackBehavior
10     {
11         #region IQuackBehavior 成员
12 
13         public void Quack()
14         {
15             Console.WriteLine("吱吱叫.......");
16         }
17 
18         #endregion
19     }
20 }
Squeak
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Wolfy.设计模式
 8 {
 9     /// <summary>
10     /// MallardDuck从Duck继承,具有flyBehavior 和quackBehavior 实例变量。
11     /// </summary>
12     public class MallardDuck : Duck
13     {
14         public MallardDuck()
15         {
16             //FlyWithWings作为IFlyBehavior类型
17             base.flyBehavior = new FlyWithWings();
18             //Quack类处理呱呱叫。
19             base.quackBehavior = new Quack();
20         }
21         public override void Display()
22         {
23             Console.WriteLine("绿头鸭......");
24         }
25     }
26 }
MallardDuck
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Wolfy.设计模式
 8 {
 9     public class WoodDuck : Duck
10     {
11         public WoodDuck()
12         {
13             flyBehavior = new FlyNoWay();
14             quackBehavior = new MuteQuack();
15         }
16         public override void Display()
17         {
18             Console.WriteLine("木头鸭子.....");
19         }
20     }
21 }
WoodDuck
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Wolfy.设计模式
 8 {
 9     class Program
10     {
11         static void Main(string[] args)
12         {
13             Duck mallard = new MallardDuck();
14             mallard.Display();
15             //调用MallardDuck继承来的perform方法,进而将绿头鸭的行为委托给quack和fly的行为类来处理。
16             mallard.PerformFly();
17             mallard.PerformQuack();
18             mallard.Swim();
19             Duck wood = new WoodDuck();
20             wood.Display();
21             wood.PerformFly();
22             wood.PerformQuack();
23             wood.Swim();
24             Console.Read();
25 
26         }
27     }
28 }

结果

总结

所有鸭子从Duck继承,飞行行为实现FlyBehavior接口,呱呱叫行为实现QuackBehavior接口。

“有一个”(has a)可能比“是一个”(is a)更好

有一个关系相当有趣:每一鸭子都有一个FlyBehavior且有一个QuackBehavior,让鸭子将飞行和呱呱叫委托它们代为处理。

如果将两个类结合起来使用(如同本例),这就是组合(Composition)。这种作法和继承不同的地方在于:鸭子的行为不是继承而来,而是和适当的行为对象组合而来。

第三个设计原则

多用组合,少用继承

使用组合建立系统具有很大的弹性,不仅可将算法族封装成类,更可以在运行时动态地改变行为。

优缺点

优点:
1、 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码转移到父类里面,从而避免重复的代码。
2、 策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为。如果不是用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为。但是,这样一来算法或行为的使用者就和算法或行为本身混在一起。决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为的逻辑混合在一起,从而不可能再独立演化。继承使得动态改变算法或行为变得不可能。
3、 使用策略模式可以避免使用多重条件转移语句。多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重转移语句里面,比使用继承的办法还要原始和落后。
缺点:
1、客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。
2、 策略模式造成很多的策略类,每个具体策略类都会产生一个新类。有时候可以通过把依赖于环境的状态保存到客户端里面,而将策略类设计成可共享的,这样策略类实例可以被不同客户端使用。换言之,可以使用享元模式来减少对象的数量。
参考书
Head first 设计模式
没想到该回家了,还能耐住性子看书,这里将书上的例子整理了一下,比较好理解。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
7238 0
设计模式(二十二) 策略模式
有时候对象需要按照某种策略改变行为,我们可以利用策略模式,将策略或算法提取出来,作为单独的类实现。使用策略模式,可以让具体算法和应用对象分离,方便的根据不同条件替换策略。
601 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,大概有三种登录方式:
2503 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
8919 0
设计模式之五(策略模式)
原文:设计模式之五(策略模式) 前言 策略模式:它定义了算法家族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化,不会影响到使用算法的客户。 策略模式结构图   Strategy:策略类,定义所有支持的算法的公共接口 ConcreteStrategy1,ConcreteStrategy2,ConcreteStrategy3这三个是具体策略类,封装了具体的算法或行为,继承于Strategy Context上下文,用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用。
726 0
Android线程池(二)——ThreadPoolExecutor及其拒绝策略RejectedExecutionHandler使用示例
MainActivity如下:package cc.vv; import java.util.concurrent.LinkedBlockingQueue; import java.
824 0
【Java设计模式】策略模式
(转载请注明出处:http://blog.csdn.net/buptgshengod) 1.背景知识    策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。比如每个人都要“交个人所得税”,但是“在美国交个人所得税”和“在中国交个人所得税”就有不同的算税方法。策略模式将不同的算法封装起来,根据客户端的需求,调用不同的对象使用不同的算法,节省了大
4010 0
+关注
杰克.陈
一个安静的程序猿~
10427
文章
2
问答
文章排行榜
最热
最新
相关电子书
更多
《Nacos架构&原理》
立即下载
《看见新力量:二》电子书
立即下载
云上自动化运维(CloudOps)白皮书
立即下载