前言:

刚刚开始学习设计模式,之前也接触过一些,但是从来都没有系统的学过,这次打算好好的学习一下。这里就当是对学习过程的一个记录、整理,以便可以在以后不时的温故知新。

这一节采用一个鸭子的示例,层层推进,引入策略模式。具体如下:

1.   基本需求:创建有一些特性的鸭子

鸭子拥有如下的一些特性:游泳戏水、呱呱叫、外观

初步实现鸭子的特性:

鸭子超类:

public abstract class Duck
{
public void Quack()
{
Console.WriteLine("鸭子叫:呱呱呱");
}
public void Swim()
{
Console.WriteLine("鸭子游泳");
}
public abstract voidDisplay();
}

鸭子子类:

public class MallardDuck:Duck
{
public override voidDisplay()
{
Console.WriteLine("绿头鸭");
}
} public class RedHeadDuck : Duck
{
public override voidDisplay()
{
Console.WriteLine("红头鸭");
}
}

2.   新的需求:让鸭子飞

2.1 在Duck类中添加飞的方法

如下:

 public abstract class Duck
{
public void Quack()
{
Console.WriteLine("鸭子叫:呱呱呱");
}
public void Swim()
{
Console.WriteLine("鸭子游泳");
} public void Fly()
{
Console.WriteLine("鸭子飞了");
}
public abstract voidDisplay();
}

2.2 并非所有的鸭子都能飞

这个时候会带来一下新的问题:

并非所有的鸭子都能飞,比如:橡皮鸭

并非所有的鸭子都能呱呱叫:比如:橡皮鸭

对应的解决办法:

2.3 覆盖鸭子超类中的Fly和Quack方法

通过在橡皮鸭的实现中覆盖鸭子超类中的Fly和Quack方法

如下:

public class RubberDuck : Duck
{
public new void Fly()
{
//Do nothing
} public new void Quack()
{
Console.WriteLine("鸭子叫:吱吱吱");
}
}

2.4 有些鸭子既不能飞也不能叫

这样带来的新的问题:

比如木头鸭,既不会游泳 也不会叫

如果直接继承自Duck超类,还需要对Fly和Quack方法进行重写。

这个时候我们可能意识到继承不是解决问题的最终办法,因为每一个继承自鸭子的子类都需要去被迫的检查并且可能要覆盖Fly和Quack方法。

我们可能只需要让某些鸭子具有Fly和Quack即可

2.5 通过接口让某些鸭子具有Fly和Quack特性

通过接口来解决此问题

如下所示:

public interface IFlyable
{
public void Fly();
} public interface IQuackable
{
public void Quack();
} public class MoodDuck : IFlyable, IQuackable
{
public void Fly()
{
//Can't Fly
} public void Quack()
{
//Can't Quack
}
}

通过接口的方式虽然结局了一部分鸭子不会飞或者不会叫的问题,

2.6 针对实现编程,代码不能复用

使用接口产生新的问题:

代码不能复用,产生过多的重复代码。如果你想要修改某个行为,必须在买个定义了此行为的类中修改它,这样很可能造成新的错误。

2.7 封装变化,针对接口编程

这个时候出现了第一个设计原则:

找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码放在一起。

即:

把会变化的部分抽取并封装起来,以便以后可以轻易的改动或者扩充此部分,而不影响其他不变的部分。

分开变化和不会变化的部分,在本例中Fly和Quack会随着鸭子的不同而改变,所以我们要将其抽离出Dock类,并建立一组新类来代表每个行为。

这时出现了设计的另一个原则:

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

这里我们用接口代表每个行为,例如:IFlyable和IQuackable,每个行为的实现都将实现对应的接口。

所以鸭子类不会负责实现IFlyable和IQuackable接口,而是由一组其他专门的类来实现对应的接口,称之为“实现类”。

这种做法跟之前做法的区别:

l  之前的做法中行为的实现来自超类的具体实现,或者继承某个接口的子类实现,这些做法都依赖于实现。

l  新的设计中,鸭子子类通过使用接口表示行为,所以实现不会被绑定在子类中,特定的行为编写在实现了接口的类中。

“针对接口编程”真正的意思是“针对超类型”编程

变量的声明类型应该是超类型,通常是一个抽象类或者接口,因此只要是实现了抽象类或者接口的对象,都可以指定给这个变量,即声明类型时不用理会以后执行时真正的对象类型。

接下来我们就可以实现鸭子具体的行为了,如下:

/// 会飞的行为类
/// </summary>
public classFlyWithWings:IFlyable
{
public void Fly ()
{
Console.WriteLine("用翅膀飞");
}
} /// <summary>
/// 不会飞的行为类
/// </summary>
public class FlyNoWay : IFlyable
{
public void Fly()
{
Console.WriteLine("不会飞");
}
} /// <summary>
/// 呱呱叫的行为类
/// </summary>
public class Quack : IQuackable
{
public void Quack()
{
Console.WriteLine("呱呱叫");
}
} /// <summary>
/// 吱吱叫的行为类
/// </summary>
public class Squeak : IQuackable
{
public void Quack()
{
Console.WriteLine("吱吱叫");
}
} /// <summary>
/// 不会叫的行为类
/// </summary>
public class MuteQuack :IQuackable
{
public void Quack()
{
Console.WriteLine("不会叫");
}
}

接下来我们要对鸭子的行为进行整合,其核心思想就是:将鸭子飞行和叫的行为委托别人处理,不使用定义在Duck类内的叫和飞行方法。

具体做法如下:

1.    在Duck类中新增2个实例变量,分别为“flyBehavior”和“quackBehavior”声明为接口类型(每个鸭子对象会动态的设置这些变量,在运行时引用正确的行为类型)

2.    将Duck类中的Quack()和Fly()方法删除,同时新增PerformFly()和PerformQuack()来取代这两个类

3.    实现PerformFly()和PerformQuack(),如下所示:

/// Description:鸭子超类
/// </summary>
public abstract class Duck
{
public IFlyable flyBehavior;
public IQuackablequackBehavior; public void PerformFly()
{
flyBehavior.Fly();
} public void PerformQuack()
{
quackBehavior.Quack();
} public void Swim()
{
Console.WriteLine("鸭子游泳");
} public abstract voidDisplay();
}

这样做的结果就是我们可以忽略flyBehavior和quackBehavior接口对象到底是什么,只需要关心该对象如何进行相应的行为即可。

4.    设定flyBehavior类和quackBehavior类的实例变量,如下所示:

 public class MallardDuck : Duck
{
public MallardDuck()
{
flyBehavior = newFlyWithWings();//使用FlyWithWings类处理飞行,当PerformFly()被调用时,飞的职责被委托给FlyWithWings对象,得到真正的飞
quackBehavior = newQuack();
} public override voidDisplay()
{
Console.WriteLine("绿头鸭");
}
}

说明:

当MallardDuck实例化的时候,构造器会把继承自quackBehavior的实例变量初始化成Quack类型的新实例,同样对于飞的行为也是如此。

5.    测试,如下:

  Duck.Duck mallard = new MallardDuck();

            mallard.PerformFly();

            mallard.PerformQuack();

            Console.Read();

结果如下:

动态的设定行为

为了能够充分的用到我们之前建的一些鸭子的动态行为,我们可以在鸭子子类中通过设定方法来设定鸭子的行为,而不是在样子的构造器内实例化。具体步骤如下:

1.    在Duck类中新增两个设置方法,如下:

public void SetFlyBehavior(IFlyable fly)
{
flyBehavior = fly;
} public voidSetQuackBehavior(IQuackable quack)
{
quackBehavior = quack;
}

2.    创建新的鸭子类型

 public class ModelDuck : Duck
{
public ModelDuck()
{
flyBehavior = newFlyNoWay();
quackBehavior = newSqueak();
}
public override void Display()
{
Console.WriteLine("模型鸭");
}
}

3.    创建一个新的FlyBehavior类型

public class FlyRocket : IFlyable
{
public void Fly()
{
Console.WriteLine("Fly with Rocket!");
}
}

4.    测试

Duck.Duck model = new ModelDuck();
model.PerformFly();
model.PerformQuack();
model.SetFlyBehavior(newFlyRocket());
model.PerformFly();

结果如下:

从上边的结果中我们可以看出,在初始化ModelDuck的时候,我们对flyBehavior对象进行了FlyNoWay的初始化,所以显示的飞行的行为为:不会飞,后边我们又通过SetFlyBehavior方法动态设置了flyBehavior的实例,所以后边就有了Fly with Rocket的行为啦。

上边的例子对于每一个鸭子都有一个FlyBehavior和QuackBehavior,当你将两个类结合起来使用,就是组合,这种做法和继承的不同之处在于,鸭子的行为不是继承来的,而是合适的对象组合来的。这里用到了面向对象的第三个设计原则:

多用组合,少用继承

通过刚刚讲的这么多,引出了本章的第一个模式:

策略模式:定义了算法簇(这里的算法簇就相当于一组行为),分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。

3.   总结

通过本章的学习,我们可以了解掌握以下知识:

1.    面向对象设计的原则(部分):

l 封装变化

l 多用组合,少用继承

l 针对接口编程,不针对实现编程

2.    面向对象模式---策略模式:

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

Head First设计模式之策略模式(Strategy Pattern)的更多相关文章

  1. 乐在其中设计模式(C#) - 策略模式(Strategy Pattern)

    原文:乐在其中设计模式(C#) - 策略模式(Strategy Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 策略模式(Strategy Pattern) 作者:webabc ...

  2. 反馈法学习设计模式(一)——策略模式Strategy Pattern

    简介(Introduction) 之前学习Java8实战时,遇到一个很好的策略模式示例.便想着借着这个示例结合反馈式的方法来,学习策略设计模式,也以便后面反复琢磨学习. 首先我们通过练习,逐步写出符合 ...

  3. 8.6 GOF设计模式四: 策略模式… Strategy Pattern

    策略模式… Strategy Pattern  在POS系统中,有时需要实行价格优惠, 该如何处理?  对普通客户或新客户报全价  对老客户统一折扣5%  对大客户统一折扣10%  注:课件 ...

  4. 二十四种设计模式:策略模式(Strategy Pattern)

    策略模式(Strategy Pattern) 介绍定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换.本模式使得算法的变化可独立于使用它的客户. 示例有一个Message实体类,对它的操作有 ...

  5. 设计模式原来如此-策略模式(Strategy Pattern)

    策略模式中体现了两个非常基本的面向对象设计的原则:1.封装变化的概念.2.编程中使用接口,而不是对接口的实现. 策略模式的定义:定义一组算法,将每个算法都封装起来,并使它们之间可以互换.策略模式使这些 ...

  6. 【UE4 设计模式】策略模式 Strategy Pattern

    概述 描述 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法的变化不会影响到使用算法的客户. 套路 Context(环境类) 负责使用算法策略,其中维持了一 ...

  7. 设计模式(一):“穿越火线”中的“策略模式”(Strategy Pattern)

    在前段时间呢陆陆续续的更新了一系列关于重构的文章.在重构我们既有的代码时,往往会用到设计模式.在之前重构系列的博客中,我们在重构时用到了“工厂模式”.“策略模式”.“状态模式”等.当然在重构时,有的地 ...

  8. 设计模式 - 策略模式(Strategy Pattern) 具体解释

    策略模式(Strategy Pattern) 具体解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26577879 本文版权全 ...

  9. HeadFirst设计模式读书笔记(1)-策略模式(Strategy Pattern)

    策略模式(Strategy Pattern): 定义了了算法簇,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户端. 第一个设计原则:找出应用中可能需要变化之处,把他们独立 ...

  10. JAVA设计模式之策略模式 - Strategy

    在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改.这种类型的设计模式属于行为型模式. 在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 ...

随机推荐

  1. Vue.js常用指令总结

    有时候指令太多会造成记错.记混的问题,所以本文在记忆的时候会采用穿插记忆的方式,交叉比对,不易出错. 本文主要讲了一下六个指令: v-if//v-show//v-else//v-for//v-bind ...

  2. Ubuntu下libpcap安装

    1.首先按下面的博客教程下载和安装四个软件包: 点击打开链接 2.这四个软件都安装好之后按下面教程新建Makefile文件和test.c文件: 点击打开链接 Makefie: all: test.c ...

  3. BSTR 的奥秘

    初学COM,总觉得BSTR很神秘,对于这种新的数据类型,总有很多疑问,那么BSTR到底是什么类型呢? 我们可以在头文件中的定义中最终找到 wchar这个类型被定义为BSTR,那么BSTR是wchar吗 ...

  4. HTTP 响应头信息

    HTTP 响应头信息 HTTP请求头提供了关于请求,响应或者其他的发送实体的信息. 在本章节中我们将具体来介绍HTTP响应头信息.

  5. SpringMvc静态资源加载出错

    使用mvc:resource配置 web.xml配置是rest风格的/ 服务器启动没问题 访问地址是报404 另外用了default-servlet的方法加载,服务器启动没错,jsp页面加载静态资源要 ...

  6. Python常用函数、方法、模块记录

    常用函数: 1.pow():乘方 2.abs():绝对值 3.round():四舍五入 4.int():转换为整数 5.input():键盘输入(会根据用户的输入来做类型的转换) raw_input( ...

  7. Win10/UWP新特性—SharedStorageAccessManager 共享文件

    首先先给大家推荐一个UWP/Win10开发者群:53078485  里面有很多大婶,还有很多学习资源,欢迎大家来一起讨论Win10开发! 在UWP开发中,微软提供了一个新的特性叫做SharedStor ...

  8. A - 迷宫问题

      Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit Status Pra ...

  9. Mac快捷键与命令学习

    最近开始使用mac air,以前从来没有接触过IOS系统,各种操作捉急.Mac快捷键相当多,遇到各种操作不会就只好百度,然后整理了一堆有用或者没用的命令,一股脑儿列在下面.其中有不少命令是和linux ...

  10. 使用Quartz.net动态设置定时时间问题

    关于使用Quartz.net就不用解释了.. 应客户需求问题..需要将做一个界面设置定时的时间.因此我在百度一番..用CronExpression类可以设置时间... 我知道这个类有定义好的字段..不 ...