面向对象设计模式与原则曾经的学习笔记

 

 

面向对象设计模式与原则

 

  1. 序章

设计模式是前人总结的软件设计开发经验,大多数都能用代码来体现。学习设计模式最主要的是理解其中的设计思想。

1.1设计模式简介

 

每个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。

--Christopher Alexander(像旅游中介、房屋中介)

设计模式描述了软件设计过程中某一类常见问题的一般性解决方案。(核心)

面向对象设计模式描述了面向对象设计过程中、特定场景下、类与相互通信的对象之间常见的组织关系。(类似问题解决方案的重用)

找出我们所面对的事情共同的部分抽象之后形成一般性解决方案就产生了设计模式。

 

 

设计模式发展至今有很多的说法或者说是流派,比较认可的应该是gof的23中设计模式,但设计模式决不仅与此23.

历史性著作《设计模式:可复用面向对象软件的基础》一书描述了23中经典面向对象设计模式,创立了模式在软件设计中的地位。该书四位作者被人们称为Gang of Four (GoF),"四人组",该书描述的23中经典设计模式又被人们称为GoF23种设计模式。

 

抽象出核心就形成了设计模式。

 

由于《设计模式:可复用面向对象软件的基础》一书确定了设计模式的地位,人们通常所说的设计模式隐含地表示"面向对象设计模式"。但这并不意味"设计模式"就等于"面向对象设计模式",也不以为着GoF23种设计模式就表示了所有的"面向对象设计模式"。除了,"面向对象设计模式"外,还有其它模式。除了Gof23种设计模式外,还有更多的面向对象的设计模式。

Gof23种设计模式是学习面向对象设计模式的起点,而非终点。

 

面向对象设计模式解决的是"类与相互通信的对象之间的组织关系",包括它们的角色、职责、协作方式几个方面。

面向对象设计模式是"好的面向对象的设计",所谓"好的面向对象设计"是那些可以满足"应对变化,提高复用的设计。

面向对象设计模式描述的是软件设计,因此它是独立于编程语言的,但是面向对象设计模式的最终实现仍然要使用面向对象编程语言来表达。

面向对象设计模式不像算法技巧,可以照搬照用,它是建立在对"面向对象"纯熟、深入的理解的基础上的经验性认识。掌握面向对象设计模式的前提是首先掌握"面向对象"!

 

从面向对象语言的角度来理解设计模式:

各种面向对象编程语言相互有别,但都能看到它们面向对象三大机制的支持,即:"封装、继承、多态"

封装,隐藏内部实现

继承,复用现有代码

多态,改写对象行为

使用面向对象编程语言,可以推动程序员以面向对象的思维来思考软件设计结构,从而强化面向对象的编程范式。

C#是一门支持面向对象编程的优秀语言,包括:各种级别的封装支持;单实现继承+多接口实现;抽象方法与虚方法重写。

 

通过面向对象编程语言(OOPL)认识到的面向对象,并不是面向对象的全部,甚至只是浅陋的面向对象。

OOPL的三大机制"封装、继承、多态"可以表达面向对象的所有感念,但这三大机制本身并没有刻画出面向对象的核心精心。换言之,既可以用这三大机制做出"好的面向对象的设计",也可以用这三大机制做出"差的面向对象设计"。不是使用了面向对象的语言,就实现了面向对象设计与开发。因此我们不能依赖编程语言的面向对象机制,来掌握面向对象。

OOPL没有回答面向对象的根本问题—我们为什么要使用面向对象?我们应该怎样使用三大机制来实现"好的面向对象设计"?我们应该遵循什么样的面向对象原则?

任何一个严肃的面向对象的程序员,都需要系统地学习面向对象的知识,单纯从编程语言上获得的面向对象知识,不能够胜任面向对象设计与开发。

1.2从设计原则到设计模式

 

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

    客户无需知道所使用对象的特定类型,只需要知道对象拥有客户所期望的接口。

优先使用对象组合,而不是类继承

    类继承通常为"白箱复用",对象组合通常为"黑箱复用"。继承在某种程度上破坏了封装性,子类父类耦合度高;而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低。封装变化点

    使用封装来创建对象之间的分界层,让设计可以在分界层的一侧进行修改,而不会对另一个产生不良的影响,从而实现层次间的松耦合。

使用重构得到模式

    设计模式的应用不宜先入为主,一上来就使用设计模式是对设计模式的最大吴用。没有一步到位的设计模式。敏捷软件开发实践提倡的"Refactoring to Patterns"是目前普遍公认的最好的使用设计模式的方法。(升级版本,逐渐达到高稳定性、高扩展性

 

(封装和降低耦合度)设计模式是用来解决问题的而不是用来作秀的。

1.3几条更具体的设计原则

 

单一职责原则(SRP Single Responsibility Principle):一个类应该仅有一个引起它变化的原因。

开放闭合原则(OCP Open Closed Princile):类模块应该是可扩展的,但是不可修改(对扩展开放、对更改封闭)

Liskov替换原则(LSP Liskov Substitution Principle):子类必须能够替换它们的基类

依赖倒置原则(DIP Dependency Inversion Principle):

  1. 高层模块不应该依赖于底层模块,二者都应该依赖于抽象
  2. 抽象不应该依赖于实现细节,实现细节应该依赖于抽象

接口隔离原则(ISP Interface Segregation Principle):

  1. 不应该强迫客户程序依赖它们不用的方法
  2. 尽量应用专门的接口,而不是单一的总接口,接口应该面向对象用户,将依赖建立在最小的接口上

合成/聚合复用原则(CARP Composit /Aggregate Reuse Principle):

  1. 在新对象中聚合已有对象,使之成为新对象的成员,从而通过操作这些对象达到复用的目的。
  2. 合成方式较继承方式耦合更松散,所以应该少继承、多聚合

迪米特法则(LoD LoW OF Demeter):又叫最小知识原则,指软件实体应该尽可能少的和其它软件实体发生相互作用

1.4模式分类

 

1.从目的来看

创建型(Creational):负责对象创建

结构型(Structural):处理类与对象之间的组合

行为型(Behavioral):类与对象交互中的职责分配

2.从范围来看

类模块处理类与子类的静态关系

对象模式处理对象间的动态关系

2.Singleton单件(单例)--创建型模式

 

2.1动机

 

在软件系统中,经常有这样一些特殊类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性、以及良好的效率。

如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?

这应该是类设计者的责任,而不是使用者的责任。

2.2.意图

 

保证一个类仅有一个实例,并且提供一个该实例的全局访问点。-《设计模式》GoF    

2.3结构

 

2.4生活中的例子

 

美国总统的职位是Singleton,美国宪法规定了总统的选举,任期以及继任的顺序。这样,在任何时刻只能由一个现任的总统。无论现任总统的身份为何,其头衔"美利坚合众国总统"是访问这个职位的人的一个全局的访问点。

2.5简单实现

 

单线程已经满足使用

按照构造函数的执行顺序,子类实例化时会先实例化其父类,这样就很难控制了。所以单例模式类一般为sealed类型。

对于线程来说不安全

单线程中已经满足条件

优点:

由于实例是在Instance属性方法内部创建的,因此类可以使用附加的功能。

直到对象要求产生一个实例才执行实例化;这种方法称为"惰性实例化"。惰性实例化避免了在应用程序启动时实例化不必要的Singleton。

2.6.线程的安全

 

多线程应用

同一个时刻加了锁的那部分程序只有一个线程可以进入

对象实例由先进入的那个线程创建

后来的线程在进入时(instance==null)为假,不会再去创建对象实例

增加了额外的开销,损失了性能

 

双层锁定版本:

多线程安全

单线程不是每次都加锁

允许实例化延迟第一次访问对象时发生

静态初始化版本:

依赖公共语言运行库负责处理变量初始化

公共静态属性为访问实例提供一个全局的访问点

对实例化机制的控制权较少(.net代码实现)

静态初始化是在.net中实现Singleton的首选方法

 

延迟初始化:

初始化工作由Nested类的一个静态成员来完成,这样就实现了延迟初始化。

2.7注意事项

 

Singleton模式中的实例构造器可以设置为protected以允许子类派生。

Singleton模式一般不要支持IConeable接口,因为这可能会导致多个对象实例,与Singleton模式的初衷违背。

Singelton模式一般不要支持序列化,因为这也有可能导致多个对象实例,同样Singleton模式的初衷违背

Singleton模式只考虑到了对象创建的管理,没有考虑对象销毁的管理。就支持垃圾回收的平台和对象开销来讲,我们一般没有必要销毁进行特殊的管理。

2.8总结

 

Single模式是限制而不是改进类的创建。

理解和扩展Singleton模式的核心是"如何控制用户使用new对一个类的构造器的任意调用"。

可以很简单的修改一个Singleton,使他有少数几个实例,这样做是允许的而且是有意义的。

3.Builder生成器(建造者)--创建型模式

 

3.1动机(Motivation)

 

在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是它们组合在一起的算法却相对稳定。

如何应对这种变化?如何提供一种"封装机制"来隔离出"复杂对象的各个部分"的变化,从而保持系统中的"稳定构建算法"不随着需求改变而改变?

3.2意图(Intent)

 

将一个复杂对象的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

《设计模式》GoF

3.3机构(Structure)

 

3.4结构详解

 

3.5生活中的例子

 

客户找到包工头,要求建一个房子,

包工头找到不同的建筑队(Builder)来建筑房子的每个部分,建好房子后,交付给客户

客户不关心房子的建造细节,由包工头负责管理房子构建细节

3.6UML类图

 

3.7构建者模式的几种演变

 

3.7.1.版本一(省略建造者抽象角色)

 

系统中只需要一个具体的建造者,省略掉抽象建造者

 

3.7.2版本二(省略指导者角色)

 

抽象建造者角色已经被省略掉,还可以省略掉指导者角色。

让Builder角色自己扮演指导者与建造者双重角色。

3.7.3版本三(合并建造者角色和产品角色)

 

具体建造者角色和产品角色合并,从而使得产品自己就是自己的建造者。

这样做混淆了对象的建造者和对象本身,但是有时候一个产品对象有着固定的几个零件,而且永远只有这几个零件,此时将产品类和建造类合并,可以使系统简单易读。

3.7.4StringBuilder

 

.net中简化的Builder模式

System.Text.StringBuilder sb = new StringBuilder();

sb.Append("aa");

sb.Append("bb");

    string str= sb.ToString();

3.8效果

 

建造者模式的使用使得产品的内部表象可以独立的变化。使用建造者模式可以使客户端不必知道产品内部组成的细节。

每一个Builder都相对独立,而与其它的Builder无关。

可使对构造过程更加精细控制。

将构建代码和表示代码分开。

建造者模式的缺点在于难于应付"分步骤构建算法"的需求变动。

3.9适用性

 

以下情况应当使用建造者模式:

1、需要生成的产品对象有复杂的内部结构。
2、需要生成的产品对象的属性相互依赖,建造者模式可以强迫生成顺序。
3、 在对象创建过程中会使用到系统中的一些其它对象,这些对象在产品对象的创建过程中不易得到。

3.10总结

 

Builder 模式主要用于"分步骤构建一个复杂的对象"。在这其中"分步骤"是一个稳定的算法,而复杂对象的各个部分则经常变化。

变化点在哪里,封装哪里—— Builder模式主要在于应对"复杂对象各个部分"的频繁需求变动。其缺点在于难以应对"分步骤构建算法"的需求变动。

Abstract Factory模式解决"系列对象"的需求变化,Builder模式解决"对象部分"的需求变化。

Builder模式通常和Composite模式组合使用。

3.11代码案例

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP03Builder

{

class Program

{

static void Main(string[] args)

{

House myhouse = null;

Director director = new Director();

myhouse = director.BuildHomeHouse();//普通房子

Console.WriteLine(myhouse);

 

House mygoodhouse = null;

mygoodhouse = director.BuildGoodHouse();//别墅

Console.WriteLine(mygoodhouse);

}

}

 

/// <summary>

/// 产品

/// </summary>

public class House

{

 

/// <summary>

/// 地基

/// </summary>

public string Base;

/// <summary>

/// 墙体

/// </summary>

public string Wall;

/// <summary>

/// 屋顶

/// </summary>

public string Roof;

 

public override string ToString()

{

return string.Format("基地:{0}\r\n墙体:{1}\r\n屋顶:{2}", this.Base, this.Wall, this.Roof);

}

}

 

/// <summary>

/// 抽象

/// </summary>

public abstract class Builder

{

public abstract void BuildPart(House house);

 

}

 

/// <summary>

/// 普通房子的建筑队-地基

/// </summary>

public class RoomBaseBuilder : Builder

{

public override void BuildPart(House house)

{

house.Base = "普通房子的地基";

}

}

/// <summary>

/// 普通房子的建筑队-墙体

/// </summary>

public class RoomWallBuilder : Builder

{

public override void BuildPart(House house)

{

house.Wall = "普通房子的墙体";

}

}

/// <summary>

/// 普通房子的建筑队-屋顶

/// </summary>

public class RoomRoofBuilder : Builder

{

public override void BuildPart(House house)

{

 

house.Roof = "普通房子的屋顶";

}

}

 

 

/// <summary>

/// 包工头

/// </summary>

public class Director

{

private List<Builder> homeBuilders = new List<Builder>();

 

private Dictionary<string, Builder> goodHouseBuilders = new Dictionary<string, Builder>();

 

/// <summary>

/// 聚合多个建筑队

/// </summary>

public Director()

{

this.homeBuilders.Add(new RoomBaseBuilder());

this.homeBuilders.Add(new RoomWallBuilder());

this.homeBuilders.Add(new RoomRoofBuilder());

 

 

///

 

this.goodHouseBuilders.Add("Wall", new GoodHouseWallBuilder());

this.goodHouseBuilders.Add("Base", new GoodHouseBaseBuilder());

this.goodHouseBuilders.Add("Roof", new GoodHouseRoofBuilder());

}

//稳定构造对象的子部分

public House BuildHomeHouse()

{

House house = new House();

foreach (var builder in this.homeBuilders)

{

builder.BuildPart(house);

}

return house;

}

 

/// <summary>

/// 建造别墅

/// </summary>

/// <returns></returns>

public House BuildGoodHouse()

{

House house = new House();

//order:

Builder builder1 = this.goodHouseBuilders["Base"];

builder1.BuildPart(house);

 

this.goodHouseBuilders["Wall"].BuildPart(house);

this.goodHouseBuilders["Roof"].BuildPart(house);

 

return house;

}

 

 

 

}

 

 

/// <summary>

/// 别墅的地基建筑队-地基

/// </summary>

public class GoodHouseBaseBuilder:Builder

{

 

public override void BuildPart(House house)

{

house.Base = "别墅的地基";

}

}

 

/// <summary>

/// 别墅的 建筑队-

/// </summary>

public class GoodHouseWallBuilder : Builder

{

 

public override void BuildPart(House house)

{

house.Wall = "别墅的墙体";

}

} /// <summary>

/// 别墅的 建筑队-屋顶

/// </summary>

public class GoodHouseRoofBuilder : Builder

{

 

public override void BuildPart(House house)

{

house.Roof = "别墅的屋顶";

}

}

}

4.Abstact Factory 简单工厂-(创建型模式)

 

4.1简单工厂    

 

只有一个工厂(具体的,没有抽象)

只生产一种产品(抽象的产品)

这种产品可以有多种具体产品类型(派生)

4.2简单工厂实现-产品    

 

4.3简单工厂实现-具体产品

 

4.4简单工厂实现-工厂

 

4.5简单工厂实现-使用

 

操作 operation = 简单工厂.创建具体操作("*");

operation.NumberA = 10;

operation.NumberB = 20;

operation.计算结果();

4.6简单工厂小结    

 

  • 只有一种类型的产品(与子类数量无关)
  • 只有一个工厂
  • 一个工厂只生产一种产品

 

5.FactoryMethod工厂方法模式(创建型模式)

 

5.1动机(Motivation)

 

在软件系统中,经常面临着"某个对象"的创建工作;由于需求的变化,这个对象经常面临着剧烈的变化,但是它却拥有比较稳定的接口。

如何应对这种变化?如何提供一种"封装机制"来隔离出"这个易变的对象" 不随着需求改变而改变。

5.2意图(Intent)

 

定义一个用于创建对象的接口,让子类决定实例化那一个类。FactoryMethod使得一个类的实例化延迟。《设计模式》GoF

5.3结构(Structure)

 

5.4结构详解

 

5.5生活中的例子

 

塑料玩具制造商加工塑料玩具,将塑料注入到希望形状的模具中。玩具的类别(车,人物等等)是由模具决定的。

5.6实现-Product

 

public abstract class 玩具

{

}

public class 玩具小马 : 玩具

{

public override string ToString()

{

return "小马";

}

}

public class 玩具汽车 : 玩具

{

public override string ToString()

{

return "汽车";

}

}

5.7实现-Creator

 

public abstract class 模具

{

protected abstract 玩具 压制();

public virtual 玩具 得到玩具()

{

玩具 toy = this.压制();

return toy;

}

}

public class 小马模具:模具

{

protected override 玩具 压制()

{

return new 玩具小马();

}

}

public class 汽车模具:模具

{

protected override 玩具 压制()

{

return new 玩具汽车();

}

}

}

5.8实现-使用

 

模具 model = null;

if (true)

{

model = new 汽车模具();

}

else

{

model = new 小马模具();

}

玩具 toy = model.得到玩具();

Console.WriteLine(toy);

5.9实现要点

 

Factory Method模式的两种情况:一是Creator类是一个抽象类且它不提供它所声明的工厂方法的实现;二是Creator是一个具体的类且它提供一个工厂方法的缺省实现。

工厂方法是可以带参数的。

工厂的作用并不仅仅只是创建一个对象,它还可以做对象的初始化,参数的设置等。

5.10效果

 

用工厂方法在一个类的内部创建对象通常比直接创建对象更灵活。

Factory Method模式通过面向对象的手法,将所要创建的具体对象的创建工作延迟到了子类,从而提供了一种扩展的策略,较好的解决了这种紧耦合的关系。

5.11适用性

 

在以下情况下,适用于工厂方法模式:

1. 当一个类不知道它所必须创建的对象的类的时候。

2. 当一个类希望由它的子类来指定它所创建的对象的时候。

3. 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。

5.12巴比馒头的故事

 

一家门店,只卖一个系列产品:馒头(玉米满头,高粱馒头)
简单工厂

多家门店(分店),但还只卖一个系列产品(馒头)
工厂方法

多家门店,产品系列丰富:馒头(同上)+包子(芥菜包子,梅干菜包子)
抽象工厂

5.12总结

 

Factory Method模式主要用于隔离类对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体类型,紧耦合关系会导致软件的脆弱。

Factory Method模式通过面向对象的手法,将所要创建的具体对象工作延迟到子类,从而实现一种扩展(而非更改)的策略,较好地解决了这种紧耦合关系。

Factory Method模式解决"单个对象"的需求变化,Abstract Factory 模式解决"系列对象"的需求变化,Builder模式解决"对象部分"的需求变化。

5.13代码实现

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP02AbstractFactory_FactoryMethod

{

class Program

{

static void Main(string[] args)

{

Console.WriteLine("请输入第一个数字");

int a = int.Parse(Console.ReadLine());

 

Console.WriteLine("请输入第二个数字");

int b = int.Parse(Console.ReadLine());

 

 

Console.WriteLine("请输入运算符号");

string op = Console.ReadLine();

 

int result;

 

//////////////////////////////

 

Computer com = null;//期望的产品

Factory factory = null;

 

/////////////////////////////工厂的创建和实例化--------------->反射技术动态创建工厂的实例

switch (op)

{

case "+":

factory = new FactoryAdd();

 

break;

case "-":

 

factory = new FactoryJian();

 

break;

case "*":

 

factory = new FactoryCheng();

 

break;

case "/":

factory = new FactoryChu();

break;

case "^":

factory = new FactoryPow();

break;

default:

com = new AddComputer();

break;

}

//////////////////////////

 

com = factory.CreateComputer();

 

 

com.NumberA = a;

com.NumberB = b;

result = com.Result;

 

//////////////////////////////

 

Console.WriteLine("{0}{1}{2}={3}", a, op, b, result);

}

 

 

 

}

 

 

//public static class SimpleFactory

//{

// /// <summary>

// /// 简单的工厂的方法

// /// *封装了对象实例创建的复杂度

// /// </summary>

// /// <param name="op">用来区分具体产品的标识</param>

// /// <returns>产品的抽象</returns>

// public static Computer LoadComputer(string op)

// {

// Computer com = null;

 

 

 

// switch (op)

// {

// case "+":

// com = new AddComputer();

 

// break;

// case "-":

 

// com = new JianComputer();

 

// break;

// case "*":

 

// com = new ChenComputer();

 

// break;

// case "/":

 

// com = new ChuComputer();

 

// break;

// case "^":

// com = new PowComputer();

// break;

// default:

// com = new AddComputer();

// break;

// }

 

// return com;

// }

//}

 

public abstract class Factory

{

public abstract Computer CreateComputer();

 

}

//public class FactoryV2

//{

// public virtual Computer CreateComputer()

// {

// return new AddComputer();//默认缺省实现

// }

//}

 

//public class demo : FactoryV2

//{

// public override Computer CreateComputer()

// {

// return new JianComputer();

// }

 

//}

public class FactoryAdd : Factory

{

public override Computer CreateComputer()

{

return new AddComputer();

}

}

 

public class FactoryJian : Factory

{

public override Computer CreateComputer()

{

return new JianComputer();

}

}

 

public class FactoryCheng : Factory

{

public override Computer CreateComputer()

{

return new ChenComputer();

}

}

 

public class FactoryChu: Factory

{

public override Computer CreateComputer()

{

return new ChuComputer();

}

}

 

public class FactoryPow : Factory

{

public override Computer CreateComputer()

{

return new PowComputer();

}

}

 

 

/// <summary>

/// 运算

/// </summary>

public abstract class Computer

{

protected int _NumberA;

 

public int NumberA

{

get { return _NumberA; }

set { _NumberA = value; }

}

 

protected int _NumberB;

 

public int NumberB

{

get { return _NumberB; }

set { _NumberB = value; }

}

 

/// <summary>

/// 子类必须完成的功能:计算结果并返回结果

/// </summary>

public abstract int Result

{

get;

}

}

 

 

public class AddComputer : Computer

{

public override int Result

{

get

{

return base._NumberA + base._NumberB;

}

}

}

 

 

public class JianComputer : Computer

{

public override int Result

{

get

{

return base._NumberA - base._NumberB;

}

}

}

 

public class ChenComputer : Computer

{

public override int Result

{

get

{

return base._NumberA * base._NumberB;

}

}

}

public class ChuComputer : Computer

{

public override int Result

{

get

{

if (base._NumberB == 0)

{

throw new ArgumentException("被除数不能为零");

}

return base._NumberA / base._NumberB;

}

}

}

 

public class PowComputer : Computer

{

public override int Result

{

get

{

return (int)Math.Pow(base._NumberA, base._NumberB);

}

}

}

 

 

}

 

6.Abstract Factory 抽象工厂(创建型模式)

 

6.1动机(Motivation)

 

在软件系统中,经常面临着"一系列相互依赖的对象"的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。

如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种"封装机制"来避免客户程序和这种"多系列具体对象创建工作"的紧耦合?

6.2意图(Intent)

 

提供一个接口,让该接口负责创建一系列"相关或者相互依赖的对象",无需指定它们具体的类。

——《设计模式》GoF

6.3结构(Structure)

 

6.4结构详解

 

6.5生活中的例子(抽象工厂)

 

  • 冲压设备可以制造汽车车身部件。不同的机械1用于冲压不同的车型2车门、挡泥板和引擎罩等等3。通过使用转轮来改变冲压盘4,这个机械产生的具体类可以在三分钟内改变。
  • 1.不同的具体工厂
  • 2.不同产品系列
  • 3.具体生产的产品
  • 4.变化点,决定生产的产品

6.6抽象工厂实现-抽象定义

 

public abstract class 工厂

{

public abstract 车门 生产车门();

public abstract 引擎 生产引擎();

}

public abstract class 车门

{

public abstract string 颜色 { get;}

}

public abstract class 引擎

{

public abstract int 功率 { get;}

public abstract void 发动();

}

6.7抽象工厂实现-抽象产品(车门)的实现

 

public class 越野车门 : 车门

{

public override string 颜色

{

get

{

return "红色";

}

}

}

public class 拖拉机车门 : 车门

{

public override string 颜色

{

get { return "色即是空,空即是色.--拖拉机车门没有颜色"; }

}

}

6.8抽象工厂实现-抽象产品(引擎)的实现

 

6.9抽象工厂实现-使用

 

工厂 factory= null;

//选工厂

factory = new 拖拉机工厂();

factory = new 越野车工厂();//核心在此:只需要改变工厂,不会影响后续代码(产品)的使用

车门 door = null;

引擎 power = null;

//开始生产

door = factory.生产车门();

power = factory.生产引擎();

//产品生产后,怎么用是另一个话题

Console.WriteLine(door.颜色);

Console.WriteLine(power.功率);

power.发动();

6.10使用反射

 

  • 动态加载一个程序集(exe,dll):
    • System.Reflection.Assembly.LoadFile(DLLfilename)
  • 获得一个程序集里的所有类型:
    • Assembly的实例.GetTypes()
  • 判断一个Type是否继承自指定的类:
    • Type的实例. IsSubclassOf(Type)
  • 获得一个Type的全名(包含所在命名空间):
    • Type的实例.FullName
  • 根据一个Type的全名,创建它的实例:
    • Assembly的实例. CreateInstance(Type的全名)

6.11实现要点

 

  • 抽象工厂将产品对象的创建延迟到它的具体工厂的子类。
  • 通常在运行时刻创建一个具体工厂类的实例,这一具体工厂的创建具有特定实现的产品对象,为创建不同的产品对象,客户应使用不同的具体工厂。
  • 把工厂作为单件,一个应用中一般每个产品系列只需一个具体工厂的实例,因此,工厂通常最好实现为一个单件模式。

创建产品,抽象工厂仅声明一个创建产品的接口,真正创建产品是由具体工厂类创建的,最通常的一个办法是为每一个产品定义一个工厂方法,一个具体的工厂将为每个产品重定义该工厂方法以指定产品,虽然这样的实现很简单,但它要求每个产品系列都要有一个新的具体工厂子类,即使这些产品系列的差别很小。

6.12适用性

 

  • 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。
  • 这个系统有多于一个的产品族,而系统只消费其中某一产品族。
  • 同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。

系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。

6.13总结

 

  • 如果没有应对"多系列对象构建"的需求变化,则没有必要使用Abstract Factory模式,这时候使用简单的静态工厂完全可以。
  • " 系列对象"指的是这些对象之间有相互依赖、或作用的关系,例如游戏开发场景中的"道路"与"房屋"的依赖,"道路"与"地道"的依赖。
  • Abstract Factory模式主要在于应对"新系列"的需求变动。其缺点在于难以应对"新对象"的需求变动。
  • Abstract Factory模式经常和Factory Method模式共同组合来应对"对象创建"的需求变化。

6.14实现代码    

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Windows.Forms;

 

namespace AbstractFactory04

{

static class Program

{

/// <summary>

/// 应用程序的主入口点。

/// </summary>

[STAThread]

static void Main()

{

Application.EnableVisualStyles();

Application.SetCompatibleTextRenderingDefault(false);

Application.Run(new Form1());

}

}

/// <summary>

/// 系列工厂

/// </summary>

public abstract class Factory

{

public abstract Shoes CreateShoes();

public abstract Hat CreateHats();

}

 

public class NIKEFactory : Factory

{

public override Shoes CreateShoes()

{

return new NIKEShoes();

}

 

public override Hat CreateHats()

{

return new NIKEHat();

}

}

 

public class LININGFactory : Factory

{

public override Shoes CreateShoes()

{

return new LININGShoes();

}

 

public override Hat CreateHats()

{

return new LININGHat();

}

}

 

 

 

 

/// <summary>

/// 鞋子系列

/// </summary>

public abstract class Shoes

{

}

/// <summary>

/// 帽子系列

/// </summary>

public abstract class Hat

{

}

 

public class NIKEShoes : Shoes

{

public override string ToString()

{

return "耐克鞋子";

}

}

public class NIKEHat : Hat

{

public override string ToString()

{

return "耐克帽子";

}

}

 

 

public class LININGShoes : Shoes

{

public override string ToString()

{

return "李宁鞋子";

}

}

public class LININGHat : Hat

{

public override string ToString()

{

return "李宁帽子";

}

}

 

}

----------------------------------------------------------------使用-------------------------------------------------------

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Windows.Forms;

 

namespace AbstractFactory04

{

public partial class Form1 : Form

{

public Form1()

{

InitializeComponent();

}

private System.Reflection.Assembly assembly = null;

private void Form1_Load(object sender, EventArgs e)

{

openFileDialog1.InitialDirectory = Application.StartupPath;

openFileDialog1.Filter = "类库|*.dll|可执行程序|*.exe";

if(openFileDialog1.ShowDialog()== DialogResult.OK)

{

assembly = System.Reflection.Assembly.LoadFile(openFileDialog1.FileName);

cbxClassLibiary.Items.Clear();

 

foreach(var item in assembly.GetTypes())

{

 

if(item.IsSubclassOf(typeof(Factory)))

{

cbxClassLibiary.Items.Add(item);

}

}

}

}

 

private void btnSubmit_Click(object sender, EventArgs e)

{

Hat hat = null;

Shoes shoes = null;

 

Factory factory = null;

 

factory = (Factory)this.assembly.CreateInstance(((Type)cbxClassLibiary.SelectedItem).FullName);

hat = factory.CreateHats();

shoes = factory.CreateShoes();

MessageBox.Show(hat.ToString()+":"+shoes.ToString());

}

}

}

 

7. Prototype 原型(创建型模式)

 

7.1动机(Motivation)

 

在软件系统中,经常面临着"某些结构复杂的对象"的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口。

如何应对这种变化?如何向"客户程序(使用这些对象的程序)"隔离出"这些易变对象" ,从而使得"依赖这些易变对象的客户程序"不随着需求改变而改变?

7.2意图(Intent)    

 

使用原型实例指定创建对象的种类,然后通过拷贝这些原型来创建新的对象。

——《设计模式》GoF

7.3结构(Structure)

 

7.4结构详解

 

7.5生活中的例子

 

在商品房销售系统中,房屋信息是基础信息。在系统运行前必须输入房屋的各种信息到系统中,这是一项枯燥的重复劳动。实际上,一个小区楼盘的样式并不多,不同的只是楼号。

从用户的角度看,系统最好支持拷贝/粘贴。例如复制一个现成的楼盘,粘贴后只要修改楼号即可,或者复制多个楼层粘贴后自动增加到楼层中,这样用户的交互过程要简单很多。

7.6实现

 

public abstract class 楼盘原型

{

private int _楼盘号 = 0;

public int 楼盘号 { get { return this._楼盘号; } set { this._楼盘号 = value; } }

private string _位置 = "";

public string 位置 { get { return this._位置; } set { this._位置 = value; } }

public abstract 楼盘原型 复制();

}

public class 楼盘:楼盘原型

{

public override 楼盘原型 复制()

{

楼盘 builds = new 楼盘();

builds.位置 = this.位置;

return builds;

}

}

7.7实现-使用

 

楼盘原型 build01 = new 楼盘();

build01.楼盘号 = 001;

build01.位置 = "西子湖畔";

楼盘原型 build02 = build01.复制();

build02.楼盘号 = 002;

楼盘原型 build03 = build02.复制();

build03.位置 = "灵岩山上";

build03.楼盘号 = 008;

Console.WriteLine("楼盘号:{0},在:{1}", build01.楼盘号, build01.位置);

Console.WriteLine("楼盘号:{0},在:{1}", build02.楼盘号, build02.位置);

Console.WriteLine("楼盘号:{0},在:{1}", build03.楼盘号, build03.位置);

7.8ICloneable    

 

  • public object Clone();
  • 实现ICloneable接口,即可实现Prototype模式

7.9ICloneable实现

 

public class 楼盘:ICloneable

{

private int _楼盘号 = 0;

public int 楼盘号 { get { return this._楼盘号; } set { this._楼盘号 = value; } }

private string _位置 = "";

public string 位置 { get { return this._位置; } set { this._位置 = value; } }

#region ICloneable 成员

public object Clone()

{

return this.MemberwiseClone();

}

#endregion

}

7.10ICloneable使用

 

Clone.楼盘 build01 = new Clone.楼盘();

build01.楼盘号 = 001;

build01.位置 = "西子湖畔";

Clone.楼盘 build02 = build01.Clone() as Clone.楼盘;

build02.楼盘号 = 002;

Clone.楼盘 build03 = build02.Clone() as Clone.楼盘 ;

build03.位置 = "灵岩山上";

build03.楼盘号 = 009;

Console.WriteLine("楼盘号:{0},在:{1}", build01.楼盘号, build01.位置);

Console.WriteLine("楼盘号:{0},在:{1}", build02.楼盘号, build02.位置);

Console.WriteLine("楼盘号:{0},在:{1}", build03.楼盘号, build03.位置);

7.11浅复制与深复制

 

如果一个对象是复合对象或者包含其他的对象,那么克隆对象的问题是仅仅复制对象本身,还是连同被引用的对象一起复制?

实现IClone接口的类基本上都采用浅复制的策略

MemberwiseClone():

值类型:逐位复制

引用类型:只复制引用(指针)但不复制引用的对象

深复制:把引用对象的变量,指向复制过的,新的对象,而不是原有的被引用的对象

7.12深复制实现-工作经历

 

public class 工作经历 : ICloneable

{

public string 公司 = "";

public string 职位 = "";

public object Clone()

{

return this.MemberwiseClone();//浅复制

}

public override string ToString()

{

return string.Format("在{0}公司任职{1}",this.公司,this.职位);

}

}

7.13深复制实现-简历

 

public class 简历 : ICloneable

{

public string 姓名 = "";

public int 年龄 = 0;

public string 申请职位 = "";

public 工作经历 以往工作经历 = new 工作经历();

public object Clone()

{

//深复制

简历 resume = new 简历();

resume.姓名 = this.姓名;

resume.年龄 = this.年龄;

resume.申请职位 = this.申请职位;

resume.以往工作经历 = this.以往工作经历.Clone() as 工作经历;

return resume;

}

public override string ToString()

{

return string.Format("申请职位:{0},姓名:{1},年龄:{2},工作经历:{3}", this.申请职位, this.姓名, this.年龄, this.以往工作经历);

}

}

7.14深复制实现-使用

 

Resume.简历 resume1 = new Resume.简历();

resume1.姓名 = "小朱";

resume1.年龄 = 20;

resume1.申请职位 = "软件工程师";

Resume.工作经历 workexperience = new Resume.工作经历();

workexperience.公司 = "IBM";

workexperience.职位 = "高级程序员";

resume1.以往工作经历 = workexperience;

Resume.简历 resume2 = resume1.Clone() as Resume.简历;

resume2.申请职位 = "架构师";

resume2.以往工作经历.公司 = "微软";

Console.WriteLine("简历1:{0}", resume1);

Console.WriteLine("简历2:{0}", resume2);

7.15.NET中的原型模式

 

  • DataSet
    • Clone():只复制结构,不复制数据à浅复制
    • Copy();复制结构,也复制数据à深复制

7.16实现要点

 

  • 善用ICloneable接口
  • 产品的创建和初始化在类的Clone方法中完成
  • 浅复制与深复制的区别
  • 需要深复制的场合需要开发人员根据需要实现
  • 有些情况下Clone功能不容易实现,特别是遇到对象的循环引用时

7.17效果

 

在运行时增加或删除产品:只要通过客户原型实例即可将新产品类型增加到系统中,例如组态软件中工具箱中的每个工具可以对应一个注册的原型对象,可以通过增加原形对象扩展工具箱。

很容易地创建复杂的对象:在图形编辑和组态等软件中,经常需要创建复杂的图元。这些图元是由简单图元组成的,采用原型模式可以很容易地将复杂图元作为一般图元来使用,使软件的工具箱具有自扩展功能。

7.18适用性

 

当一个系统应该独立于产品的创建、构成和表示时,可以使用原型模式。

在使用时,我们可以用一些原型对象来代替生成相应对象的工厂对象,并且可以使拷贝、粘贴等操作独立于需要复制的对象。

7.19总结

 

Prototype模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些"易变类"拥有"稳定的接口"。

Prototype模式对于"如何创建易变类的实体对象"采用"原型克隆"的方法来做,它使得我们可以非常灵活地动态创建"拥有某些稳定接口"的新对象——所需工作仅仅是注册一个新的对象(即原型),然后在任何需要的地方不断地Clone。

Prototype模式中的Clone方法可以利用.NET中的Object类的MemberwiseClone()方法或者序列化来实现深拷贝。

7.20实现代码

 

7.20.1版本一-简单实现

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace PrototypeVersionV1

{

class Program

{

static void Main(string[] args)

{

Prototype instance1 = new Build() { Address="希尔互通", Number=78};

 

Prototype instance2 = instance1.Clone();

 

Console.WriteLine(instance1);

Console.WriteLine(instance2);

 

Console.ReadKey();

}

}

 

public abstract class Prototype

{

public int Number { get; set; }

public string Address { get; set; }

 

public abstract Prototype Clone();

public override string ToString()

{

return string.Format("门牌号:{0},地址:{1}", this.Number, this.Address);

}

}

public class Build : Prototype

{

public override Prototype Clone()

{

Build newInstance = new Build();

newInstance.Number = this.Number + 1;

newInstance.Address = this.Address;

return newInstance;

}

}

 

}

7.20.2版本二-.net中的实现

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace PrototypeVersionV1

{

class Program

{

static void Main(string[] args)

{

Prototype instance1 = new Prototype() { Address = "希尔互通", Number = 78 };

 

Prototype instance2 = (Prototype)(instance1 as ICloneable).Clone();

 

Console.WriteLine(instance1);

Console.WriteLine(instance2);

 

Console.ReadKey();

}

}

 

public class Prototype:ICloneable

{

public int Number { get; set; }

public string Address { get; set; }

 

 

public override string ToString()

{

return string.Format("门牌号:{0},地址:{1}", this.Number, this.Address);

}

 

#region ICloneable 成员

 

public object Clone()

{

return base.MemberwiseClone();

}

 

#endregion

}

 

 

}

7.20.3版本三-深复制的实现

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace PrototypeVersion03

{

class Program

{

static void Main(string[] args)

{

}

}

 

public class Phone : ICloneable

{

#region ICloneable 成员

public string Name { get; set; }

public int Price { get; set; }

public Factory TheFactory { get; set; }

 

public object Clone()

{

Phone phone = base.MemberwiseClone() as Phone;

Factory factory = phone.TheFactory.Clone() as Factory;

phone.TheFactory = factory;

}

 

#endregion

}

public class Factory : ICloneable

{

#region ICloneable 成员

 

public object Clone()

{

return base.MemberwiseClone();

}

 

#endregion

}

 

}

8. Adapter 适配器(结构型模式)

 

8.1动机(Motivation)

 

在软件系统中,由于应用环境的变化,常常需要将"一些现存的对象"放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。

如何应对这种"迁移的变化"?如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口?

8.2意图(Intent)    

 

将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

——《设计模式》GoF

8.3结构(Structure)-对象适配器

 

对象适配器采用对象组合,通过引用一个类与另一个类接口 在对象适配器中通过组合获得Adaptee对象

通过调用Adaptee对象的方法,转换后返回Target结果

8.4结构(Structure)-类适配器

 

  • 类适配器通过多继承对一个接口与另一个接口进行匹配。
  • Target定义了Client使用的与特定领域相关的接口,Client通过调用Target实现某一个特定的操作。Adaptee是一个已经存在的类,需要与Target协同工作,这个接口需要适配。Adapter适配器适配Adaptee和Target接口。在类适配器中,通过继承获得Adaptee中的方法。
  • .NET不支持多重继承,因此当Target是一个类,而不是一个接口时无法实现类适配器,这时需要使用对象适配器。

8.5结构详解-对象适配器

 

8.6生活中的例子

 

  • 在国内使用的电源供电电压为220V,美国为380V,当你出差到美国,你的电器需要220V的电压,但旅馆里不提供220V,只提供380,所以,你到市场买了一个电源适配器,在接上适配器后,旅馆里的电源就可以使用在你的电器上了。
  • Target:标准电源
  • Adaptee:美国电源
  • Adapter:适配器

8.7实现-Target

 

/// <summary>

/// 目标:Target

/// </summary>

public class 标准电源

{

public virtual int 供电()

{

Console.Write("正常供电,电压:");

return 220;

}

}

8.8实现-Adaptee

 

/// <summary>

/// 要适配的对象:Adaptee

/// </summary>

public class 美国电源

{

public int 供电()

{

Console.Write("在美国供给380V电压");

return 380;

}

}

8.9实现-Adapter

 

/// <summary>

/// 适配器:Adapter

/// </summary>

public class 电源适配器 : 标准电源

{

private 美国电源 _当地电源 = new 美国电源();

public override int 供电()

{

int v = _当地电源.供电();

if (v != 220)

{

//这里做一些转换工作

v = 220;

}

Console.WriteLine("转换后的电压:{0}", v);

return v;//转换后,即适配后

}

}

8.10实现-使用

 

标准电源 电源 = null;

//在中国的时候

电源 = new 标准电源();

Console.WriteLine(电源.供电());

//到美国以后,买一个适配器

电源 = new 电源适配器();

Console.WriteLine("适配后提供电源电压为:{0}",电源.供电());

8.11练习&作业

 

8.12UML类图

 

8.13.NET中的Adapter模式

 

  • DataAdapter:数据适配器
  • DataAdpter使应用程序的数据操作统一到DataSet上,而与具体的数据库类型无关

8.14实现要点

 

  • 适配器模式重在转换接口,它能够使原本不能在一起工作的两个类一起工作,所以经常用在类库复用,代码迁移等方面,有一种亡羊补牢的味道
  • 类适配器和对象适配器可以根据具体实际情况来选用,但一般情况建议使用对象适配器模式

8.15效果

 

  • 通过类的继承或者对象的组合转换已有的接口为目标接口

8.16适用性

 

  • 需要使用一个已经存在的类,但接口与设计要求不符。
  • 希望创建一个可以复用的类,该类可以与其他不相关的类或者是将来不可预见的类协同工作。

8.17总结

 

  • Adapter模式主要应用于"希望复用一些现存的类,但是接口又与复用环境要求不一致的情况" ,在遗留代码复用、类库迁移等方面非常有用。
  • GoF 23 定义了两种Adapter模式的实现结构:对象适配器和类适配器。但类适配器采用"多继承"的实现方式,带来了不良的高耦合,所以一般不推荐使用。对象适配器采用"对象组合"的方式,更符合松耦合精神。
  • Adapter模式可以实现的非常灵活,不必拘泥于Gof23中定义的两种结构。例如,完全可以将Adapter模式中的"现存对象"作为新的接口方法参数,来达到适配的目的。
  • Adapter模式本身要求我们尽可能地使用"面向接口的编程"风格,这样才能在后期很方便地适配。

8.18代码实现

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP06Adapter

{

class Program

{

static void Main(string[] args)

{

Player p1 = new USAPlayer() { Name = "Kebi" };

Player p2 = new USAPlayer() { Name = "Jordan" };

 

Player p3 = new Translator();

 

p1.Attack();

p2.Defense();

p3.Attack();

p3.Defense();

 

 

}

}

 

public abstract class Player

{

public string Name { get; set; }

 

/// <summary>

/// 新接口中的方法

/// </summary>

public abstract void Attack();

public abstract void Defense();

 

//public void Attack(ChinaCBAPlayer adaptee)

//{

// adaptee.进攻();

//}

}

 

public class USAPlayer : Player

{

public override void Attack()

{

Console.WriteLine("{0}-Attack!",base.Name);

}

 

public override void Defense()

{

Console.WriteLine("{0}-Defense",base.Name);

}

}

 

 

 

 

public class ChinaCBAPlayer

{

public string Name { get; set; }

/// <summary>

/// 老系统

/// </summary>

public void 进攻()

{

Console.WriteLine("{0}:进攻",this.Name);

}

public void 防守()

{

Console.WriteLine("{0}:防守",this.Name);

}

}

 

 

/// <summary>

/// 适配器

/// </summary>

public class Translator : Player

{

/// <summary>

/// 被适配的对象

/// </summary>

private ChinaCBAPlayer _adaptee = new ChinaCBAPlayer() { Name = "姚明" };

 

public override void Attack()

{

 

//借力打力

this._adaptee.进攻();

}

 

public override void Defense()

{

//移花接木

this._adaptee.防守();

}

}

 

 

}

9.Bridge桥接模式(结构型模式)

 

9.1动机(Motivation)

 

同一个类型,有两个变化的维度

如何应对这种"多维度的变化"?如何利用面向对象技术来使得同一类型可以轻松地沿着两个方向变化,而不引入额外的复杂度?

  • 数据库操作
    • 操作类型:增删改查
    • 操作对象:客户,订单,产品…
  • 手机软件
    • 软件功能:通讯录,游戏
    • 支持品牌:M(摩托骡拉),N(喏鸡呀)
  • 计算机软件
    • 软件功能:游戏,开发工具,绘图软件…
    • 运行平台:Windows,Unix….

9.2过度使用继承的设计    

 

    

9.3使用桥接模式的设计

 

9.4意图(Intent)

 

  • 将抽象部分与实现部分分离,使它们都可以独立地变化。

——《设计模式》GoF

9.5结构(Structure)

 

9.6结构详解

 

9.7生活中的例子

 

  • 手机软件
    • 软件功能:通讯录,游戏
    • 支持品牌:M(摩托骡拉),N(喏鸡呀)
  • 在一个手机上安装多个软件,然后运行软件。

9.8案例UML类图

 

9.9实现-抽象

 

/// <summary>

/// 第一维度上的抽象:实现者抽象:Implementor

/// </summary>

public abstract class 手机软件

{

public abstract void 运行();

}

/// <summary>

/// 另一维度上的抽象:Abstraction

/// </summary>

public abstract class 手机品牌

{

protected System.Collections.ArrayList softs = new System.Collections.ArrayList();

public void 安装软件(手机软件 newsoft)

{

this.softs.Add(newsoft);

}

public virtual void 运行软件()

{

if (this.softs.Count== 0 )

{

throw new Exception("还没有安装软件");

}

foreach (手机软件 soft in this.softs)

{

soft.运行();

}

}

}     

9.10实现-具体实现

 

/// <summary>

/// 具体的实现者

/// </summary>

public class 游戏:手机软件

{

public override void 运行()

{

Console.WriteLine("游戏真是好玩!可不要过度沉迷.");

}

}

/// <summary>

/// 具体的实现者

/// </summary>

public class 通讯录:手机软件

{

public override void 运行()

{

Console.WriteLine("睡在我上铺的兄弟,你现在还好吗?你的号码是多少啊?让我找找.");

}

}

9.11实现-提炼抽象    

 

/// <summary>

/// 进一步提炼的抽象:RefinedAbstraction

/// </summary>

public class M牌手机 : 手机品牌

{

public override void 运行软件()

{

Console.WriteLine("摩托虽好,也要骡拉,开始运行软件.");

base.运行软件();

Console.WriteLine("运行完毕,小骡累了,该喂草啦!");

Console.WriteLine();

}

}

/// <summary>

/// 进一步提炼的抽象:RefinedAbstraction

/// </summary>

public class N牌手机 : 手机品牌

{

public override void 运行软件()

{

Console.WriteLine("喏~,就是鸡呀,鸭呀,开始运行软件.");

base.运行软件();

Console.WriteLine("运行完毕,小鸡要吃虫,小鸭要洗澡了!");

Console.WriteLine();

}

}

9.12实现-使用    

 

手机品牌 cellphone1 = null;

手机品牌 cellphone2 = null;

cellphone1 = new M牌手机();

cellphone1.安装软件(new 游戏());

cellphone1.安装软件(new 通讯录());

cellphone1.运行软件();

cellphone2 = new N牌手机();

cellphone2.安装软件(new 游戏());

 

cellphone2.运行软件();

9.13实现-结果    

 

9.14    实现要点

 

  • Abstraction:定义抽象类的接口并维护指向Implementor类的对象指针。
  • RefinedAbstraction:扩充Abstraction定义的接口。
  • Implementor:定义实现类的接口,该接口不一定要与Abstraction的接口完全一致。事实上这两个接口可以完全不同。
  • 一般而言,Implementor接口仅提供基本操作,而Abstraction则定义了基于操作的较高层次的操作。
  • ConcreteImplementor:实现Implementor接口并定义它的具体实现。

9.15效果    

 

  • 将接口与实现分离:一个接口可以有若干实现,一个实现也可以为若干对象服务,表示逻辑的对象可以动态地与实现功能的对象组合。
  • 提高可扩充性:逻辑和实现都可以通过类层次的扩展进行扩充。
  • 逻辑和实现被封装在不同的对象中,逻辑对实现的引用是动态进行的。在实际中,需要分别用不同的工厂创建逻辑和实现,然后组装。

 

9.16适用性

 

  • 不希望在业务和业务的软件实现之间存在固定的绑定关系。例如,不希望"入库"业务过程和具体的数据库访问技术或数据库管理系统有过于密切的关系。
  • 希望类的抽象和实现部分可以扩充,进而实现不同的抽象接口和实现部分的组合。
  • 修改实现部分对用户不产生影响,即代码无须重新编译。
  • 复用实现部分。由于实现部分所处的层次较低,因此可以被多种业务模块复用。例如,数据库访问模块可以用在多种业务单元中。

9.17练习&作业    

 

  • "设备管理"中的"零部件管理","物资管理"中的"库房管理","实验室管理"中的"试剂管理"等都是"库存管理"。但由于业务场景的不同,导致业务中的细节不同,因此不能简单地复用。另外,"库存管理"软件的实现主要通过操作关系数据库来完成,我们希望保持软件系统与数据库访问技术以及数据库管理系统的独立性。
  • 为了实现以上要求,我们可以以桥接模式。即将抽象的"库存管理"和"数据库"操作作为具体的"库存管理"应用和具体的"数据库访问技术"的桥梁

9.18UML类图

 

9.19总结

 

  • Bridge模式使用"对象间的组合关系"解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。
  • 所谓抽象和实现沿着各自维度的变化,即"子类化"它们。得到各个子类之后,便可以任意组合它们。
  • Bridge模式有时候类似于多继承方案,但是多继承方案往往违背单一职责原则(即一个类只有一个变化的原因),复用性比较差。Bridge模式是比多继承方案更好的解决方法。
  • Bridge模式的应用一般在"两个非常强的变化维度",有时候即使有两个变化的维度,但是某个方向的变化维度并不剧烈——换言之两个变化不会导致纵横交错的结果,并不一定要使用Bridge模式。

9.20代码实现

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DB07Bridge

{

class Program

{

static void Main(string[] args)

{

职位 p1 = new 董事长();

职位 p2 = new 办公室主任();

 

 

党内职务 d1 = new 支部书记();

党内职务 d2 = new 纪检委员();

党内职务 d3 = new 调研员();

 

p1.任命(d1);

p1.任命(d2);

 

p2.任命(d3);

p2.任命(d2);

 

p1.履行职责();

p2.履行职责();

 

//d2.为人民服务();

//d3.为人民服务();

 

}

}

 

/// <summary>

/// 主要抽象

/// </summary>

public abstract class 职位

{

public abstract string 名称 { get; }

 

private List<党内职务> duties = new List<党内职务>();

public void 任命(党内职务 newDuty)

{

this.duties.Add(newDuty);

}

 

public void 履行职责()

{

Console.WriteLine("\r\n{0}准备为人民服务了:", this.名称);

foreach (var item in this.duties)

{

item.为人民服务();

}

Console.WriteLine("{0}为人民服务完毕", this.名称);

}

 

}

 

/// <summary>

/// 次要抽象,功能的抽象

/// </summary>

public abstract class 党内职务

{

public abstract void 为人民服务();

}

 

 

public class 董事长 : 职位

{

 

public override string 名称

{

get { return "董事长"; }

}

}

 

public class 办公室主任 : 职位

{

 

public override string 名称

{

get { return "办公室主任"; }

}

}

 

public class 支部书记 : 党内职务

{

public override void 为人民服务()

{

Console.WriteLine("组织党员进行思想总结和自我批评");

}

}

public class 纪检委员 : 党内职务

{

public override void 为人民服务()

{

Console.WriteLine("听取群众的意见和建议");

}

}

public class 调研员 : 党内职务

{

public override void 为人民服务()

{

Console.WriteLine("调研基层的大好形势");

}

}

}

10. Composite 组合(结构型模式)

 

10.1动机(Motivation)

 

  • 在面向对象系统中,我们常会遇到一类具有"容器"特征的对象——即它们在充当对象的同时,又是其他对象的容器。
  • 如何将"客户代码与复杂的对象容器结构"解耦?让对象容器自己来实现自身的复杂结构,从而使得客户代码就像处理简单对象一样来处理复杂的对象容器?

10.2意图(Intent)

 

  • 将对象组合成树形结构以表示"部分-整体"的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。

——《设计模式》GoF

10.3结构(Structure)

 

10.4结构详解

 

10.5生活中的例子

 

  • 一棵树,只有一个根,可以有多个树枝和树叶
  • 树枝下还可有多个树枝或树叶
  • 树叶下边不再有树枝或树叶
  • 大风吹过,树上的树枝和树叶都会摇摆

10.6实现-Component

 

/// <summary>

/// 对树枝和树叶的抽象:Component

/// </summary>

public abstract class 部分

{

protected string name = "";

public 部分(string 名称)

{

this.name = 名称;

}

public abstract void 添加(部分 component);

public abstract void 移除(部分 component);

public abstract void 随风摇摆(int 风力);

}

10.7实现-Leaf

 

/// <summary>

/// Leaf

/// </summary>

public class 树叶:部分

{

public 树叶(string 名称) : base(名称) { }

public override void 添加(部分 component)

{

}

public override void 移除(部分 component)

{

}

public override void 随风摇摆(int 风力)

{

//以显示表示

Console.WriteLine(new string('-', 风力) + this.name);

}

}

10.8实现- Composite

 

public class 树枝 : 部分

{

private System.Collections.Generic.List<部分> childs = new System.Collections.Generic.List<部分>();

public 树枝(string 名称) : base(名称) { }

public override void 添加(部分 component)

{

this.childs.Add(component);

}

public override void 移除(部分 component)

{

this.childs.Remove(component);

}

public override void 随风摇摆(int 风力)

{

//先自己摇摆一下

Console.WriteLine(new string('-', 风力) + this.name);

//关键在此:再显示自己的子部分

foreach (部分 component in this.childs)

{

component.随风摇摆(风力 + 2);

}

}

}

10.9实现- 使用

 

//树根

部分 root = new 树枝("树根");

root.添加(new 树叶("第一个叶子"));

root.添加(new 树叶("第二个叶子"));

部分 branch1 = new 树枝("第一个树枝");

branch1.添加(new 树叶("第三个叶子"));

branch1.添加(new 树叶("第四个叶子"));

root.添加(branch1);

部分 branch2 = new 树枝("第二个树枝");

branch2.添加(new 树叶("第五个叶子"));

branch2.添加(new 树叶("第六个叶子"));

branch2.添加(new 树叶("第七个叶子"));

root.添加(branch2);

 

 

部分 leaf = new 树叶("第八个叶子");

root.添加(leaf);

root.添加(new 树叶("第九个叶子"));

//leaf被风刮跑了

root.移除(leaf);

//开始刮风

root.随风摇摆(5);

10.10透明方式&安全方式

 

透明方式 :

  • Component中声明所有管理子对象的方法,包括Add,Remove等
  • 枝节点和叶节点使用起来没有区别,有完全一致的接口
  • 叶节点虽然实现Add,Remove,但没有任何实际意义

 

安全方式 :

  • Component中不声明管理子对象的方法
  • 叶节点就不需要实现Add,Remove
  • 在Composite中声明管理子对象的方法
  • 不够透明,枝和叶具有不同的接口,必须由客户端自行判断

10.11练习&作业

 

10.12适用性

 

  • 两个或者多个类有相似的形式,或者共同代表某个完整的概念,外界的用户也希望他们合而为一,就可以把这几个类"组合"起来,成为一个新的类,用户只需要调用这个新的类就可以了。

10.13总结

 

  • Composite模式采用树形结构来实现普遍存在的对象容器,从而将"一对多"的关系转化为"一对一"的关系,使得客户代码可以一致地处理对象和对象容器,无需关心处理的是单个的对象,还是组合的对象容器。
  • 将"客户代码与复杂的对象容器结构"解耦是Composite模式的核心思想,解耦之后,客户代码将与纯粹的抽象接口——而非对象容器的内部实现结构——发生依赖关系,从而更能"应对变化"。
  • Composite模式中,是将"Add和Remove等和对象容器相关的方法"定义在"表示抽象对象的Component类"中,还是将其定义在"表示对象容器的Composite类"中,是一个关乎"透明性"和"安全性"的两难问题,需要仔细权衡。这里有可能违背面向对象的"单一职责原则",但是对于这种特殊结构,这又是必须付出的代价。
  • Composite模式在具体实现中,可以让父对象中的子对象反向追溯;如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率。

10.14代码实现

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP08Composite

{

class Program

{

static void Main(string[] args)

{

 

Component computer = new Composite();

 

Component box = new Composite() { Name = "机箱", Price = 300 };

 

Component video = new Leaf() { Name = "显示器", Price = 1500 };

 

Component masterboard = new Composite() { Name = "主板", Price =1000 };

Component harddisk = new Leaf() { Name = "硬盘", Price = 600 };

 

Component cpu = new Leaf() { Name = "CPU", Price = 1000 };

Component videocard = new Leaf() { Name = "显卡", Price = 1200 };

Component memory = new Leaf() { Name = "内存", Price = 500 };

 

computer.Add(box);

computer.Add(video);

 

box.Add(masterboard);

box.Add(harddisk);

 

masterboard.Add(cpu);

masterboard.Add(videocard);

masterboard.Add(memory);

 

 

// masterboard.Remove(videocard);

int total = computer.TotalPrice();

Console.WriteLine(total);

 

 

 

 

 

}

}

 

public abstract class Component

{

public int Price { get; set; }

public string Name { get; set; }

public abstract void Add(Component part);

public abstract void Remove(Component part);

/// <summary>

/// 计算当前的组件的总价

/// </summary>

/// <returns></returns>

public abstract int TotalPrice();

 

// public Component Father { get; set; }

}

/// <summary>

/// 不是容器的组件

/// </summary>

public class Leaf : Component

{

public override void Add(Component part)

{

throw new InvalidOperationException("本节点不允许该操作");

}

 

public override void Remove(Component part)

{

throw new InvalidOperationException("本节点不允许该操作");

}

 

public override int TotalPrice()

{

return base.Price;

}

}

 

 

public class Composite : Component

{

private List<Component> _childParts = new List<Component>();

public override void Add(Component part)

{

// part.Father = this;

 

this._childParts.Add(part);

}

 

public override void Remove(Component part)

{

this._childParts.Remove(part);

}

 

public override int TotalPrice()

{

int total = this.Price;

foreach (var item in this._childParts)

{

total += item.TotalPrice();

}

return total;

}

}

 

}

 

11. Decorator 装饰 (结构型模式)    

 

11.1动机(Motivation)

 

在软件系统中,有时候我们会使用继承来扩展对象的功能,但是由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。

如何使"对象功能的扩展"能够根据需要来动态地实现?同时避免"扩展功能的增多"带来的子类膨胀问题?从 而使得任何"功能扩展变化"所导致的影响降为最低?

假如我们需要为游戏中开发一种坦克,除了各种不同型号的坦克外,我们还希望在不同场合中为其增加以下一种或多种功能:比如红外线夜视功能,比如水陆两栖功能,比如卫星定位功能等等。

11.2意图(Intent)

 

  • 动态地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类更为灵活。

——《设计模式》GoF

 

11.3结构(Structure)

 

11.4结构详解

 

11.5生活中的例子

 

  • 男人和女人都需要装扮
  • 服饰就是装扮人的对象
  • 服饰分为:
    • 西服
    • 皮鞋
    • 七分裤
    • 高跟鞋

 

11.6实现-Component

 

public abstract class 装饰

{

public abstract void 显示();

}

 

11.7实现- ConcreteComponent

 

public class 男人 : 装饰

{

public override void 显示()

{

Console.WriteLine("男人应该孔武有力");

}

}

public class 女人 : 装饰

{

public override void 显示()

{

Console.WriteLine("女人应该柔情似水.");

}

}

11.8实现-Decorator

 

public abstract class 服饰 : 装饰

{

private 装饰 component = null;

public 装饰 要装饰的对象

{

set

{

this.component = value;

}

}

public override void 显示()

{

if (this.component!=null)

{

this.component.显示();

}

}

}

 

11.9实现-ConcreteDecorator

 

public class 西服 : 服饰

{

public override void 显示()

{

base.显示();

Console.WriteLine("西服,精神抖擞.");

}

}

public class 皮鞋 : 服饰

{

public override void 显示()

{

base.显示();

Console.WriteLine("皮鞋,铿锵有力.");

}

}

 

 

public class 七分裤 : 服饰

{

public override void 显示()

{

base.显示();

Console.WriteLine("七分裤,婀娜多姿.");

}

}

public class 高跟鞋 : 服饰

{

public override void 显示()

{

base.显示();

Console.WriteLine("高跟鞋,凌波微步.");

}

}

11.10实现-使用

 

男人 man = new 男人();

服饰 _西服 = new 西服();

服饰 _皮鞋 = new 皮鞋()

 

_皮鞋.要装饰的对象 = man;

_西服.要装饰的对象 = _皮鞋;

_西服.显示();

Console.WriteLine();

女人 woman = new 女人();

服饰 _七分裤 = new 七分裤();

服饰 _高跟鞋 = new 高跟鞋();

_七分裤.要装饰的对象 = woman;

_高跟鞋.要装饰的对象 = _七分裤;

_高跟鞋.显示();

Console.WriteLine("================混搭================");

_七分裤.要装饰的对象 = man;

_皮鞋.要装饰的对象 = _七分裤;

_皮鞋.显示();

11.11实现-结果

 

11.12实现要点

 

  • 在Decorator的一个方法或属性中传入要装饰的对象
    _七分裤.要装饰的对象 = woman;
  • 装饰的过程有先后顺序
    先穿裤子再穿鞋子: _高跟鞋.要装饰的对象 = _七分裤;

11.13在实际中的灵活运用

 

  • 如果只有一个ConcreteComponent类而没有抽象的Component类,那么Decorator类可以是ConcreteComponent的一个子类。

  • 如果只有一个ConcreteDecorator类,那么就没有必要建立一个单独的Decorator类,而可以把Decorator和ConcreteDecorator的责任合并成一个类。

11.14效果

 

  • 通过采用组合、而非继承的手法, Decorator模式实现了在运行时动态地扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了单独使用继承带来的"灵活性差"和"多子类衍生问题"。
  • Decorator模式的优点是提供了比继承更加灵活的扩展,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。
  • 由于使用装饰模式,可以比使用继承关系需要较少数目的类。使用较少的类,当然使设计比较易于进行。但是,在另一方面,使用装饰模式会产生比使用继承关系更多的对象。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。

11.15适用性

 

  • 需要扩展一个类的功能,或给一个类增加附加责任。
  • 需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
  • 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变得不现实。

11.16练习&作业

 

  • 为游戏中开发一种坦克,除了各种不同型号的坦克外,我们还希望在不同场合中为其增加以下一种或多种功能:比如红外线夜视功能,比如水陆两栖功能,比如卫星定位功能等等
  • Component:坦克
  • ConcreteComponent:不同型号的坦克
  • Decorator:功能
  • ConcreteDecorator:
        红外线夜视功能
        水陆两栖功能
        卫星定位功能    

11.17UML类图

 

11.18总结

 

  • Component类在Decorator模式中充当抽象接口的角色,不应该去实现具体的行为。而且Decorator类对于Component类应该透明——换言之Component类无需知道Decorator类,Decorator类是从外部来扩展Component类的功能。
  • Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component类。我们可以使用一个或者多个Decorator对象来"装饰"一个Component对象,且装饰后的对象仍然是一个Component对象。
  • Decorator模式并非解决"多子类衍生的多继承"问题,Decorator模式应用的要点在于解决"主体类在多个方向上的扩展功能"——是为"装饰"的含义。

11.19代码实现

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP09Decorator

{

class Program

{

static void Main(string[] args)

{

IDecoratable camera = new Canon500D();

 

Decorator flash = new 闪光灯();

Decorator thri = new 三脚架();

Decorator jingtou = new 镜头();

 

//thri.ObjetoToDecorate = jingtou;

//jingtou.ObjetoToDecorate = flash;

//flash.ObjetoToDecorate = camera;

 

// thri.Decorate(jingtou).Decorate(flash).Decorate(camera);

 

 

flash.Decorate(thri).Decorate(jingtou).Decorate(camera);

 

(camera as IDecoratable).Decorate();

 

 

 

}

}

 

/// <summary>

/// 可以动态添加新功能的接口

/// </summary>

public interface IDecoratable

{

/// <summary>

/// 添加并运行新功能

/// </summary>

void Decorate();

}

 

/// <summary>

/// 相机

/// </summary>

public abstract class Camera : IDecoratable

{

/// <summary>

/// 拍照

/// </summary>

public abstract void TakePhoto();

 

/// <summary>

/// 实现接口中定义的新的功能

/// </summary>

public void Decorate()

{

//转化为相机自身的功能:拍照

this.TakePhoto();

}

}

public class Canon500D : Camera

{

 

public override void TakePhoto()

{

Console.WriteLine("500D拍照");

}

}

 

 

/// <summary>

/// 所有的动态添加的功能的抽象

/// </summary>

public abstract class Decorator : IDecoratable

{

/// <summary>

/// 这个功能去添加的对象

/// </summary>

public IDecoratable ObjetoToDecorate { get; set; }

public Decorator Decorate(Decorator objectToDecorate)

{

this.ObjetoToDecorate = (IDecoratable)objectToDecorate;

return objectToDecorate;

}

public void Decorate(IDecoratable objectToDecorate)

{

this.ObjetoToDecorate = objectToDecorate;

}

 

public virtual void Decorate()

{

if (this.ObjetoToDecorate!=null)

{

this.ObjetoToDecorate.Decorate();

}

}

}

 

public class 三脚架 : Decorator

{

public override void Decorate()

{

Console.WriteLine("树立三脚架,稳定相机");

 

base.Decorate();

 

}

}

 

public class 闪光灯 : Decorator

{

public override void Decorate()

{

Console.WriteLine("闪光灯充电并闪光");

 

base.Decorate();

 

}

}

 

public class 镜头 : Decorator

{

public override void Decorate()

{

Console.WriteLine("安装镜头,并调整焦距和光圈");

 

base.Decorate();

 

}

}

}

12. Facade外观(结构型模式)

 

12.1动机(Motivation)

 

在软件开发系统中,客户程序经常会与复杂系统的内部子系统之间产生耦合,而导致客户程序随着子系统的变化而变化。那么如何简化客户程序与子系统之间的交互接口?如何将复杂系统的内部子系统与客户程序之间的依赖解耦?(无类图,指表达一种思想。)

12.2意图(Intent)

 

为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

《设计模式》GoF

12.3结构(Struct)

 

12.4结构详解

 

12.5生活中的例子

 

当消费者按照目录采购时,则体现了一个外观模式。消费者拨打一个号码与客户代表联系,客服代表则扮演了这个"Facade"外观,她包含了与订货不、收银部和送货部的接口。

12.6实现-各子系统

 

12.7实现-Facade

 

12.8实现-使用

 

12.9不适用Facade

 

12.10实现-结果

 

12.11实现要点

 

在Facade耦合子系统之间的业务逻辑和调用

Facade对客户端(使用者)公开简单的接口,隐藏内部子系统之间调用的细节。

12.12效果

 

Facade模式对客户屏蔽了子系统组件,因而减少了客户处理的对象的数目并使得子系统使用起来更加方便。

Facade模式实现了子系统与客户之间的松耦合关系,而子系统与客户之间的耦合关系,而子系统内部的功能组件往往是紧耦合的。松耦合关系使得子系统的组件变化不会影响到它的客户。

如果应用需要,它并不限制它们使用子系统类。因此,可以在系统易用性与通用性之间选择。

12.13适用性

 

为了一个复杂子系统提供一个简单接口。

提高子系统的独立性。

在层次化结构中,可以使用Facade模式定义系统中每一层的入口。

12.14N层架构中的Facade模式

 

12.15练习&作业

 

现在有一辆汽车,我们(客户程序)要启动它,那我们就要发动引擎,使四个车轮转动。但是实际中我们并不需要直接干涉引擎使之启动,也不需要用手推动车轮转动,我们只需要踩下油门。

子系统:引擎、车轮

Facade(外观):汽车

公开的接口:踩下油门

12.16总结

 

在客户程序的角度来看,Facade模式不仅简化了整个系统组件的接口,同时对于组件内部与外部客户程序来说,从某种程度上也达到了一种"解耦"的效果—内部子系统的任何变化不会影响到Facade接口的变化。

Facade设计模式更注重从架构的层次看整个系统,而不是单个类的层次。Facade很多时候更是一种架构设计模式。

注意区分Facade模式、Adapter模式、Bridge模式与Decorator模式。

Facade模式注重简化接口

Adapte模式注重转换接口

Bridge模式注重分离接口(抽象)与其实现

Decorator模式注重稳定接口的前提下为对象扩展功能。

13.Flyweight享元(结构型模式)

 

13.1动机(Motivation)

 

  • 采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中,从而带来很高的运行时代价——主要指内存需求方面的代价。
  • 如何在避免大量细粒度对象问题的同时,让外部客户程序仍然能够透明地使用面向对象的方式来进行操作?
  • 在某些特殊的应用中下,由于对象的数量太大,采用面向对象会给系统带来难以承受的内存开销。比如图形应用中的图元等对象、字处理应用中的字符对象等。

13.2意图(Intent)

 

  • 运用共享技术有效地支持大量细粒度的对象。

——《设计模式》GoF

13.3结构(Structure)

 

13.4结构详解

 

13.5生活中的例子

 

  • 客户(某社区)通过家政服务公司雇佣保姆
  • 家政服务公司不可能为每个客户的要求雇佣一个新的保姆,反而会通过"共享"保姆的方式为不同客户服务
  • 保姆分为:
    • 看孩子的保姆
    • 看老人的保姆
    • 不通过中介的宠物保姆(不共享)
  • 保姆有:
    • 内部属性:工号,姓名,服务的公司,工资标准(元/小时)
    • 外部属性:工作地点

13.6实现-Flyweight

 

//Flyweight

public abstract class 保姆

{

protected string 工号;

protected string 姓名;

protected string 公司;

protected int 时薪;

//以上是内部状态

public abstract void 服务(string 工作地点);//工作地点:外部状态

//改变内部状态,将影响所有共享的本实例

public virtual void 加薪(int 加薪数量)

{

this.时薪 += 加薪数量;

}

}

13.7实现-ConcreteFlyweight

 

public class 小孩保姆 : 保姆

{

public 小孩保姆(string 公司, int 时薪)

{

base.工号 = "001";

base.姓名 = "翠花";

base.公司 = 公司;

base.时薪 = 时薪;

}

public override void 服务(string 工作地点)

{

Console.WriteLine("耐心是我的特长!\r\n====小孩子保姆:{0},工资标准:{1},正在:{2}为小孩子服务!", base.姓名, base.时薪, 工作地点);

}

}

public class 老人保姆 : 保姆

{

public 老人保姆(string 公司, int 时薪)

{

base.工号 = "009";

base.姓名 = "富贵嫂";

base.公司 = 公司;

base.时薪 = 时薪;

}

public override void 服务(string 工作地点)

{

Console.WriteLine("老吾老,以及人之老!\r\n====老人保姆:{0},工资标准:{1},正在:{2}为老人服务!", base.姓名,base.时薪, 工作地点);

}

}

13.8实现-UnSharedConcreteFlyweight

 

public class 宠物保姆 : 保姆

{

public 宠物保姆()

{

base.工号 = "000";

base.姓名 = "潮人";

base.公司 = "无";

base.时薪 = 100;

}

public override void 服务(string 工作地点)

{

Console.WriteLine("声明:非中介,非诚勿扰.\r\n--------宠物保姆{2}:工资标准:{0}.在{1}为宠物服务.", base.时薪, 工作地点,base.姓名);

}

}

13.9实现-FlyweightFactory

 

public class 和谐家政服务公司

{

private string companyName = "和谐家政服务公司";

private System.Collections.Hashtable _所有员工 = null;

//开张

public 和谐家政服务公司()

{

this.companyName = "和谐家政服务公司";

//招兵买马

this._所有员工 = new System.Collections.Hashtable();

//雇佣员工

保姆 翠花 = new 小孩保姆(this.companyName, 20);

保姆 富贵嫂 = new 老人保姆(this.companyName, 30);

//记录在花名册,按服务对象

this._所有员工.Add("小孩", 翠花);

this._所有员工.Add("老人", 富贵嫂);

}

 

 

public 保姆 安排保姆(string 保姆类型)

{

switch (保姆类型)

{

case "小孩":

return this._所有员工["小孩"] as 保姆;

case "老人":

return this._所有员工["老人"] as 保姆;

default:

Console.WriteLine("本公司只为小孩和老人服务.宠物保姆请自已找.");

return null;

}

}

}

13.10实现-使用

 

和谐家政服务公司 company = new 和谐家政服务公司();//找到中介公司了

Console.WriteLine("=========找到中介公司:========");

保姆 _保姆1 = company.安排保姆("小孩");

保姆 _保姆2 = company.安排保姆("小孩");

 

保姆 _保姆4 = company.安排保姆("老人");

保姆 _保姆5 = company.安排保姆("老人");

保姆 _保姆7 = company.安排保姆("宠物");

if (_保姆7 == null)

{

_保姆7 = new 宠物保姆();//中介公司不提供,只能自己找:UnSharedConcreteFlyweight

}

Console.WriteLine("=========雇佣保姆结束,开始服务:========");

 

 

_保姆1.服务("2号楼301室");

_保姆2.服务("5号楼601室");

_保姆4.服务("9号楼201室");

_保姆5.服务("6号楼402室");

_保姆7.服务("8号楼101室");

 

_保姆1.加薪(20);

_保姆4.加薪(30);//服务不错,加工资,只给1,4两个加

Console.WriteLine("============加薪,继续服务:============");

_保姆1.服务("2号楼301室");

_保姆2.服务("5号楼601室");

_保姆4.服务("9号楼201室");

_保姆5.服务("6号楼402室");

_保姆7.服务("8号楼101室");

13.11实现-结果

 

13.12实现要点

 

  • 本例只为演示 ,但是FlyweightFactory中的switch应通过Factory进行重构
  • 区分内外部状态很重要,这是享元对象能做到享元的关键所在
  • 享元类是可以被实例化的,单例类不能直接实例化
  • FlyweightFactory建议设计为单例模式

13.13内部状态 VS 外部状态

 

  • 内部状态存储于ConcreteFlyweight对象之中
  • 外部状态应该由客户端对象存储或计算,当调用Flyweight对象的操作时,将该外部状态传递给它

13.14效果

 

  • Flyweight采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力。
  • 享元模式的优点在于它大幅度地降低内存中对象的数量。但是,它做到这一点所付出的代价也是很高的:享元模式使得系统更加复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。另外它将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长。

13.15适用性

 

  • 一个系统有大量的对象。
  • 这些对象耗费大量的内存。
  • 这些对象的状态中的大部分都可以外部化。
  • 这些对象可以按照内部(内蕴)状态分成很多的组,当把外部(外蕴)对象从对象中剔除时,每一个组都可以仅用一个对象代替。
  • 软件系统不依赖于这些对象的身份,换言之,这些对象可以是不可分辨的。
  • 使用享元模式需要维护一个记录了系统已有的所有享元的表,而这需要耗费资源。因此,应当在有足够多的享元实例可供共享时才值得使用享元模式。

13.16练习&作业

 

  • 在一篇文档中,字符的数量远不止几百个这么简单,可能上千上万,内存中就同时存在了上千上万个Charactor对象,这样的内存开销是可想而知的。进一步分析可以发现,虽然我们需要的Charactor实例非常多,这些实例之间只不过是状态不同而已,也就是说这些实例的状态数量是很少的。所以我们并不需要这么多的独立的Charactor实例,而只需要为每一种Charactor状态创建一个实例,让整个字符处理软件共享这些实例就可以了
  • 注:Display()应加一个参数:Point(显示的位置),是外部状态
  • 注:SetPointSize(int)为改变内部状态(假设每种字符只有一种大小)

13.17总结

 

  • 面向对象很好地解决了抽象性的问题,但是作为一个运行在机器中的程序实体,我们需要考虑对象的代价问题。
  • Flyweight设计模式主要解决面向对象的代价问题,一般不触及面向对象的抽象性问题。
  • Flyweight采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力。在具体实现方面,要注意对象状态的处理。
  • 对象的数量太大从而导致对象内存开销加大——什么样的数量才算大?这需要我们仔细的根据具体应用情况进行评估,而不能凭空臆断。
  • Flyweight模式解决的是由于大量的细粒度对象所造成的内存开销的问题,它在实际的开发中并不常用,但是作为底层的提升性能的一种手段却很有效。

14. Proxy 代理(结构型模式)

 

14.1动机(Motivation)

 

  • 在面向对象系统中,有些对象由于某种原因(比如对象创建的开销很大,或者某些操作需要安全控制,或者需要进程外的访问等),直接访问会给使用者、或者系统结构带来很多麻烦。
  • 如何在不失去透明操作对象的同时来管理/控制这些对象特有的复杂性?增加一层间接层是软件开发中常见的解决方式。

 

14.2意图(Intent)

 

  • 为其他对象提供一种代理以控制对这个对象的访问。

——《设计模式》GoF

14.3结构(Structure)

 

14.4结构详解

 

14.5生活中的例子

 

  • 欠债还钱,天经地义。
  • 收讨债有两种方式:
    • 自己直接收:难度高
    • 找专业的讨债代理公司:难度低,但会花点小钱
  • Subject:债权人
  • Request:讨债
  • RealSubject:直接债权人

Proxy:讨债公司

14.6实现-Subject

 

public interface 债权人

{

//Request

void 讨债();

}

14.7实现-RealSubject

 

public class 直接债权人 : 债权人

{

public void 讨债()

{

Console.WriteLine("直接债权人:欠钱的是大爷,求你还钱吧");

}

public void 支付讨债费用()

{

Console.WriteLine("直接债权人:花点小钱,能把大钱搞回来,值.");

}

}

14.8实现-Proxy

 

public class 讨债公司 : 债权人

{

private 直接债权人 客户 = new 直接债权人();

public void 讨债()

{

Console.WriteLine("讨债公司:先把欠钱的揍一顿再说.");

this.客户.讨债();

this.客户.支付讨债费用();

Console.WriteLine("讨债公司:搞定,收工.");

}

}

14.9实现-使用

 

//先自己直接讨债

债权人 收钱的 = new 直接债权人();

收钱的.讨债();

Console.WriteLine("--------直接讨债搞不定---------");

//请讨债公司

收钱的 = new 讨债公司();

收钱的.讨债();

14.10实现-结果

 

14.11实现要点

 

  • 接口并不是必须的,大多数情况下,我们为了保持对对象操作的透明性,并强制实现类实现代理类所要调用的所有的方法,我们会让它们实现与同一个接口。
  • 但是我们说代理类它其实只是在一定程度上代表了原来的实现类,所以它们有时候也可以不实现于同一个接口。
  • Proxy封装了对RealSubject的引用,以实现调用RealSubject的功能,并提供了额外的功能

14.12效果

 

  • Proxy模式根据种类不同,效果也不尽相同:
  • 1.远程(Remote)代理:为一个位于不同的地址空间的对象提供一个局域代表对象。这个不同的地址空间可以是在本机器中,也可是在另一台机器中。远程代理又叫做大使(Ambassador)。好处是系统可以将网络的细节隐藏起来,使得客户端不必考虑网络的存在。客户完全可以认为被代理的对象是局域的而不是远程的,而代理对象承担了大部份的网络通讯工作。由于客户可能没有意识到会启动一个耗费时间的远程调用,因此客户没有必要的思想准备。
  • 2.虚拟(Virtual)代理:根据需要创建一个资源消耗较大的对象,使得此对象只在需要时才会被真正创建。使用虚拟代理模式的好处就是代理对象可以在必要的时候才将被代理的对象加载;代理可以对加载的过程加以必要的优化。当一个模块的加载十分耗费资源的情况下,虚拟代理的好处就非常明显。
  • 3.Copy-on-Write代理:虚拟代理的一种。把复制(克隆)拖延到只有在客户端需要时,才真正采取行动。
  • 4.保护(Protect or Access)代理:控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限。保护代理的好处是它可以在运行时间对用户的有关权限进行检查,然后在核实后决定将调用传递给被代理的对象。
  • Proxy模式根据种类不同,效果也不尽相同:
  • 5.Cache代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
  • 6.防火墙(Firewall)代理:保护目标,不让恶意用户接近。
  • 7.同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。
  • 8.智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等。

     

14.13练习&作业

 

  • 程序中使用到Math类,Math类并没有部署在我们本地,而是部署在一台服务器上,也就是说Math类根本和我们的客户程序不在同一个地址空间之内,我们现在要面对的是跨越Internet这样一个网络障碍
  • 使用一个本地的代理来替Math类打点一切,即为我们的系统引入了一层间接层

14.14总结

 

  • "增加一层间接层"是软件系统中对许多复杂问题的一种常见解决方法。在面向对象系统中,直接使用某些对象会带来很多问题,作为间接层的proxy对象便是解决这一问题的常用手段。
  • 具体proxy设计模式的实现方法、实现粒度都相差很大,有些可能对单个对象做细粒度的控制,有些可能对组件模块提供抽象代理层,在架构层次对对象做proxy。
  • Proxy并不一定要求保持接口的一致性,只要能够实现间接控制,有时候损及一些透明性是可以接受的。

14.15代码实例

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP12Proxy

{

class Program

{

static void Main(string[] args)

{

IGirlFriend girl = new Classmate();

girl.WatchFilm();

 

Console.WriteLine();

girl = new Diamond();

girl.WatchFilm();

}

}

 

public interface IGirlFriend

{

//电影约会

void WatchFilm();

 

}

 

 

/// <summary>

/// RealSubject:校花

/// </summary>

public class SchoolFlower : IGirlFriend

{

public void WatchFilm()

{

Console.WriteLine("校花看电影");

}

}

 

/// <summary>

/// 好友:Proxy

/// </summary>

public class Classmate : IGirlFriend

{

private SchoolFlower sf = new SchoolFlower();

public void WatchFilm()

{

Console.WriteLine("先铺垫");

Console.WriteLine("再动员");

sf.WatchFilm();

Console.WriteLine("红娘工作完成");

}

}

 

 

/// <summary>

/// 钻石

/// </summary>

public class Diamond : IGirlFriend

{

private SchoolFlower sf = new SchoolFlower();

public void WatchFilm()

{

Console.WriteLine("鸽子蛋大的钻石Show出来");

sf.WatchFilm();

Console.WriteLine("看完电影想干嘛就干嘛");

}

public void Show()

{

Console.WriteLine("鸽子蛋大的钻石Show出来");

sf.WatchFilm();

Console.WriteLine("看完电影想干嘛就干嘛");

 

}

 

 

}

 

 

}

15. Template Method 模板方法(行为型模式)

 

15.1动机(Motivation)

 

  • 在软件构建过程中,对于某一项任务,它常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,或者由于固有的原因(比如框架与应用之间的关系)而无法和任务的整体结构同时实现。
  • 如何在确定稳定操作结构的前提下,来灵活应对各个子步骤的变化或者晚期实现需求?

15.2意图(Intent)

 

  • 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

——《设计模式》GoF

15.3结构(Structure)

 

15.4结构详解

 

15.5生活中的例子

 

房屋建筑师在开发新项目时会使用模板方法。一个典型的规划包括一些建筑平面图,每个平面图体现了不同部分。在一个平面图中,地基、结构、上下水和走线对于每个房间都是一样的。只有在建筑的后期才开始有差别而产生了不同的房屋样式。

15.6实现- AbstractClass

 

public abstract class 建筑平面图

{ //TemplateMethod

public void 建造()

{

Console.WriteLine("----开始盖*****{0}******房子喽-----",this.建筑类型);

this.地基();

this.水管();

this.走线();

Console.WriteLine("-----建造完毕----");

}

#region PrimitiveOperations

protected abstract string 建筑类型 { get;}

protected virtual void 地基()

{

Console.WriteLine("打地基");

}

protected virtual void 走线()

{

Console.WriteLine("走线");

}

protected virtual void 水管()

{

Console.WriteLine("水管");

}

#endregion

}

15.7实现- ConcreteClass

 

public class 别墅平面图 : 建筑平面图

{

protected override string 建筑类型

{

get { return "别墅"; }

}

protected override void 地基()

{

base.地基();

Console.WriteLine("\t别墅的地基打得深些");

}

protected override void 水管()

{

base.水管();

Console.WriteLine("\t别墅的水管");

}

protected override void 走线()

{

base.走线();

Console.WriteLine("\t别墅走线:再安装闭路监控线");

}

}

 

 

 

public class 钉子户平面图 : 建筑平面图

{

protected override string 建筑类型

{

get { return "钉子户"; }

}

protected override void 地基()

{

base.地基();

Console.WriteLine("\t钉子户的地基打得浅点");

}

protected override void 水管()

{

base.水管();

Console.WriteLine("\t钉子户的水管--断水");

}

protected override void 走线()

{

base.走线();

Console.WriteLine("\t钉子户走线:使用劣质配件");

}

}

 

 

15.8实现-使用

 

建筑平面图 design = null;

if (false)

{

design = new 钉子户平面图();

}

else

{

design = new 别墅平面图();

}

design.建造();

15.9实现-结果

 

15.10适用性

 

  • 1.一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
  • 2.各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。这是Opdyke和Johnson所描述过的"重分解以一般化"的一个很好的例子。首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
  • 3.控制子类扩展。模板方法只在特定点调用"Hook"操作,这样就只允许在这些点进行扩展。

15.11练习&作业

 

  • 我们需要简单的读取Northwind数据库中的表的记录并显示出来。对于数据库操作,我们知道不管读取的是哪张表,它一般都应该经过如下这样的几步:
  • 1.连接数据库(Connect)
  • 2.执行查询命令(Select)
  • 3.显示数据(Display)
  • 4.断开数据库连接(Disconnect)
  • 顶级的框架DataAccessObject中给出了固定的轮廓,方法Run()便是模版方法,Template Method模式也由此而得名。而对于Select()和Display()这两个抽象方法则留给具体的子类去实现

15.12总结

 

  • Template Method模式是一种非常基础性的设计模式,在面向对象系统中有着大量的应用。它用最简洁的机制(虚函数的多态性)为很多应用程序框架提供了灵活的扩展点,是代码复用方面的基本实现结构。
  • 除了可以灵活应对子步骤的变化外,"不要调用我,让我来调用你"的反向控制结构是Template Method的典型应用。
  • 在具体实现方面,被Template Method调用的虚方法可以具有实现,也可以没有任何实现(抽象方法、纯虚方法),但一般推荐将它们设置为protected方法。

15.13代码实例

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP13TemplateMethod

{

class Program

{

static void Main(string[] args)

{

厨师 cooker1 = new 大排档师傅();

厨师 cooker2 = new 五星级大厨();

 

cooker1.工作();

Console.WriteLine();

cooker2.工作();

}

}

 

public abstract class 厨师

{

private void 上班()

{

Console.WriteLine("{0}开始上班", this.姓名);

}

private void 下班()

{

}

 

/// <summary>

/// 模板方法:步骤不变的,具体实现过程,可以给子类再在运行时决定

/// </summary>

public void 工作()

{

Console.WriteLine("{0}开始工作",this.姓名);

this.上班();

this.洗菜();

this.炒菜();

this.盛菜();

this.下班();

 

}

 

/// <summary>

/// 以下3个方法,是交给子类实现 的具体子步骤

/// </summary>

protected abstract void 洗菜();

protected abstract void 炒菜();

protected abstract void 盛菜();

 

/// <summary>

/// 钩子:模板方法(交给子类实现),在父类中调用

/// </summary>

public abstract string 姓名

{

get;

}

 

}

 

public class 大排档师傅 : 厨师

{

protected override void 洗菜()

{

Console.WriteLine("大排档师傅:洗菜");

}

 

protected override void 炒菜()

{

 

Console.WriteLine("大排档师傅:炒菜");

}

 

protected override void 盛菜()

{

 

Console.WriteLine("大排档师傅:盛菜");

}

 

public override string 姓名

{

get { return "张师傅"; }

}

}

 

public class 五星级大厨 : 厨师

{

protected override void 洗菜()

{

Console.WriteLine("五星级大厨:洗菜");

}

 

protected override void 炒菜()

{

Console.WriteLine("五星级大厨:炒菜");

}

 

protected override void 盛菜()

{

Console.WriteLine("五星级大厨:盛菜");

}

 

public override string 姓名

{

get {

return "大牛";

}

}

}

 

}

 

16. Command 命令(行为型模式)

 

16.1动机(Motivation)

 

  • 在软件构建过程中,"行为请求者"与"行为实现者"通常呈现一种"紧耦合"。但在某些场合——比如需要对行为进行"记录、撤销/重做(undo/redo)、事务"等处理,这种无法抵御变化的紧耦合是不合适的。
  • 在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。

16.2意图(Intent)

 

  • 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

——《设计模式》GoF

16.3结构(Structure)

 

16.4结构详解

 

16.5生活中的例子

 

  • 服务员接受顾客的点单,把它记在账单上封装。这个点单被排队等待烹饪。

16.6实现-Receiver

 

//Receiver

public class 厨师

{

//Action

public void 烤鸡翅(int 数量)

{

Console.WriteLine("厨师烤鸡翅:{0}", 数量);

}

//Action

public void 烤羊肉串(int 数量)

{

Console.WriteLine("厨师烤羊肉串:{0}", 数量);

}

}

 

//Command

public abstract class 命令

{

protected 厨师 _cook; //receiver接受者

public 命令(厨师 cook)

{

this._cook=cook;

}

//Execute执行

public abstract void 烧烤();

//State状态

public bool 执行完毕 = false;

}

 

 

private int _数量;

public int 数量

{

get

{

return this._数量;

}

set

{

if (value <0)

{

Console.WriteLine("数量应>0");

}

else

{

this._数量 = value;

}

}

}

 

16.7实现-ConcreteCommand

 

//ConcreteCommand具体命令

public class 烤鸡翅命令:命令

{

public 烤鸡翅命令(厨师 cook) : base(cook) { }

public override void 烧烤()

{

base._cook.烤鸡翅(base.数量);

this.执行完毕 = true;

}

}

public class 烤羊肉串命令 : 命令

{

public 烤羊肉串命令(厨师 cook) : base(cook) { }

public override void 烧烤()

{

base._cook.烤羊肉串(base.数量);

this.执行完毕 = true;

}

}

16.8实现- Invoker

 

//Invoker

public class 服务员

{ private int 鸡翅数量 = 60;

private int 羊肉串数量 = 80;

private System.Collections.Generic.List<命令> orders = new List<命令>() ;

public void 下单(命令 command) {

if (command is 烤鸡翅命令) {

if (this.鸡翅数量<command.数量) {

Console.WriteLine("对不起,鸡翅不够了,现在只剩下:{0}",this.鸡翅数量);

return;

} else {

this.鸡翅数量 -= command.数量;

}

}

if (command is 烤羊肉串命令) {

if (this.羊肉串数量 < command.数量) {

Console.WriteLine("对不起,羊肉串不够了,现在只剩下:{0}", this.鸡翅数量);

return;

} else {

this.羊肉串数量 -= command.数量;

}

}

orders.Add(command);

Console.WriteLine("新下单:{0},数量:{1}\t{2}", command, command.数量, DateTime.Now);

}

16.9实现- Invoker(续)

 

public void 取消订单(命令 command) {

if (command.执行完毕)

{

Console.WriteLine("订单已执行完毕,东西都吃到肚子里了.不能再取消");

return;

}

orders.Remove(command);

if (command is 烤鸡翅命令)

{

this.鸡翅数量 += command.数量;

}

else

{

this.羊肉串数量 += command.数量;

}

Console.WriteLine("订单已取消:" + command.ToString() + "\t" + DateTime.Now.ToString());

}

public void 全部执行() {

foreach (命令 cmd in orders)

{ if (!cmd.执行完毕)

{

cmd.烧烤();

}

}

}

}

16.10实现- 使用

 

服务员 mm = new 服务员();

厨师 cook = new 厨师();

//30个鸡翅

命令 cmd30个鸡翅 = new 烤鸡翅命令(cook);

cmd30个鸡翅.数量 = 30;

//40个羊肉串

命令 cmd40个羊肉串 = new 烤羊肉串命令(cook);

cmd40个羊肉串.数量 = 40;

Console.WriteLine("========先来30个鸡翅,40个羊肉串=========");

mm.下单(cmd30个鸡翅);

mm.下单(cmd40个羊肉串);

mm.全部执行();

 

 

Console.WriteLine("========实在太爽了,再来40个鸡翅,50个羊肉串=========");

//40个鸡翅

命令 cmd40个鸡翅 = new 烤鸡翅命令(cook);

cmd40个鸡翅.数量 = 40;

 

//50个羊肉串

命令 cmd50个羊肉串 = new 烤羊肉串命令(cook);

cmd50个羊肉串.数量 = 50;

mm.下单(cmd40个鸡翅);

mm.下单(cmd50个羊肉串);

mm.全部执行();

Console.WriteLine("========不爽了,取消30个鸡翅,40个羊肉串=========");

mm.取消订单(cmd30个鸡翅);

mm.取消订单(cmd40个羊肉串);

16.11实现-结果

 

16.12适用性

 

  • 1.使用命令模式作为"CallBack"在面向对象系统中的替代。"CallBack"讲的便是先将一个函数登记上,然后在以后调用此函数。
  • 2.需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求发出者可以有不同的生命期。换言之,原先的请求发出者可能已经不在了,而命令对象本身仍然是活动的。这时命令的接收者可以是在本地,也可以在网络的另外一个地址。命令对象可以在串形化之后传送到另外一台机器上去。
  • 3.系统需要支持命令的撤消(undo)。命令对象可以把状态存储起来,等到客户端需要撤销命令所产生的效果时,可以调用undo()方法,把命令所产生的效果撤销掉。命令对象还可以提供redo()方法,以供客户端在需要时,再重新实施命令效果。
  • 4.如果一个系统要将系统中所有的数据更新到日志里,以便在系统崩溃时,可以根据日志里读回所有的数据更新命令,重新调用Execute()方法一条一条执行这些命令,从而恢复系统在崩溃前所做的数据更新。

16.13练习&作业

 

  • 为家里的电器设计一个远程遥控器,通过这个控制器,我们可以控制电器(诸如灯、风扇、空调等)的开关。我们的控制器上有一系列的按钮,分别对应家中的某个电器,当我们在遥控器上按下"On"时,电器打开;当我们按下"Off"时,电器关闭

16.14总结

 

  • Command模式是非常简单而又优雅的一种设计模式,它的根本目的在于将"行为请求者"与"行为实现者"解耦。
  • Command模式的根本目的在于将"行为请求者"与"行为实现者"解耦,在面向对象语言中,常见的实现手段是"将行为抽象为对象"。
  • 实现Command接口的具体命令对象ConcreteCommand有时候根据需要可能会保存一些额外的状态信息。
  • 通过使用Compmosite模式,可以将多个命令封装为一个"复合命令"MacroCommand。
  • Command模式与C#中的Delegate有些类似。但两者定义行为接口的规范有所区别:Command以面向对象中的"接口-实现"来定义行为接口规范,更严格,更符合抽象原则;Delegate以函数签名来定义行为接口规范,更灵活,但抽象能力比较弱。
  • 使用命令模式会导致某些系统有过多的具体命令类。某些系统可能需要几十个,几百个甚至几千个具体命令类,这会使命令模式在这样的系统里变得不实际。

16.15实现代码

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP14Command

{

/// <summary>

/// 网络

/// </summary>

public static class NetWork

{

/// <summary>

/// 网络是否通畅

/// </summary>

public static bool Online = true;

 

}

 

/// <summary>

/// Receiver:命令的真正的执行者(接收者)

/// </summary>

public class WebService

{

 

/// <summary>

/// Action

/// </summary>

/// <param name="OrderNumber"></param>

/// <param name="Total"></param>

/// <param name="Customer"></param>

public void NewOrder(Guid OrderNumber, int Total, string Customer)

{

Console.WriteLine("{0}在{1}下订单,订单金额为:{2},订单编号为:{3}", Customer, DateTime.Now, Total, OrderNumber);

}

}

 

/// <summary>

/// 命令的抽象

/// </summary>

public abstract class Command

{

/// <summary>

/// 执行命令

/// </summary>

public abstract void Execute();

 

public abstract Guid CommandID { get; }

 

}

/// <summary>

/// 具体的命令

/// </summary>

public class OrderCommand : Command

{

private WebService receiver = new WebService();///真的接收者的关联

 

///实现父类中的方法

public override void Execute()

{

this.receiver.NewOrder(this.OrderNumber, this.Total, this.Customer);

}

private Guid id = Guid.Empty;

public OrderCommand()

{

this.id = Guid.NewGuid();

}

public override Guid CommandID

{

get { return this.id; }

}

 

//以下三个属性是属于具体命令的特有的state

public int Total { get; set; }

public Guid OrderNumber

{

get

{

return this.CommandID;

}

}

public string Customer { get; set; }

 

public override string ToString()

{

return string.Format("订单:{0},客户:{1},金额:{2}", this.id, this.Customer, this.Total);

}

}

 

 

/// <summary>

/// 调度者

/// </summary>

public class Invoker

{

private Dictionary<Guid, Command> commands = new Dictionary<Guid, Command>();

 

 

public void AddCommand(Command cmd)

{

if (!this.commands.ContainsKey(cmd.CommandID))

{

this.commands.Add(cmd.CommandID, cmd);

Console.WriteLine("命令已添加:{0}", cmd);

}

else

{

Console.WriteLine("不重复添加同一命令");

}

}

public void RemoveCommand(Command cmd)

{

if (this.commands.ContainsKey(cmd.CommandID))

{

this.commands.Remove(cmd.CommandID);

Console.WriteLine("命令已删除:{0}", cmd);

}

else

{

Console.WriteLine("该命令不存在");

}

}

 

public void ExceuteAllCommand()

{

if (NetWork.Online)

{

foreach (var keyvaluePair in this.commands)

{

Command cmd = keyvaluePair.Value;

 

cmd.Execute();

Console.WriteLine("{0}在线执行完毕",cmd);

}

this.commands.Clear();

}

else

{

Console.WriteLine("现在不在线,当前{0}个命令先不执行,缓存在本地",this.commands.Count);

}

}

 

}

 

class Program

{

 

 

static void Main(string[] args)

{

 

Invoker invoker = new Invoker();

 

Command cmd1 = new OrderCommand() { Total = 200, Customer = "张三" };

Command cmd2 = new OrderCommand() { Total = 800, Customer = "李四" };

Command cmd3 = new OrderCommand() { Total = 500, Customer = "王五" };

 

invoker.AddCommand(cmd1);

Console.WriteLine();

 

invoker.AddCommand(cmd2);

Console.WriteLine();

 

invoker.AddCommand(cmd3);

 

Console.WriteLine();

 

invoker.RemoveCommand(cmd3);

 

NetWork.Online = false;

 

Console.WriteLine();

 

invoker.ExceuteAllCommand();

 

NetWork.Online = true;

 

Console.WriteLine();

 

invoker.ExceuteAllCommand();

 

Console.WriteLine();

 

invoker.ExceuteAllCommand();

 

 

 

}

}

}

 

17. Interpreter 解释器(行为型模式)

 

17.1动机(Motivation)

 

  • 在软件构建过程中,如果某一特定领域的问题比较复杂,类似的模式不断重复出现,如果使用普通的编程方式来实现将面临非常频繁的变化。
  • 在这种情况下,将特定领域的问题表达为某种语法规则下的句子,然后构建一个解释器来解释这样的句子,从而达到解决问题的目的。

17.2意图(Intent)

 

  • 给定一个语言,定义它的文法的一种表示,并定义一种解释器,这个解释器使用该表示来解释语言中的句子。

——《设计模式》GoF

17.3结构(Structure)

 

17.4结构详解

 

17.5生活中的例子

 

  • 机器人,通过对它发出命令,它通过内置的解释器执行相应的动作。
  • 状态相关
    • S:启动
    • E:关机
  • 动作相关
    • L:左转
    • R:右转
    • F:前行
    • B:后退

17.6实现-Context

 

public class 指令//Context

{

private string _txt = "";

public string 内容

{

get { return this._txt; }

set { this._txt = value; }

}

}

17.7实现-AbstractExpression

 

public abstract class 表达式

{

public abstract void 解释(指令 context);

}

17.8实现-TerminalExpression

 

public class 动作表达式 : 表达式

{

public override void 解释(指令 context)

{

string key = context.内容.Substring(0, 1);

string result = "";

switch (key)

{

case "F":

result = "前进";

break;

case "B":

result = "后退";

break;

case "L":

result = "左转弯";

break;

case "R":

result = "右转弯";

break;

}

Console.WriteLine("执行动作:{0}", result);

context.内容 = context.内容.Substring(1, context.内容.Length - 1);//指令减少

}

}

 

 

public class 状态表达式 : 表达式

{

public override void 解释(指令 context)

{

string key = context.内容.Substring(0, 1);

string result = "";

switch (key)

{

case "S":

result = "启动";

break;

case "E":

result = "关机";

break;

}

Console.WriteLine("状态改变:{0}", result);

context.内容 = context.内容.Substring(1, context.内容.Length - 1);//指令减少

}

}

17.9实现-Client

 

public class 机器人

{

public void 执行命令(指令 context)

{

表达式 expression = null;

while (context.内容.Length>0)

{

string 当前命令 = context.内容.Substring(0, 1);

if (当前命令 == "S" || 当前命令 == "E")

{

expression = new 状态表达式();

}

else

{

expression = new 动作表达式();

}

expression.解释(context);

}

}

}

17.10实现-使用

 

机器人 robot = new 机器人();

指令 command = new 指令();

command.内容 = "SLRBFLLRRBBFFE";

robot.执行命令(command);

17.11实现-结果

 

17.12实现要点

 

  • TerminalExpression:
    • 可明确的知道如何解释的表达式
    • 如:"关机"
  • NonterminalExpression:
    • 多义的,需要根据具体情况才能进行最终解释的表达式
    • 如:"你真有才"
  • 只适用于简单文法(表达式)

17.13效果

 

  • 解释器模式提供了一个简单的方式来执行语法,而且容易修改或者扩展语法。
  • 一般系统中很多类使用相似的语法,可以使用一个解释器来代替为每一个规则实现一个解释器。而且在解释器中不同的规则是由不同的类来实现的,这样使得添加一个新的语法规则变得简单。

17.14适用性

 

  • 业务规则频繁变化,且类似的模式不断重复出现,并且容易抽象为语法规则

17.15练习&作业

 

  • 悟空跟随唐僧多年,师徒之间的交流方式越来越模式化,师傅的吩咐如下:
  • 战斗模式:
    • 打:用金箍棒狠打妖怪
    • 杀:不留活口,打死为止
    • 撤:打不过妖怪,掩护师傅撤退
  • 日常模式:
    • 吃:肚子饿了,悟空去化缘
    • 睡:打扫地面,开始睡觉
    • 走:前往下一个取经地点

17.16总结

 

  • 使用Interpreter模式来表示文法规则,从而可以使用面向对象技巧来方便地"扩展"文法。
  • Interpreter模式比较适合简单的文法表示,对于复杂的文法表示,Interperter模式会产生比较大的类层次结构,需要求助于语法分析生成器这样的标准工具。

17.17代码案例

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP14Command

{

/// <summary>

/// 网络

/// </summary>

public static class NetWork

{

/// <summary>

/// 网络是否通畅

/// </summary>

public static bool Online = true;

 

}

 

/// <summary>

/// Receiver:命令的真正的执行者(接收者)

/// </summary>

public class WebService

{

 

/// <summary>

/// Action

/// </summary>

/// <param name="OrderNumber"></param>

/// <param name="Total"></param>

/// <param name="Customer"></param>

public void NewOrder(Guid OrderNumber, int Total, string Customer)

{

Console.WriteLine("{0}在{1}下订单,订单金额为:{2},订单编号为:{3}", Customer, DateTime.Now, Total, OrderNumber);

}

}

 

/// <summary>

/// 命令的抽象

/// </summary>

public abstract class Command

{

/// <summary>

/// 执行命令

/// </summary>

public abstract void Execute();

 

public abstract Guid CommandID { get; }

 

}

/// <summary>

/// 具体的命令

/// </summary>

public class OrderCommand : Command

{

private WebService receiver = new WebService();///真的接收者的关联

 

///实现父类中的方法

public override void Execute()

{

this.receiver.NewOrder(this.OrderNumber, this.Total, this.Customer);

}

private Guid id = Guid.Empty;

public OrderCommand()

{

this.id = Guid.NewGuid();

}

public override Guid CommandID

{

get { return this.id; }

}

 

//以下三个属性是属于具体命令的特有的state

public int Total { get; set; }

public Guid OrderNumber

{

get

{

return this.CommandID;

}

}

public string Customer { get; set; }

 

public override string ToString()

{

return string.Format("订单:{0},客户:{1},金额:{2}", this.id, this.Customer, this.Total);

}

}

 

 

/// <summary>

/// 调度者

/// </summary>

public class Invoker

{

private Dictionary<Guid, Command> commands = new Dictionary<Guid, Command>();

 

 

public void AddCommand(Command cmd)

{

if (!this.commands.ContainsKey(cmd.CommandID))

{

this.commands.Add(cmd.CommandID, cmd);

Console.WriteLine("命令已添加:{0}", cmd);

}

else

{

Console.WriteLine("不重复添加同一命令");

}

}

public void RemoveCommand(Command cmd)

{

if (this.commands.ContainsKey(cmd.CommandID))

{

this.commands.Remove(cmd.CommandID);

Console.WriteLine("命令已删除:{0}", cmd);

}

else

{

Console.WriteLine("该命令不存在");

}

}

 

public void ExceuteAllCommand()

{

if (NetWork.Online)

{

foreach (var keyvaluePair in this.commands)

{

Command cmd = keyvaluePair.Value;

 

cmd.Execute();

Console.WriteLine("{0}在线执行完毕",cmd);

}

this.commands.Clear();

}

else

{

Console.WriteLine("现在不在线,当前{0}个命令先不执行,缓存在本地",this.commands.Count);

}

}

 

}

 

class Program

{

 

 

static void Main(string[] args)

{

 

Invoker invoker = new Invoker();

 

Command cmd1 = new OrderCommand() { Total = 200, Customer = "张三" };

Command cmd2 = new OrderCommand() { Total = 800, Customer = "李四" };

Command cmd3 = new OrderCommand() { Total = 500, Customer = "王五" };

 

invoker.AddCommand(cmd1);

Console.WriteLine();

 

invoker.AddCommand(cmd2);

Console.WriteLine();

 

invoker.AddCommand(cmd3);

 

Console.WriteLine();

 

invoker.RemoveCommand(cmd3);

 

NetWork.Online = false;

 

Console.WriteLine();

 

invoker.ExceuteAllCommand();

 

NetWork.Online = true;

 

Console.WriteLine();

 

invoker.ExceuteAllCommand();

 

Console.WriteLine();

 

invoker.ExceuteAllCommand();

 

 

 

}

}

}

18. Mediator 中介者(行为型模式)

 

18.1动机(Motivation)

 

  • 在软件构建过程中,经常会出现多个对象互相关联交互的情况,对象之间常常会维持一种复杂的引用关系,如果遇到一些需求的更改,这种直接的引用关系将面临不断的变化。
  • 在这种情况下,我们可使用一个"中介对象"来管理对象间的关联关系,避免相互交互的对象之间的紧耦合引用关系,从而更好地抵御变化。

18.2意图(Intent)

 

  • 用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式的相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

——《设计模式》GoF

18.3结构(Structure)

 

18.4结构详解

 

18.5生活中的例子

 

  • 联合国机构下设安理会,专门负责各国之间的调停和协调工作

不同国家之间通过联合国机构进行事务声明

18.6实现-Mediator

 

//Mediator

public abstract class 联合国机构

{ protected System.Collections.Generic.List<国家> 成员国 = new List<国家>();

public void 接收新成员国(国家 colleague)

{

if (!this.成员国.Contains(colleague))

{

this.成员国.Add(colleague);

}

}

public void 开除成员国(国家 colleague)

{

if (this.成员国.Contains(colleague))

{

this.成员国.Remove(colleague);

        Console.WriteLine("<{0}>:开除成员国:{1}", this, colleague);

}

}

public abstract void 发表声明(string 内容, 国家 发表者);

}

18.7实现-ConcreteMediator

 

//ConcreteMediator

public class 安理会:联合国机构

{

public override void 发表声明(string 内容, 国家 发表者)

{

foreach (国家 colleague in base.成员国)

{

if (colleague != 发表者)

{

colleague.接收声明(内容,发表者);

}

}

}

}

18.8实现-Colleague

 

//Colleague

public abstract class 国家

{

protected 联合国机构 _协调人;

public 国家(联合国机构 mediator)

{

this._协调人 = mediator;

mediator.接收新成员国(this);

Console.WriteLine("{0}在{1}加入联合国", this.ToString(), DateTime.Now);

}

public virtual void 发表声明(string 内容)

{

Console.WriteLine("<{0}>发表声明:\r\n\t{1}", this, 内容);

this._协调人.发表声明(内容, this);

}

public virtual void 接收声明(string 内容,国家 发表者)

{

Console.WriteLine("{0}收到<{1}>发表的声明:\r\n\t{2}\t时间:{3}", this,发表者, 内容, DateTime.Now);

}

}

18.9实现-ConcreteColleague

 

public class 美国:国家

{

public 美国(联合国机构 meidator)

: base(meidator)

{

 

}

public override void 接收声明(string 内容, 国家 发表者)

{

base.接收声明(内容, 发表者);

if (内容.Contains("核武器"))

{

Console.WriteLine("!!!!警报:FBI开始对{0}进行全程监控", 发表者);

}

}

}

 

 

public class 伊拉克 : 国家

{

public 伊拉克(联合国机构 meidator)

: base(meidator)

{ }

public override void 接收声明(string 内容, 国家 发表者)

{

base.接收声明(内容, 发表者);

if (内容.Contains("核武器"))

{

Console.WriteLine("<{0}>说:额是冤枉的,{1}不要诬陷好人", this,发表者);

}

}

public override void 发表声明(string 内容)

{

if (内容.Contains("恐怖活动"))

{

Console.WriteLine("<{0}>:******低调,低调,低调*******",this);

}

base.发表声明(内容);

}

}

 

联合国机构 mediator = new 安理会();//调停者mediator

国家 usa = new 美国(mediator);//colleague

国家 iraq = new 伊拉克(mediator);//colleague

usa.发表声明("禁止研制核武器和大规模杀伤武器,否则就开打!");//不直接与iraq耦合

iraq.发表声明("严正声明:本国没有研制任何核武器!");//不直接与usa耦合

iraq.发表声明("别欺国太甚,否则本国会悍然来次恐怖活动!");

mediator.开除成员国(iraq);//iraq不听话,开除之

usa.发表声明("再次声明:禁止研制核武器和大规模杀伤武器,否则就开打!");//iraq再也接收不到了

 

18.10实现-结果

 

18.11实现要点

 

  • 中介者角色集中了太多的责任,所有有关的同事对象都要由它来控制
  • 建议在使用中介者模式的时候注意控制中介者角色的大小

18.12效果

 

  • 将同事角色解耦
  • 系统结构改善:提高了原有系统的可读性、简化原有系统的通信协议——将原有的多对多变为一对多、提高了代码的可复用性
  • "与其多个人累,不如我一个人来累"

18.13适用性

 

  • 多个对象互相关联交互的情况,对象之间维持一种复杂的引用关系
  • 一组对象以定义良好但是复杂的方式进行通信,产生了混乱的依赖关系,也导致对象难以复用

18.14练习&作业

 

  • 自己买房子, 中间的手续:
    • 自己要和房东讲价钱。
    • 到银行贷款。
    • 卖方也是银行贷款的,要把买方帐户钱转到卖方帐户上。
    • 到政府机构办理证件。
  • 通过中介买房子
    • 和中介去谈价。
    • 中介办理贷款的一切手续。
    • 把钱打入中介的中转帐户。
    • 中介办理证件。

18.15总结

 

  • 将多个对象间复杂的关联关系解耦,Mediator模式将多个对象间的控制逻辑进行集中管理,变"多个对象互相关联"为"多个对象和一个中介者关联",简化了系统的维护,抵御了可能的变化。
  • 随着控制逻辑的复杂化,Mediator具体对象的实现可能相当复杂。这时候可以对Mediator对象进行分解处理。
  • Façade模式是解耦系统外到系统内(单向)的对象关联关系;Mediator模式是解耦系统内各个对象之间(双向)的关联关系。

18.16代码案例

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP15Mediator

{

class Program

{

static void Main(string[] args)

{

SupperMarket mediator = new SupperMarket();

Colleague c1 = new Customer(mediator,"李阿婆");

 

Colleague c3 = new Customer(mediator, "王阿姨");

Colleague c2 = new Supplier(mediator);

 

c2.PublishNotice("厂家促销,买一送一");

 

Console.WriteLine();

mediator.Promot();

 

Console.WriteLine();

(mediator as Mediator).Remove(c2);

 

mediator.Promot();

 

Console.WriteLine();

c3.PublishNotice("家乐福最近大米很便宜");

}

}

 

/// <summary>

/// 参与交互的对象

/// </summary>

public abstract class Colleague

{

/// <summary>

/// 中间人的引用

/// </summary>

public Mediator TheMediator { get; set; }

 

/// <summary>

/// 收听中间人的通知

/// </summary>

/// <param name="Notice"></param>

public abstract void ReceiveNotice(string Notice);

 

/// <summary>

/// 发布消息

/// </summary>

/// <param name="Notice"></param>

public void PublishNotice(string Notice)

{

this.TheMediator.Bulletin(Notice);

}

}

 

/// <summary>

/// 中介者

/// </summary>

public abstract class Mediator

{

/// <summary>

/// 对"同事"的聚合

/// </summary>

protected List<Colleague> colleagues = new List<Colleague>();

 

public void Register(Colleague colleague)

{

this.colleagues.Add(colleague);

}

 

public void Remove(Colleague colleague)

{

this.colleagues.Remove(colleague);

}

 

public virtual void Bulletin(string Content)

{

foreach (var colleague in this.colleagues)

{

colleague.ReceiveNotice(Content);

}

}

 

}

 

 

public class SupperMarket : Mediator

{

public override void Bulletin(string Content)

{

Console.WriteLine("超市发布新的公告:{0}", Content);

base.Bulletin(Content);

}

 

public void Promot()

{

this.Bulletin("中秋大促销");

}

}

 

public class Customer : Colleague

{

private string _name;

 

public Customer(Mediator mediator,string Name)

{

this._name = Name;

base.TheMediator = mediator;

mediator.Register(this);

}

public override void ReceiveNotice(string Notice)

{

Console.WriteLine("顾客{1},听到新的通知:{0}", Notice,this._name);

}

}

public class Supplier : Colleague

{

public Supplier(Mediator mediator)

{

base.TheMediator = mediator;

mediator.Register(this);

}

 

public override void ReceiveNotice(string Notice)

{

Console.WriteLine("供应商听到新的通知:{0}", Notice);

Console.WriteLine("加大进货或减少库存");

}

}

}

19. Iterator 迭代器(行为型模式)

 

19.1动机(Motivation)

 

  • 在面向对象的软件设计中,我们经常会遇到一类集合对象,这类集合对象的内部结构可能有着各种各样的实现,但是归结起来,无非有两点是需要我们去关心的:一是集合内部的数据存储结构,二是遍历集合内部的数据。面向对象设计原则中有一条是类的单一职责原则,所以我们要尽可能的去分解这些职责,用不同的类去承担不同的职责。
  • Iterator模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明的访问集合内部的数据。

19.2意图(Intent)

 

  • 提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。

——《设计模式》GoF

19.3结构(Structure)

 

19.4结构详解

 

19.5生活中的例子

 

  • 在公交车上, 售票员不管上来的是什么人, 总会要求每个人都来买票。
  • 不管乘客是什么,售票员的做法都是相同的:
    • 从第一个开始
    • 当前是谁
    • 下一个
    • 是否结束

19.6实现- Iterator

 

//迭代器,用来执行迭代操作

public abstract class Iterator

{

public abstract object First();//第一个

public abstract object CurrentItem();//当前这一个

public abstract object Next();//下一个

public abstract bool IsDone();//结束了没有?

}

19.7实现- Aggregate

 

//聚合类,代表一个可迭代的集合

public abstract class Aggregate

{

public abstract Iterator CreateIterator();//返回一个迭代器

}

19.8实现-ConcreteAggregate

 

//ConcreteAggregate//具体的聚合类

public class 车上的乘客 : Aggregate

{

private System.Collections.ArrayList al = new System.Collections.ArrayList();

public override Iterator CreateIterator()

{

return new ConcreteIterator(this);

}

public void 上车(string 乘客姓名)

{

this.al.Add(乘客姓名);

}

public string this[int index]

{

get

{

return this.al[index].ToString();

}

}

public int 数量

{

get

{

return this.al.Count;

}

}

}

19.9实现-ConcreteIterator

 

//ConcreteIterator//具体的迭代器

public class ConcreteIterator : Iterator

{

private 车上的乘客 _乘客们 = null;

private int 当前第几个 = 0;

public ConcreteIterator(车上的乘客 乘客)

{

this._乘客们 = 乘客;

}

public override object First()

{

if (this._乘客们.数量 > 0)

{

this.当前第几个 = 0;

return this._乘客们[0];

}

else

{

return null;

}

}

 

 

public override object CurrentItem()

{

return this._乘客们[当前第几个];

}

public override object Next()

{

this.当前第几个++;

if (this.当前第几个 < this._乘客们.数量)

{

return this._乘客们[当前第几个];

}

else

{

return null;

}

}

public override bool IsDone()

{

return this.当前第几个 >= this._乘客们.数量;

}

}

19.10实现-使用

 

车上的乘客 隧道六线 = new 车上的乘客();

隧道六线.上车("张三");

隧道六线.上车("李四");

隧道六线.上车("王五");

隧道六线.上车("赵六");

Iterator iterator = 隧道六线.CreateIterator();

 

while (!iterator.IsDone())

{

Console.WriteLine("{0},请你买票,谢谢!", iterator.CurrentItem());

iterator.Next();

}

19.11实现-结果

 

19.12适用性

 

1.访问一个聚合对象的内容而无需暴露它的内部表示。

    2.支持对聚合对象的多种遍历。

    3.为遍历不同的聚合结构提供一个统一的接口(即, 支持多态迭代)。

19.13.NET中的Iterator模式

 

namespace System.Collections

{

public interface IEnumerator

{

    object Current { get; }

    bool MoveNext();

    void Reset();

}

public interface IEnumerable

{

    IEnumerator GetEnumerator();

}

}

19.14.NET版-Ienumerable

 

public class 车上的乘客NET : System.Collections.IEnumerable

{

private System.Collections.ArrayList al = new System.Collections.ArrayList();

public void 上车(string 乘客姓名)

{

this.al.Add(乘客姓名);

}

public string this[int index]

{

get

{ return this.al[index].ToString(); }

}

public int 数量

{

get

{ return this.al.Count; }

}

#region IEnumerable 成员

public System.Collections.IEnumerator GetEnumerator()

{

return new ConcreteIteratorNET(this);

}

#endregion

}

19.15.NET版-Ienumerator

 

public class ConcreteIteratorNET : System.Collections.IEnumerator

{

private 车上的乘客NET _乘客们 = null;

private int 当前第几个 = -1;

public ConcreteIteratorNET(车上的乘客NET 乘客)

{

this._乘客们 = 乘客;

}

#region IEnumerator 成员

public object Current

{

get { return this._乘客们[当前第几个]; }

}

public bool MoveNext()

{

this.当前第几个++;

return this.当前第几个 < this._乘客们.数量;

}

public void Reset()

{

this.当前第几个 = -1;

}

#endregion

}

19.16.NET版-使用

 

车上的乘客NET 隧道六线 = new 车上的乘客NET();

隧道六线.上车("张三");

隧道六线.上车("李四");

隧道六线.上车("王五");

隧道六线.上车("赵六");

System.Collections.IEnumerator iteraotr = 隧道六线.GetEnumerator();

while (iteraotr.MoveNext())

{

Console.WriteLine("{0},请你买票,谢谢!", iteraotr.Current);

}

19.17.NET版-迭代使用

 

车上的乘客NET 隧道六线 = new 车上的乘客NET();

隧道六线.上车("张三");

隧道六线.上车("李四");

隧道六线.上车("王五");

隧道六线.上车("赵六");

foreach (object 乘客 in 隧道六线)

{

Console.WriteLine("{0},请你买票,谢谢!",乘客);

}

19.18.NET2.0版-yield关键字

 

public class 车上的乘客NET2 : System.Collections.IEnumerable

{

private System.Collections.ArrayList al = new System.Collections.ArrayList();

public void 上车(string 乘客姓名)

{

this.al.Add(乘客姓名);

}

#region IEnumerable 成员

public System.Collections.IEnumerator GetEnumerator()

{

foreach (object var in this.al)

{

yield return var;

}

}

 

#endregion

}

19.19总结

 

  • 迭代抽象:访问一个聚合对象的内容而无需暴露它的内部表示。
  • 迭代多态:为遍历不同的集合结构提供一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作。
  • 迭代器的健壮性考虑:遍历的同时更改迭代器所在的集合结构,会导致问题。

19.20代码实现

 

19.20.1version01

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP17IteratorV1

{

 

/// <summary>

/// 聚合对象(集合)

/// </summary>

public abstract class Aggregate

{

//

public abstract Iterator CreateInterator();

 

}

 

/// <summary>

/// 迭代器:能遍历一个集合的遍历行为

/// </summary>

public abstract class Iterator

{

public abstract void Next();

public abstract object Current();

public abstract bool Done { get; }

}

 

 

/// <summary>

/// 班级

/// </summary>

public class Clazz:Aggregate

{

private List<Student> _students = new List<Student>();

public void AddStudent(Student student)

{

this._students.Add(student);

}

 

public int StudentsCount

{

get

{

return this._students.Count;

}

}

public Student this[int index]

{

get

{

return this._students[index];

}

}

 

 

 

 

 

public override Iterator CreateInterator()

{

return new ClazzDESCIterator(this);

}

}

 

/// <summary>

/// 按照升序迭代

/// </summary>

public class ClazzASCIterator : Iterator

{

/// <summary>

/// 关联到需要迭代的那个班级(集合)

/// </summary>

private Clazz myclass = null;

 

/// <summary>

/// 对班级的依赖

/// </summary>

/// <param name="classToInterator"></param>

public ClazzASCIterator(Clazz classToInterator)

{

this.myclass = classToInterator;

}

 

private int currentIndex = 0;

 

public override void Next()

{

this.currentIndex++;

}

 

public override object Current()

{

return this.myclass[this.currentIndex];

}

 

public override bool Done

{

get {

return this.currentIndex == this.myclass.StudentsCount;

}

}

}

 

 

/// <summary>

/// 按照降序迭代

/// </summary>

public class ClazzDESCIterator : Iterator

{

/// <summary>

/// 关联到需要迭代的那个班级(集合)

/// </summary>

private Clazz myclass = null;

 

/// <summary>

/// 对班级的依赖

/// </summary>

/// <param name="classToInterator"></param>

public ClazzDESCIterator(Clazz classToInterator)

{

this.myclass = classToInterator;

this.currentIndex = this.myclass.StudentsCount - 1;

}

 

private int currentIndex;

 

public override void Next()

{

this.currentIndex--;

}

 

public override object Current()

{

return this.myclass[this.currentIndex];

}

 

public override bool Done

{

get

{

return this.currentIndex == -1;

}

}

}

 

 

 

public class Student

{

public Student(string Name)

{

this._Name = Name;

}

public string _Name;

public override string ToString()

{

return this._Name;

}

}

 

 

 

class Program

{

static void Main(string[] args)

{

 

Clazz clazz = new Clazz();

clazz.AddStudent(new Student("张三"));

clazz.AddStudent(new Student("李四"));

clazz.AddStudent(new Student("王五"));

 

Iterator iterator = (clazz as Aggregate).CreateInterator();

while (!iterator.Done)

{

object current = iterator.Current();

Console.WriteLine(current);

iterator.Next();

}

 

 

 

 

}

}

}

19.20.2version02

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP17IteratorV2

{

 

/// <summary>

/// 聚合对象(集合)

/// </summary>

public abstract class Aggregate

{

//

public abstract Iterator CreateInterator();

 

}

 

/// <summary>

/// 迭代器:能遍历一个集合的遍历行为

/// </summary>

public abstract class Iterator

{

public abstract void Next();

public abstract object Current();

public abstract bool Done { get; }

}

 

 

/// <summary>

/// 班级

/// </summary>

public class Clazz : Aggregate

{

private List<Student> _students = new List<Student>();

public void AddStudent(Student student)

{

this._students.Add(student);

}

 

 

 

 

 

 

public override Iterator CreateInterator()

{

return new ClazzDESCIterator(this);

}

 

 

 

/// <summary>

/// 按照升序迭代

/// </summary>

private class ClazzASCIterator : Iterator

{

/// <summary>

/// 关联到需要迭代的那个班级(集合)

/// </summary>

private Clazz myclass = null;

 

/// <summary>

/// 对班级的依赖

/// </summary>

/// <param name="classToInterator"></param>

public ClazzASCIterator(Clazz classToInterator)

{

this.myclass = classToInterator;

}

 

private int currentIndex = 0;

 

public override void Next()

{

this.currentIndex++;

}

 

public override object Current()

{

return this.myclass._students[this.currentIndex];

}

 

public override bool Done

{

get

{

return this.currentIndex == this.myclass._students.Count;

}

}

}

 

 

/// <summary>

/// 按照降序迭代

/// </summary>

private class ClazzDESCIterator : Iterator

{

/// <summary>

/// 关联到需要迭代的那个班级(集合)

/// </summary>

private Clazz myclass = null;

 

/// <summary>

/// 对班级的依赖

/// </summary>

/// <param name="classToInterator"></param>

public ClazzDESCIterator(Clazz classToInterator)

{

this.myclass = classToInterator;

this.currentIndex = this.myclass._students.Count - 1;

}

 

private int currentIndex;

 

public override void Next()

{

this.currentIndex--;

}

 

public override object Current()

{

return this.myclass._students[this.currentIndex];

}

 

public override bool Done

{

get

{

return this.currentIndex == -1;

}

}

}

 

 

 

}

 

 

public class Student

{

public Student(string Name)

{

this._Name = Name;

}

public string _Name;

public override string ToString()

{

return this._Name;

}

}

 

 

 

class Program

{

static void Main(string[] args)

{

 

Clazz clazz = new Clazz();

clazz.AddStudent(new Student("张三"));

clazz.AddStudent(new Student("李四"));

clazz.AddStudent(new Student("王五"));

 

Iterator iterator = (clazz as Aggregate).CreateInterator();

while (!iterator.Done)

{

object current = iterator.Current();

Console.WriteLine(current);

iterator.Next();

}

 

 

 

}

}

}

19.20.3version03

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP17IteratorV3

{

 

 

/// <summary>

/// 班级

/// </summary>

public class Clazz :System.Collections.IEnumerable

{

private List<Student> _students = new List<Student>();

public void AddStudent(Student student)

{

this._students.Add(student);

}

 

 

 

 

/// <summary>

/// 按照升序迭代

/// </summary>

private class ClazzASCIterator :System.Collections.IEnumerator

{

/// <summary>

/// 关联到需要迭代的那个班级(集合)

/// </summary>

private Clazz myclass = null;

 

/// <summary>

/// 对班级的依赖

/// </summary>

/// <param name="classToInterator"></param>

public ClazzASCIterator(Clazz classToInterator)

{

this.myclass = classToInterator;

}

 

private int currentIndex = -1;

 

 

 

 

 

public object Current

{

get { return this.myclass._students[this.currentIndex]; }

}

 

public bool MoveNext()

{

this.currentIndex++;

return this.currentIndex != this.myclass._students.Count;

}

 

public void Reset()

{

this.currentIndex = -1;

}

}

 

 

 

 

public System.Collections.IEnumerator GetEnumerator()

{

return new ClazzASCIterator(this);

}

}

 

 

public class Student

{

public Student(string Name)

{

this._Name = Name;

}

public string _Name;

public override string ToString()

{

return this._Name;

}

}

 

 

 

class Program

{

static void Main(string[] args)

{

 

Clazz clazz = new Clazz();

clazz.AddStudent(new Student("张三"));

clazz.AddStudent(new Student("李四"));

clazz.AddStudent(new Student("王五"));

 

System.Collections.IEnumerator enumerator = (clazz as System.Collections.IEnumerable).GetEnumerator();

while (enumerator.MoveNext())

{

object current = enumerator.Current;

Console.WriteLine(current);

}

 

foreach (var item in clazz)

{

Console.WriteLine(item);

}

 

 

 

 

}

}

}

19.20.4version04

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP17IteratorV4

{

 

 

/// <summary>

/// 班级

/// </summary>

public class Clazz :IEnumerable<Student>

{

private List<Student> _students = new List<Student>();

public void AddStudent(Student student)

{

this._students.Add(student);

}

 

 

 

 

/// <summary>

/// 按照升序迭代

/// </summary>

private class ClazzASCIterator : IEnumerator<Student>

{

 

/// <summary>

/// 关联到需要迭代的那个班级(集合)

/// </summary>

private Clazz myclass = null;

 

/// <summary>

/// 对班级的依赖

/// </summary>

/// <param name="classToInterator"></param>

public ClazzASCIterator(Clazz classToInterator)

{

this.myclass = classToInterator;

}

 

private int currentIndex = -1;

 

 

 

 

 

 

public bool MoveNext()

{

this.currentIndex++;

return this.currentIndex != this.myclass._students.Count;

}

 

public void Reset()

{

this.currentIndex = -1;

}

 

 

public void Dispose()

{

 

GC.Collect();

}

 

object System.Collections.IEnumerator.Current

{

get { return this.myclass._students[this.currentIndex]; }

}

 

public Student Current

{

get { return this.myclass._students[this.currentIndex]; }

}

 

}

 

 

 

 

 

public IEnumerator<Student> GetEnumerator()

{

return new ClazzASCIterator(this);

}

 

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()

{

return new ClazzASCIterator(this);

}

}

 

 

public class Student

{

public Student(string Name)

{

this._Name = Name;

}

public string _Name;

public override string ToString()

{

return this._Name;

}

}

 

 

 

class Program

{

static void Main(string[] args)

{

 

Clazz clazz = new Clazz();

clazz.AddStudent(new Student("张三"));

clazz.AddStudent(new Student("李四"));

clazz.AddStudent(new Student("王五"));

 

System.Collections.IEnumerator enumerator = (clazz as System.Collections.IEnumerable).GetEnumerator();

while (enumerator.MoveNext())

{

object current = enumerator.Current;

Console.WriteLine(current);

}

 

foreach (var item in clazz)

{

 

Console.WriteLine(item);

}

 

 

 

 

}

}

}

19.20.5version05

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP17IteratorV5

{

 

 

/// <summary>

/// 班级

/// </summary>

public class Clazz : IEnumerable<Student>

{

private List<Student> _students = new List<Student>();

public void AddStudent(Student student)

{

this._students.Add(student);

}

 

 

 

public IEnumerator<Student> GetEnumerator()

{

foreach (var student in this._students)

{

yield return student;

}

}

 

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()

{

foreach (var student in this._students)

{

yield return student;

}

}

}

 

 

public class Student

{

public Student(string Name)

{

this._Name = Name;

}

public string _Name;

public override string ToString()

{

return this._Name;

}

}

 

 

 

class Program

{

static void Main(string[] args)

{

 

Clazz clazz = new Clazz();

clazz.AddStudent(new Student("张三"));

clazz.AddStudent(new Student("李四"));

clazz.AddStudent(new Student("王五"));

 

System.Collections.IEnumerator enumerator = (clazz as System.Collections.IEnumerable).GetEnumerator();

while (enumerator.MoveNext())

{

object current = enumerator.Current;

Console.WriteLine(current);

}

 

foreach (var item in clazz)

{

 

Console.WriteLine(item);

}

 

 

 

 

}

}

}

20. Observer 观察者(行为型模式)

 

20.1动机(Motivation)

 

  • 在软件构建过程中,我们需要为某些对象建立一种"通知依赖关系" ——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。
  • 使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。

20.2意图(Intent)

 

  • 定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。

——《设计模式》GoF

20.3结构(Structure)

 

20.4结构详解

 

20.5生活中的例子

 

  • 拍卖,每个投标人都有一个标有数字的牌子用于出价。拍卖师(主题)每次接受一个新的出价都改变了拍卖的当前价格,并且广播给所有的投标人(观察者)进行新的出价。

20.6实现-Observer

 

//Observer

public abstract class 竞拍人

{

//Update()

public abstract void 出价();

}

20.7实现-Subject

 

//Subject

public abstract class 拍卖

{ protected System.Collections.ArrayList observers = new System.Collections.ArrayList();

//Attach

public void 注册竞拍人(竞拍人 person)

{

this.observers.Add(person);

Console.WriteLine("------{0}加入拍卖.", person);

}

//Detach

public void 注销竞拍人(竞拍人 person)

{

this.observers.Remove(person);

Console.WriteLine("-----{0}离开拍卖.", person);

}

//Notify,广播

public virtual void 报价()

{

for (int i = this.observers.Count - 1; i >= 0; i--)

{

((竞拍人)this.observers[i]).出价();

}

}

public abstract bool 结束 { get; }

}

20.8实现-ConcreteSubject

 

//ConcreteSubject

public class 艺术品拍卖 : 拍卖

{

public int _price;

public 艺术品拍卖(int 起拍价格)

{

this._price = 起拍价格;

}

//SubjectState

public int 当前价格

{

get

{

return this._price;

}

set

{

if (this._price > value)

{

return;

}

this._price = value;

}

}

 

 

//Notify,广播

public override void 报价()

{

base.报价();

if (base.observers.Count == 1)

{

Console.WriteLine("{0}最终胜出,最终报价为:{1}", base.observers[0], this._price);

}

else

{

if (base.observers.Count == 0)

{

Console.WriteLine("本次拍卖流拍.");

}

}

}

//SubjectState

public override bool 结束

{

get { return base.observers.Count == 1 || base.observers.Count == 0; }

}

}

20.9实现-ConcreteObserver

 

//ConcreteObserver

public class 艺术品竞拍人 : 竞拍人

{ //ObserverState

private int _myprice;

private int _topprice;

private string _name;

//subject

private 艺术品拍卖 subject;

public 艺术品竞拍人(string 竞拍人姓名, int 起始价位, int 最高心理价位, 艺术品拍卖 参加的拍卖)

{

this._myprice = 起始价位;

this._topprice = 最高心理价位;

this._name = 竞拍人姓名;

this.subject = 参加的拍卖;

this.subject.注册竞拍人(this);

}

public override string ToString()

{

return this._name;

}

 

 

//Update

public override void 出价()

{

if (this.subject.当前价格 > this._topprice)

{

Console.WriteLine("{0}退出,当前最高价格:{1}已超出最高心理价位:{2}", this._name, this.subject.当前价格, this._topprice);

this.subject.注销竞拍人(this);

}

else

{

this._myprice += 1;

this.subject.当前价格 = _myprice;

if (this._myprice >= this.subject.当前价格)

{

Console.WriteLine("{0}报出新的最高价格:{1}", this._name, this._myprice);

}

}

}

}

20.10实现-使用

 

艺术品拍卖 subject = new 艺术品拍卖(8);

竞拍人 observer1 = new 艺术品竞拍人("比尔盖茨", 8, 11, subject);

竞拍人 observer2 = new 艺术品竞拍人("布什", 8, 9, subject);

竞拍人 observer3 = new 艺术品竞拍人("普京", 8, 8, subject);

竞拍人 observer4 = new 艺术品竞拍人("萨打姆", 8, 8, subject);

int n = 1;

while (!subject.结束)

{

Console.WriteLine("========第{0}轮报价=========", n);

subject.报价();

n++;

}

20.11实现-结果

 

20.12实现要点

 

  • Observer依赖Subject
  • Subject需要维护多个Observer
  • Observer的状态依赖于Subject的状态

20.13推模式与拉模式

 

  • 推模式:
    • 当有消息时,把消息信息以参数的形式传递(推)给所有观察者
    • 所有的观察者得到的消息是一样的,也许有些信息对某个观察者来说根本就用不上,也就是观察者不能"按需所取"
    • 当通知消息的参数有变化时,所有的观察者对象都要变化
  • 拉模式:
    • 当有消息时,通知消息的方法本身并不带任何的参数,是由观察者自己到主体对象那儿取回(拉)消息
    • 由观察者自己主动去取消息,需要什么信息,就可以取什么,不会像推模式那样得到所有的消息参数

20.14.NET中的事件和委托

 

  • 利用事件和委托来实现Observer模式更加的简单和优雅,也是一种更好的解决方案
  • 通过Observer模式,把一对多对象之间的通知依赖关系的变得更为松散,大大地提高了程序的可维护性和可扩展性,也很好的符合了开放-封闭原则。

20.15(简单)事件和委托版-猫和老鼠

 

//事件参数

public class 心情Args

{

private bool _good = false;

public bool 好心情

{

get { return this._good; }

set { this._good = value; }

}

}

//委托,用来处理"心情改变"的方法的签名

public delegate void 心情改变Handler(猫 sender, 心情Args e);

20.16猫和老鼠-猫

 

public class 猫

{

private string _name;

public 猫(string name) { this._name = name; }

public override string ToString() { return this._name; }

//猫的事件 ,会心情改变

public event 心情改变Handler 心情改变;

//用来激发事件 的方法,对外公开方法,事件 的激发在内部封装

public void 改变心情(bool 好不好)

{

if (this.心情改变 != null)

{

心情Args e = new 心情Args();

e.好心情 = 好不好;

this.心情改变(this, e);

}

}

}

20.17猫和老鼠-老鼠

 

public class 老鼠

{

private string _name;

public 老鼠(string name) { this._name = name; }

public override string ToString() { return this._name; }

//此方法与 心情改变Handler 委托签名一样,用以处理猫的心情改变事件

public void 应急处理(猫 cat, 心情Args 心情参数)

{

if (心情参数.好心情)

{

Console.WriteLine("{0}:\t今天:{1}的心情不错,不用担心.", this,cat);

}

else

{

Console.WriteLine("{0}:\t10级警报,{1}今天心情大坏,逃命要紧", this,cat);

}

}

}

20.18猫和老鼠-使用

 

猫 tom = new 猫("tom");

老鼠 jerry = new 老鼠("jerry");

老鼠 jack = new 老鼠("jack");

老鼠 steve = new 老鼠("steve");

tom.心情改变 += new 心情改变Handler(jerry.应急处理);

tom.心情改变 += new 心情改变Handler(jack.应急处理);

心情改变Handler steveHandler = new 心情改变Handler(steve.应急处理);

tom.心情改变 += steveHandler;

tom.改变心情(true);

tom.改变心情(false);

tom.心情改变 -= steveHandler;//steve不再监听tom 的 心情改变 事件

Console.WriteLine("-------------steve不再监听tom 的 心情改变 事件 ----------");

tom.改变心情(true);

tom.改变心情(false);

20.19猫和老鼠-结果

 

20.20实现要点

 

  • 本例:猫和老鼠:
    • 只有单向监听:即,老鼠监听猫的事件
    • 只有单向广播:即,只有猫向老鼠广播(激发事件)
  • += 可以实现注册监听
  • -= 可以实现注销监听

20.21事件和委托版-竞拍EventArgs

 

//竞拍的事件,拍卖和竞拍人都会根据此事件参数进行判断

public class 竞拍EventArgs : EventArgs

{

private int _price;

public int 价格

{

get { return this._price; }

set { this._price = value; }

}

}

20.22事件和委托版-拍卖

 

public class 拍卖

{ public int _price;

public 拍卖(int 起拍价格)

{ this._price = 起拍价格; }

public int 当前价格

{

get { return this._price; }

}

//当前最高出价的竞拍人

private 竞拍人 winner = null;

//专门处理竞拍人出价的事件,类似于对出价做出响应

public void 出价Handler(object sender, EventArgs e)

{

竞拍EventArgs args = e as 竞拍EventArgs;

//如果竞排人的价格高于当前的价格

if (args.价格 >= this._price)

{

this._price = args.价格;//更新当前的价格为最高的

竞拍人 person = sender as 竞拍人;//把事件的sender转化为竞拍人

this.winner = person;//当前出价最高的人是事件的sender(激发人)

Console.WriteLine("{0}提出新价格:{1}", person, args.价格);

} }

//会发出事件 :报价(广播)

public event EventHandler 报价 = null;

 

 

//是否结束

public bool 结束

{ get

{

//如果已有最高出价者,并且,只有一个人在报价,此人便胜出,拍卖结束

if (winner != null && this.报价.GetInvocationList().Length == 1)

{

Console.WriteLine("拍卖结束,获胜者为:{0},最高价格为:{1}", this.winner, this._price);

return true;

}

//如果没有人参与报价(没有人对拍卖关注),则流拍,此时,拍卖结束

if (this.报价 == null)

{

Console.WriteLine("本次拍卖流拍.");

return true;

}

else {

//既然没有结束,就激发事件:报价,同时,把竞拍参数传出

竞拍EventArgs e = new 竞拍EventArgs();

e.价格 = this._price;

this.报价(this, e);

return false;

} } } }

20.23事件和委托版-竞拍人

 

public class 竞拍人

{

private int _myprice;

private int _topprice;

private string _name;

public 竞拍人(string 竞拍人姓名, int 起始价位, int 最高心理价位)

{

this._myprice = 起始价位;

this._topprice = 最高心理价位;

this._name = 竞拍人姓名;

}

public override string ToString()

{

return this._name;

}

 

 

 

//竞拍人会对拍卖的出价事件做出响应,引处为事件:出价

public event EventHandler 出价 = null;

//专门处理拍卖出价事件的方法

public void 报价Handler(object sender, EventArgs e)

{

竞拍EventArgs args = e as 竞拍EventArgs;

//如果拍卖的最高价格还不高于自己的最高价,继续出价

if (args.价格 <= this._topprice-2)

{

this._myprice += 2;//这里写成每次+2元

args.价格 = this._myprice;

this.出价(this, args);//激发事件,出价

}

else

{

//把sender转化为拍卖对象

拍卖 subject = sender as 拍卖;

//在拍卖中取消自己对拍卖的报价事件的处理,即取消监听

subject.报价 -= new EventHandler(this.报价Handler);

}

}

}

20.24事件和委托版-使用

 

拍卖 subject = new 拍卖(8);

竞拍人 observer1 = new 竞拍人("比尔盖茨", 8, 15);

竞拍人 observer2 = new 竞拍人("布什", 8, 16);

竞拍人 observer3 = new 竞拍人("普京", 8, 14);

竞拍人 observer4 = new 竞拍人("萨打姆", 8, 13);

subject.报价 += new EventHandler(observer1.报价Handler);

subject.报价 += new EventHandler(observer2.报价Handler);

subject.报价 += new EventHandler(observer3.报价Handler);

subject.报价 += new EventHandler(observer4.报价Handler);

observer1.出价 += new EventHandler(subject.出价Handler);

observer2.出价 += new EventHandler(subject.出价Handler);

observer3.出价 += new EventHandler(subject.出价Handler);

observer4.出价 += new EventHandler(subject.出价Handler);

int n = 1;

Console.WriteLine("========第{0}轮报价=========", n);

while (!subject.结束)

{

n++;

Console.WriteLine("========第{0}轮报价=========", n);

}

20.25事件和委托版-结果

 

20.26实现要点

 

  • 本例:事件和委托版拍卖略为复杂:
    • 拍卖与竞拍人之间相互监听
      • 拍卖监听竞拍人的出价事件
      • 竞拍人监听拍卖的报价事件
    • 这是一种双向监听
  • 更多时候,使用单向监听,即:单向广播

20.27适用性

 

  • 需要实现发布-订阅模型时
  • 需要实现广播机制时

20.28练习&作业

 

  • 员工都在监听老板的发彪事件,以做出相应的处理
  • 事件参数:发彪事件(倒霉的人,时间)
  • 事件发起人:老板
  • 能处理事件的人:在职员工
  • 员工离职后,不再监听事件
  • 注:本例实现单向广播即可

20.29总结

 

  • 使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达致松耦合。
  • 目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。观察者自己决定是否需要订阅通知,目标对象对此一无所知。
  • 在C#的event中,委托充当了抽象的Observer接口,而提供事件的对象充当了目标对象。委托是比抽象Observer接口更为松耦合的设计。

20.30实例代码

 

20.30.1verson01

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP18ObserverV1

{

/// <summary>

/// 关注的目标

/// </summary>

public abstract class Subject

{

private List<Observer> observers = new List<Observer>();

 

public void Attach(Observer observer)

{

observers.Add(observer);

}

 

public void Detach(Observer observer)

{

observers.Remove(observer);

}

 

public void NotifyAllObservers(object args)

{

foreach (var observer in this.observers)

{

observer.Update(args);

}

}

}

 

/// <summary>

/// 关注目标的对象:观察者

/// </summary>

public abstract class Observer

{

/// <summary>

/// 对目标的通知做出响应

/// </summary>

public abstract void Update(object args);

}

 

public class BadMan : Subject

{

private string _currentLocation = "";

 

/// <summary>

/// 自己的状态,给它的观察者使用

/// </summary>

public string CurrentLocation

{

get { return _currentLocation; }

 

}

/// <summary>

/// 逃跑,通过父类进行对所有观察者的通知

/// </summary>

/// <param name="Location"></param>

public void RunAway(string Location)

{

this._currentLocation = Location;

base.NotifyAllObservers(Location);

}

}

 

public class PoliceMan : Observer

{

private BadMan _target = null;

public PoliceMan(BadMan target)

{

this._target = target;

this._target.Attach(this);

}

public override void Update(object args)

{

//var location = this._target.CurrentLocation;

 

Console.WriteLine("警察:在{0}部署警力进行围堵", args);

}

}

 

public class Citizen : Observer

{

private BadMan _target = null;

public Citizen(BadMan target)

{

this._target = target;

this._target.Attach(this);

}

public override void Update(object args)

{

var location = this._target.CurrentLocation;

 

Console.WriteLine("热心市民:打110报警,坏人在:{0}", location);

}

}

 

public class Wife : Observer

{

private BadMan _target = null;

public Wife(BadMan target)

{

this._target = target;

this._target.Attach(this);

}

public override void Update(object args)

{

//拉模式

var location = this._target.CurrentLocation;

 

Console.WriteLine("老婆:在{0}准备接应.", location);

}

}

 

class Program

{

static void Main(string[] args)

{

Subject subject = new BadMan();

 

Observer police = new PoliceMan(subject as BadMan);

Observer citizen = new Citizen(subject as BadMan);

Observer wife = new Wife(subject as BadMan);

 

BadMan badman = subject as BadMan;

 

badman.RunAway("广西");

Console.WriteLine();

badman.RunAway("海南");

Console.WriteLine();

subject.Detach(wife);

badman.RunAway("美国");

 

subject.Detach(police);

Console.WriteLine();

badman.RunAway("澳大利亚");

Console.WriteLine();

subject.Detach(citizen);

badman.RunAway("月球");

 

}

}

}

20.30.2version02

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP18ObserverV2

{

 

/// <summary>

/// 逃跑的参数

/// </summary>

public class RunArgs

{

public string NewLocation { get; set; }

public string OldLocaion { get; set; }

public bool Cancel { get; set; }

}

 

/// <summary>

/// 处理逃跑事件的委托,给事件定义使用

/// </summary>

/// <param name="sender"></param>

/// <param name="args"></param>

public delegate void RunAwaryHandler(BadMan sender, RunArgs args);

 

public class BadMan

{

private string _location="居住地";

 

public string Location

{

get { return _location; }

}

/// <summary>

/// 跑路事件

/// </summary>

public event RunAwaryHandler Run = null;

 

/// <summary>

/// 公开一个方法 ,能激发事件

/// </summary>

/// <param name="NewLocaion"></param>

public void RunAway(string NewLocaion)

{

 

//激发事件

if (this.Run != null)

{

RunArgs args = new RunArgs();

args.NewLocation = NewLocaion;

args.OldLocaion = this._location;

 

 

this.Run(this, args);

if (!args.Cancel)

{

this._location = NewLocaion;

}

else

{

Console.WriteLine("暂时不跑");

}

}

 

}

 

public void RunAway(object sender, EventArgs args)

{

Console.WriteLine("听到警察的警笛");

}

}

 

public class Police

{

public void CatchBadMan(BadMan target, RunArgs args)

{

//推来的数据

Console.WriteLine("警察撤销:{0}的警力,重新在:{1}部属警力",args.OldLocaion,args.NewLocation);

}

 

public void Gogogo()

{

Console.WriteLine("出警");

if (this.GoOut!=null)

{

this.GoOut(this, null);

}

 

}

public event EventHandler GoOut = null;

}

 

public class Wife

{

public void Pray(BadMan husband, RunArgs args)

{

//拉来的数据

var location = husband.Location;

Console.WriteLine("老婆为远在:{0}的老公祈祷",location);

if (args.NewLocation=="海南")

{

args.Cancel = true;

}

}

}

 

class Program

{

static void Main(string[] args)

{

BadMan man = new BadMan();

Police police = new Police();

Wife wife= new Wife();

 

police.GoOut += man.RunAway;

 

 

man.Run += new RunAwaryHandler(police.CatchBadMan);

man.Run += wife.Pray;

 

man.Run += (sender, arguments) =>

{

Console.WriteLine("热心市民:在{0}看到一个罪犯",arguments.NewLocation);

};

 

man.RunAway("南宁");

man.RunAway("海南");

Console.WriteLine("现在所在位置:{0}",man.Location);

 

police.Gogogo();

}

 

}

}

21. Chain of Responsibility 职责链(行为型模式)

 

21.1动机(Motivation)

 

  • 在软件构建过程中,一个请求可能被多个对象处理,但是每个请求在运行时只能有一个接受者,如果显式指定,将必不可少地带来请求发送者与接受者的紧耦合。
  • 如何使请求的发送者不需要指定具体的接受者?让请求的接受者自己在运行时决定来处理请求,从而使两者解耦。

21.2意图(Intent)

 

  • 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。

——《设计模式》GoF

21.3结构(Structure)

 

21.4结构详解

 

21.5生活中的例子

 

  • 员工向主管上级提出申请
  • 申请有两种:
    • 请假和加薪
  • 职责链是:
    • 总监>经理>总经理
  • 总监能处理:
    • 请假,且在2小时以内
  • 经理能处理:
    • 请假,且在5小时以内
  • 总经理能处理:
    • 请假:
      • 小时不限,都通过
    • 加薪:
      • 1000元以内批准
      • 1000元以上要考察再说

21.6实现-申请

 

public enum 申请类型

{

请假,

加薪

}

public class 申请

{

public 申请类型 类型;

public string 内容;

public int 数量;

public override string ToString()

{

return string.Format("内容:{0},类型:{1},数量:{2}", this.内容, this.类型, this.数量);

}

}

21.7实现-管理者

 

public abstract class 管理者

{

protected string _name;

public 管理者(string 姓名)

{

this._name = 姓名;

}

protected 管理者 _superior;

public 管理者 上级主管

{

get {return this._superior;}

set {this._superior = value;}

}

public abstract void 处理员工申请(申请 request);

}

21.8实现-总监

 

public class 总监 : 管理者

{

public 总监(string 姓名) : base(姓名) { }

public override void 处理员工申请(申请 request)

{

if (request.类型 == 申请类型.请假 && request.数量 <= 2)

{

Console.WriteLine("总监-{0}:通过{1}申请,数量{2}", base._name, request.内容, request.数量);

}

else

{

if (base._superior!= null)

{

Console.WriteLine("*总监-{0}:无权处理{1}申请,数量{2},转交给上级.", base._name, request.内容, request.数量);

base._superior.处理员工申请(request);

}

}

}

}

21.9实现-经理

 

public class 经理 : 管理者

{

public 经理(string 姓名) : base(姓名) { }

public override void 处理员工申请(申请 request)

{

if (request.类型 == 申请类型.请假 && request.数量 <= 5)

{

Console.WriteLine("经理-{0}:通过{1}申请,数量{2}", base._name, request.内容, request.数量);

}

else

{

if (base._superior != null)

{

Console.WriteLine("*经理-{0}:无权处理{1}申请,数量{2},转交给上级.", base._name, request.内容, request.数量);

base._superior.处理员工申请(request);

}

}

}

}

21.10实现-总经理

 

public class 总经理 : 管理者

{

public 总经理(string 姓名) : base(姓名) { }

public override void 处理员工申请(申请 request)

{

if (request.类型 == 申请类型.请假)

{

Console.WriteLine("总经理-{0}:通过{1}申请,数量{2}", base._name, request.内容, request.数量);

return;

}

else

{

if (request.类型== 申请类型.加薪 && request.数量<=1000)

{

Console.WriteLine("总经理-{0}:通过{1}申请,数量{2}", base._name, request.内容, request.数量);

return;

}

else

{

Console.WriteLine("总经理-{0}:没有通过{1}申请,数量{2}.再考察一下工作表现再说吧.", base._name, request.内容, request.数量);

     }

}

}

}

21.11实现-使用

 

申请 request = new 申请();

request.类型 = 申请类型.请假;

request.数量 = 1;

request.内容 = "小李要请假";

总监 张三 = new 总监("张三");

经理 李四 = new 经理("李四");

张三.上级主管=李四;

总经理 王五 = new 总经理("王五");

李四.上级主管=王五;

Console.WriteLine("\n第1次申请:{0}", request);

张三.处理员工申请(request);

 

 

request.数量 = 8;

Console.WriteLine("\n第2次申请:{0}", request);

张三.处理员工申请(request);

request.类型 = 申请类型.加薪;

request.内容 = "小李要涨工资";

request.数量 = 800;

Console.WriteLine("\n第3次申请:{0}", request);

张三.处理员工申请(request);

request.数量 = 1500;

Console.WriteLine("\n第4次申请:{0}", request);

张三.处理员工申请(request);

21.12实现-结果

 

21.13实现要点

 

  • 链的顺序可灵活改变-改变"上级主管"即可
  • 发起请求者只需要将请求发送给"最近"的上级,即总监,不需要关心后续的链接关系

21.14适用性

 

  • 系统已经有一个由处理者对象组成的链。这个链可能由复合模式给出
  • 当有多于一个的处理者对象会处理一个请求,而且在事先并不知道到底由哪一个处理者对象处理一个请求。这个处理者对象是动态确定的。
  • 当系统想发出一个请求给多个处理者对象中的某一个,但是不明显指定是哪一个处理者对象会处理此请求。
  • 当处理一个请求的处理者对象集合需要动态地指定时。

21.15练习&作业

 

  • 小孩子向家长要零花钱
  • 家长之间的关系:
    • 妈妈 >爸爸>奶奶>爷爷
  • 妈妈:50元以内
  • 爸爸:200元以内
  • 奶奶:500元以内
  • 爷爷:1000元以内
  • 超过1000元,不允许

21.16总结

 

  • Chain of Responsibility 模式的应用场合在于"一个请求可能有多个接受者,但是最后真正的接受者只有一个",只有这时候请求发送者与接受者的耦合才有可能出现"变化脆弱"的症状,职责链的目的就是将二者解耦,从而更好地应对变化。
  • 应用了Chain of Responsibility 模式后,对象的职责分派将更具灵活性。我们可以在运行时动态添加/修改请求的处理职责。
  • 如果请求传递到职责链的末尾仍得不到处理,应该有一个合理的缺省机制。这也是每一个接受对象的责任,而不是发出请求的对象的责任

22. Memento 备忘录(行为型模式)

 

22.1动机(Motivation)

 

  • 在软件构建过程中,某些对象的状态在转换过程中,可能由于某种需要,要求程序能够回溯到对象之前处于某个点时的状态。如果使用一些公有接口来让其他对象得到对象的状态,便会暴露对象的细节实现。
  • 如何实现对象状态的良好保存与恢复?但同时又不会因此而破坏对象本身的封装性。

22.2意图(Intent)

 

  • 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。

——《设计模式》GoF

22.3结构(Structure)

 

22.4结构详解

 

22.5生活中的例子

 

  • 玩游戏(单机)时,需要经常保存进度,在必要的时候(如gameover),再读取存档,继续游戏(即S/L大法)。
  • 三个对象(与下图对应关系):
    • 游戏=游戏角色
    • 进度=角色状态存储箱
    • 存取档工具=角色状态管理者

22.6实现- Memento

 

//Memento

public class 游戏进度

{

private int _attack;

private int _life;

private int _defense;

private DateTime _createtime;

public 游戏进度(int 攻击, int 生命, int 防御)

{

this._attack = 攻击;

this._life = 生命;

this._defense = 防御;

this._createtime = DateTime.Now;

}

public int 攻击力

{

get { return this._attack; }

}

 

 

public int 生命力

{

get { return this._life; }

}

public int 防御力

{

get { return this._defense; }

}

public DateTime 存档日期

{

get { return this._createtime; }

}

public override string ToString()

{

return string.Format("{0}:生命:{1},攻击:{2},防御:{3}", this._createtime, this._life, this._attack, this._defense);

}

}

22.7实现-Originator

 

public class 游戏

{

private int _attack;

private int _life;

private int _defense;

public 游戏()

{

this._attack = 100;

this._life = 100;

this._defense = 100;

Console.WriteLine("游戏开始于:{0}.", DateTime.Now);

}

public int 攻击力

{

get { return this._attack; }

}

public int 生命力

{

get { return this._life; }

}

public int 防御力

{

get { return this._defense; }

}

public void 查看状态()

{

Console.WriteLine("当前生命力:{0},攻击力:{1},防御力:{2},游戏是否结束:{3}", this._life, this._attack, this._defense,this.游戏结束);

}

 

 

public void 战斗()

{

Console.WriteLine("\t开始战斗.");

System.Threading.Thread.Sleep(3000);

int lifeless = new Random().Next(200);

this._life = this._life - lifeless;

int attackless = new Random().Next(100);

this._attack = this._attack - attackless;

int defenseless = new Random().Next(100);

this._defense = this._defense - defenseless;

Console.WriteLine("本回合战斗结束,损失:生命:{0},攻击:{1},防御:{2}", lifeless, attackless, defenseless);

}

public bool 游戏结束

{

get { return this._life <= 0; }

}

public 游戏进度 存档()

{

return new 游戏进度(this._attack, this._life, this._defense);

}

public void 读档(游戏进度 saved)

{

this._life = saved.生命力;

this._attack = saved.攻击力;

this._defense = saved.防御力;

Console.WriteLine("读档结束:{0}", saved);

}

}

22.8实现- CareTaker

 

//CareTaker

public class 存取档工具

{

private System.Collections.Hashtable saves

= new System.Collections.Hashtable();

public void 存档(string 进度编号, 游戏进度 progress)

{

if (!this.saves.ContainsKey(进度编号))

{

this.saves.Add(进度编号, progress);

}

else

{

this.saves[进度编号] = progress;

}

Console.WriteLine("进度已保存,进度编号:{0},进度信息:{1}", 进度编号, progress);

}

public 游戏进度 读档(string 进度编号)

{

return this.saves[进度编号] as 游戏进度;

}

}

22.9实现-使用

 

游戏 game = new 游戏();

存取档工具 sltool = new 存取档工具();

游戏进度 progress = game.存档();

sltool.存档("000", progress);

game.查看状态();//查询初始状态

Console.WriteLine("\n第一次战斗\n");

game.战斗();//第一次战斗

game.查看状态();

progress = game.存档();

sltool.存档("001", progress);

Console.WriteLine("\n第二次战斗\n");

game.战斗();//第二次战斗

game.查看状态();

progress = game.存档();

sltool.存档("002", progress);

 

 

Console.WriteLine("\n读档:000\n");

progress = sltool.读档("000");

game.读档(progress);

game.查看状态();

Console.WriteLine("\n读档:001\n");

progress = sltool.读档("001");

game.读档(progress);

game.查看状态();

Console.WriteLine("\n读档:002\n");

progress = sltool.读档("002");

game.读档(progress);

game.查看状态();

22.10实现-结果

 

22.11实现要点

 

  • 让"游戏"自己来创建"进度"
  • "存取档工具"只是负责"进度"的读取,并不关心"进度"的内部细节
  • 根据"进退"来重新初始化"游戏"内部细节的工作,应交给"游戏"自己实现
  • "存取档工具"可维护多个"进度"

22.12适用性

 

  • 必须保存一个对象在某一个时刻的(部分)状态,这样以后需要时它才能恢复到先前的状态;
  • 如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。

22.13总结

 

  • 备忘录(Memento)存储原发器(Originator)对象的内部状态,在需要时恢复原发器状态。
  • Memento模式适用于"由原发器管理,却又必须存储在原发器之外的信息"
  • 在实现Memento模式中,要防止原发器以外的对象访问备忘录对象。备忘录对象有两个接口,一个为原发器使用的宽接口;一个为其他对象使用的窄接口。
  • 在实现Memento模式时,要考虑拷贝对象状态的效率问题,如果对象开销比较大,可以采用某种增量式改变来改进Memento模式。

22.14代码案例

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP20Memento

{

/// <summary>

/// 备忘:仅封装数据

/// </summary>

public class Memento

{

public int Life { get; set; }

public override string ToString()

{

return string.Format("当前生命:{0}", this.Life);

}

}

 

 

 

public interface IOriginator

{

void Load(Memento memento);

Memento Save();

}

 

public class Game : IOriginator

{

private int _life = 100;

public void Fight()

{

System.Threading.Thread.Sleep(2000);

this._life -= new Random().Next(100);

}

public override string ToString()

{

return string.Format("当前生命值:{0},游戏结束?{1}", this._life, this._life < 1);

}

 

 

 

 

public void Load(Memento memento)

{

this._life = memento.Life;

}

 

public Memento Save()

{

return new Memento() { Life = this._life };

}

}

 

/// <summary>

/// 存档管理

/// </summary>

public class CareTaker

{

private Dictionary<int, Memento> mementos = new Dictionary<int, Memento>();

 

public void SaveMemento(int ID, Memento memento)

{

this.mementos.Add(ID, memento);

}

public Memento LoadMemento(int ID)

{

return this.mementos[ID];

}

 

public override string ToString()

{

StringBuilder sb = new StringBuilder();

foreach (var item in this.mementos)

{

sb.AppendFormat("存档ID:{0}--{1}\r\n",item.Key,item.Value);

}

return sb.ToString();

}

}

 

class Program

{

static void Main(string[] args)

{

IOriginator originator = new Game();

Console.WriteLine(originator);

 

Memento m = originator.Save();

Console.WriteLine(m);

 

 

CareTaker caretaker = new CareTaker();

caretaker.SaveMemento(1, m);//第一次存档

 

(originator as Game).Fight();

 

Console.WriteLine(originator);

 

m = originator.Save();

Console.WriteLine(m);

caretaker.SaveMemento(2, m);//第二次存档

 

(originator as Game).Fight();

 

Console.WriteLine(originator);

 

m = originator.Save();

Console.WriteLine(m);

caretaker.SaveMemento(3, m);//第三次存档

 

Console.WriteLine("-----------");

Console.WriteLine(caretaker);

 

///还原

///

 

Game game = originator as Game;

 

game.Load(caretaker.LoadMemento(2));

Console.WriteLine(game);

game.Load(caretaker.LoadMemento(1));

Console.WriteLine(game);

game.Load(caretaker.LoadMemento(2));

Console.WriteLine(game);

game.Load(caretaker.LoadMemento(3));

Console.WriteLine(game);

 

 

}

}

}

23. State 状态(行为型模式)    

 

23.1动机(Motivation)

 

  • 在软件构建过程中,某些对象的状态如果改变,其行为也会随之而发生变化,比如文档处于只读状态,其支持的行为和读写状态支持的行为就可能完全不同。
  • 如何在运行时根据对象的状态来透明地更改对象的行为?而不会为对象操作和状态转化之间引入紧耦合?

23.2意图(Intent)

 

  • 允许一个对象在其内部状态改变时改变它的行为。从而使对象看起来似乎修改了其行为。

——《设计模式》GoF

23.3结构(Structure)

 

23.4结构详解

 

23.5生活中的例子

 

  • 情人节里,要:过节。
  • 过节时要:庆祝节日:
    • 单身状态下:单身情歌
    • 热恋状态下:花前月下
    • 失恋状态下:对酒当歌几多愁

23.6实现- State

 

//State

public abstract class 节日状态

{

public abstract void 庆祝节日();//Handle()

}

public class 单身状态 : 节日状态 //ConcreteSate

{

public override void 庆祝节日()

{

Console.WriteLine("单身情歌,没有情人的情人节:( ~~~~");

}

}

public class 热恋状态 : 节日状态 //ConcreteSate

{

public override void 庆祝节日()

{

Console.WriteLine("花好月园,花前月下,生活真美好:) ~~~~");

}

}

public class 失恋状态 : 节日状态 //ConcreteSate

{

public override void 庆祝节日()

{

Console.WriteLine("对酒当歌几多愁,恰似一江春水向东流 &_& ~~~~");

}

}

23.7实现- Context

 

//Context

public class 情人节

{

//-state

private 节日状态 state = new 单身状态();

public 节日状态 状态

{

get { return this.state; }

set { this.state = value; }

}

//Request()

public void 过节()

{

this.state.庆祝节日();

}

}

23.8实现-使用

 

情人节 loverday = new 情人节();

Console.WriteLine("\n第一年的情人节:");

loverday.状态 = new 单身状态();

loverday.过节();

Console.WriteLine("\n第二年的情人节:");

loverday.状态 = new 热恋状态();

loverday.过节();

Console.WriteLine("\n第三年的情人节:");

loverday.状态 = new 失恋状态();

loverday.过节();

23.9实现-结果

 

23.10适用性

 

  • 一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。
  • 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常, 有多个操作包含这一相同的条件结构。State模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。

23.11练习&作业

 

  • 员工写程序功能, 依赖于工作时间和工作是否完成
  • "工作"作为参数传入"状态"的"写程序"方法
  • "工作"对象的状态,在具体"状态"类中来改变

23.12总结

 

  • State模式将所有与一个特定状态相关的行为都放入一个State的子类对象中,在对象状态切换时,切换相应的对象;但同时维持State的接口,这样实现了具体操作与状态转换之间的解耦。
  • 为不同的状态引入不同的对象使得状态转换变得更加明确,而且可以保证不会出现状态不一致的情况,因为转换是原子性的——即要么彻底转换过来,要么不转换。
  • 如果State对象没有实例变量,那么各个上下文可以共享同一个State对象,从而节省对象开销。

23.13实例代码

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP21State

{

class bod

{

}

}

 

 

 

 

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP21State

{

/// <summary>

/// State

/// </summary>

public abstract class BodyState

{

public abstract void Work();

 

}

 

public class GoodState : BodyState

{

public override void Work()

{

Console.WriteLine("精力充沛地工作");

}

}

 

public class ColdState : BodyState

{

public override void Work()

{

Console.WriteLine("打喷嚏+咳嗽工作");

}

}

 

 

/// <summary>

/// Context

/// </summary>

public class Teacher

{

private Dictionary<int, BodyState> _states = new Dictionary<int, BodyState>();

public Teacher()

{

this._states.Add(1, new GoodState());

this._states.Add(2,new ColdState());

}

 

/// <summary>

/// Request

/// </summary>

public void Teach(bool Good)

{

 

if (Good)

{

this._states[1].Work();

}

else

{

this._states[2].Work();

}

}

}

 

class Program

{

static void Main(string[] args)

{

Teacher t = new Teacher();

t.Teach(true);

t.Teach(false);

}

}

}

 

24. Strategy 策略(行为型模式)

 

24.1动机(Motivation)

 

  • 在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂;而且有时候支持不使用的算法也是一个性能负担。
  • 如何在运行时根据需要透明地更改对象的算法?将算法与对象本身解耦,从而避免上述问题?

24.2意图(Intent)

 

  • 定义一系列算法,把它们一个个封装起来,并且使它们可互相替换。该模式使得算法可独立于使用它的客户而变化。

——《设计模式》GoF

24.3结构(Structure)

 

24.4结构详解

 

24.5生活中的例子

 

  • 超市购物,根据总价有不同的优惠策略:
    • 不折扣现金,要赠品
    • 满300元送100元
    • 打8折
    • ……….

24.6实现-Strategy

 

//Strategy策略

public abstract class 优惠策略

{

//algorithmInterface 算法定义

public abstract double 计算优惠(double 总价);

}

24.7实现-ConcreteStrategy

 

public class 要赠品 : 优惠策略 //ConcreteStrategy

{

public override double 计算优惠(double 总价)

{

Console.WriteLine("不折扣现金,要赠品:\t送一个玩具狗熊.");

return 总价;

}

}

 

public class 打8折 : 优惠策略 //ConcreteStrategy

{

public override double 计算优惠(double 总价)

{

Console.WriteLine("打8折.");

return 总价*0.8;

}

}

 

 

public class 返现金 : 优惠策略 //ConcreteStrategy

{

public override double 计算优惠(double 总价)

{

Console.WriteLine("满300元送100元.");

if (总价 >= 300)

{

return 总价 - 100;

}

else

{

return 总价;

}

}

}

24.8实现-Context

 

//Context

public class 顾客

{

private double total;

public 顾客(double 总价)

{

this.total = 总价;

}

public void 使用优惠(优惠策略 strategy)

{

double 实价 = 0;

实价 = strategy.计算优惠(this.total);

Console.WriteLine("本次优惠后,实际价格为:{0}\n", 实价);

}

}

24.9实现-使用

 

double 总价 = 1000;

优惠策略 strategy;

顾客 customer = new 顾客(总价);

strategy = new 要赠品();

customer.使用优惠(strategy);

strategy = new 返现金();

customer.使用优惠(strategy);

strategy = new 打8折();

customer.使用优惠(strategy);

24.10实现-结果

 

24.11实现要点

 

  • 使用的过程中,必须要"知道"具体的策略类,耦合性高
  • 抽象类到具体类的实例化过程还可再改进更灵活
  • 如果具体算法需要更多的参数(不只是一个"总价"),可以考虑使用实例类来参数进行聚合,再传入参数类
  • 使用"反射"解耦使用者 必须"知道"具体策略类的编码时依赖

24.12升级版-使用反射

 

  • 动态加载一个程序集(exe,dll):
    • System.Reflection.Assembly.LoadFile(DLLfilename)
  • 获得一个程序集里的所有类型:
    • Assembly的实例.GetTypes()
  • 判断一个Type是否继承自指定的类:
    • Type的实例. IsSubclassOf(Type)
  • 获得一个Type的全名(包含所在命名空间):
    • Type的实例.FullName
  • 根据一个Type的全名,创建它的实例:
    • Assembly的实例. CreateInstance(Type的全名)

24.13反射-界面

 

24.14反射-动态装载程序集,显示可用子类

 

private string DLLfilename;

private void buttonOpenDLL_Click(object sender, EventArgs e)

{

OpenFileDialog of = new OpenFileDialog();

of.Filter = "可执行程序(*.exe)|*.exe|动态链接库(*.dll)|*.dll";

if (of.ShowDialog() == DialogResult.OK)

{

this.comboBoxStrategies.Items.Clear();

this.DLLfilename = of.FileName;

System.Reflection.Assembly container = System.Reflection.Assembly.LoadFile(DLLfilename);

foreach (Type type in container.GetTypes())

{

if (type.IsSubclassOf(typeof(优惠策略)))

{

this.comboBoxStrategies.Items.Add(type.FullName);

}

}

if (this.comboBoxStrategies.Items.Count > 0)

{

this.comboBoxStrategies.SelectedIndex = 0;

}

}

}

24.15反射-动态创建实例并使用

 

 

private void buttonOK_Click(object sender, EventArgs e)

{

System.Reflection.Assembly container = System.Reflection.Assembly.LoadFile(this.DLLfilename);

优惠策略 strategy = container.CreateInstance(this.comboBoxStrategies.Text) as 优惠策略;

double 总价 = double.Parse(this.textBoxTotal.Text);

顾客 customer = new 顾客(总价);

customer.使用优惠(strategy);

}

24.16反射-结果-加载程序集

 

24.17反射-结果

 

24.18练习&作业

 

  • 球类比赛的胜出与否依赖于是哪种球赛
  • 裁判比赛结果是预先定义的抽象算法
  • 在不同的具体比赛中,实现裁判结果的算法实现
  • 界面要求:
    • 程序界面中允许用户选择不同的程序集,并在界面中显示指定程序集中可用的具体比赛算法类
    • 用户在Windows程序中输入两队的比分
    • 用户选择一个可使用的具体比赛算法类,程序显示使用该具体比分裁判算法类进行裁判操作的结果

24.19总结

 

  • Strategy及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换。所谓封装算法,支持算法的变化。
  • Strategy模式提供了用条件判断语句以外的另一种选择,消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要Strategy模式。
  • 与State类似,如果Strategy对象没有实例变量,那么各个上下文可以共享同一个Strategy对象,从而节省对象开销。
  • 结合反射技术,能达到最好的解耦效果

24.20实现代码

 

24.20.1version01

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP22Strategy

{

/// <summary>

/// Strategy

/// </summary>

public abstract class Strategy

{

/// <summary>

/// 算法

/// </summary>

/// <param name="currentTotal"></param>

public abstract void Algorith(ref double currentTotal);

}

 

 

public class Gift : Strategy

{

 

public override void Algorith(ref double currentTotal)

{

Console.WriteLine("送一个玩具");

}

}

 

public class Discount : Strategy

{

 

public override void Algorith(ref double currentTotal)

{

currentTotal *= 0.8;

Console.WriteLine("打8折");

}

}

 

/// <summary>

/// 减法(抵扣)

/// </summary>

public class Reduce : Strategy

{

 

public override void Algorith(ref double currentTotal)

{

if (currentTotal>=300)

{

currentTotal -= 100;

Console.WriteLine("抵扣100元");

}

else

{

Console.WriteLine("不够抵扣");

}

}

}

 

 

public class Order

{

private double _total;

public Order(double Total)

{

this._total = Total;

}

 

private List<Strategy> _strategies = new List<Strategy>();

public void AddStrategy(Strategy strategy)

{

this._strategies.Add(strategy);

}

 

public void UsePromot()

{

foreach (var item in this._strategies)

{

this.UsePromot(item);

}

}

 

public void UsePromot(Strategy strategy)

{

strategy.Algorith(ref this._total);

Console.WriteLine("最终价格:{0}",this._total);

 

}

}

 

 

 

class Program

{

static void Main(string[] args)

{

Order order = new Order(1000);

Strategy s1 = new Gift();

Strategy s2 = new Discount();

Strategy s3 = new Reduce();

 

order.AddStrategy(s1);

order.AddStrategy(s2);

order.AddStrategy(s3);

 

order.UsePromot();

 

}

}

}

24.20.2dll库

 

1》库1

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP22Strategy3rdPart

{

public class Holidy:DP22StrategyV2.Strategy

{

public override void Algorith(ref double currentTotal)

{

if (currentTotal>20000)

{

Console.WriteLine("送你新马泰七日旅游");

}

else

{

if (currentTotal>10000)

{

Console.WriteLine("九寨沟3日旅游");

}

}

}

}

 

public class Discount : DP22StrategyV2.Strategy

{

public override void Algorith(ref double currentTotal)

{

currentTotal *= 0.5;

}

}

 

}

2》库2

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using DP22StrategyV2;

 

namespace DP22StrategyDLL

{

 

public class Gift : Strategy

{

 

public override void Algorith(ref double currentTotal)

{

Console.WriteLine("送一个玩具");

}

}

 

public class Discount : Strategy

{

 

public override void Algorith(ref double currentTotal)

{

currentTotal *= 0.8;

Console.WriteLine("打8折");

}

}

 

/// <summary>

/// 减法(抵扣)

/// </summary>

public class Reduce : Strategy

{

 

public override void Algorith(ref double currentTotal)

{

if (currentTotal >= 300)

{

currentTotal -= 100;

Console.WriteLine("抵扣100元");

}

else

{

Console.WriteLine("不够抵扣");

}

}

}

 

}

3》库3

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Windows.Forms;

 

namespace DP22StrategyWin

{

public partial class Form1 : Form

{

public Form1()

{

InitializeComponent();

 

this.listBox1.Items.Clear();

}

 

private void button1_Click(object sender, EventArgs e)

{

this.openFileDialog1.InitialDirectory =@"E:\NET软件工程师1001\12DP\Code\DP22StrategyDLL\bin\Debug" ;//Application.StartupPath;

if (this.openFileDialog1.ShowDialog()== System.Windows.Forms.DialogResult.OK)

{

System.Reflection.Assembly assembly = System.Reflection.Assembly.LoadFrom(this.openFileDialog1.FileName);

 

foreach (Type item in assembly.GetTypes())

{

if (item.IsSubclassOf(typeof(DP22StrategyV2.Strategy)))

{

object instanceOfStrategy = assembly.CreateInstance(item.FullName);

this.listBox1.Items.Add(instanceOfStrategy);

}

}

}

}

 

private void button2_Click(object sender, EventArgs e)

{

DP22StrategyV2.Order order = new DP22StrategyV2.Order(double.Parse(this.textBox1.Text));

 

foreach (var item in this.listBox1.SelectedItems)

{

order.AddStrategy(item as DP22StrategyV2.Strategy);

}

 

var result = order.UsePromot();

MessageBox.Show(result);

}

}

}

 

 

 

 

 

 

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP22StrategyV2

{

/// <summary>

/// Strategy

/// </summary>

public abstract class Strategy

{

/// <summary>

/// 算法

/// </summary>

/// <param name="currentTotal"></param>

public abstract void Algorith(ref double currentTotal);

}

 

 

 

public class Order

{

private double _total;

public Order(double Total)

{

this._total = Total;

}

 

private List<Strategy> _strategies = new List<Strategy>();

public void AddStrategy(Strategy strategy)

{

this._strategies.Add(strategy);

}

 

public string UsePromot()

{

StringBuilder sb = new StringBuilder();

foreach (var item in this._strategies)

{

sb.Append (this.UsePromot(item));

}

return sb.ToString();

}

 

public string UsePromot(Strategy strategy)

{

strategy.Algorith(ref this._total);

return string.Format("最终价格:{0}\r\n", this._total);

 

}

}

 

 

}

25. Visitor 访问者(行为型模式)

 

25.1动机(Motivation)

 

  • 在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给子类带来很繁重的变更负担,甚至破坏原有设计。
  • 如何在不更改类层次结构的前提下,在运行时根据需要透明地为类层次结构上的各个类动态添加新的操作,从而避免上述问题?

25.2意图(Intent)

 

  • 表示一个作用于某对象结构中的各元素的操作。它可以在不改变各元素的类的前提下定义作用于这些元素的新的操作。

——《设计模式》GoF

25.3结构(Structure)

 

25.4结构详解

 

25.5生活中的例子

 

  • 老师对学生做家访
  • 老师:
    • 数学老师
    • 英语老师
    • ….
  • 学生:
    • 天才学生
    • 调皮学生
    • …..
  • 学校是老师和学生的维护和调度者

25.6实现- ConcreteElement

 

//ConcreteElement

public class 天才学生 : 学生

{

public override void 接受家访(老师 vistor)

{

vistor.做天才学生家访(this);

this.显示天才();

}

//具体类中自己的方法

public void 显示天才()

{

Console.WriteLine("天才学生做了个腾空1800度空翻");

}

}

 

 

public class 调皮学生 : 学生

{

public override void 接受家访(老师 vistor)

{

vistor.做调皮学生家访(this);

this.开玩笑();

}

//具体类中自己的方法

public void 开玩笑()

{

Console.WriteLine("调皮学生和老师开玩笑,哈哈哈哈");

}

}

25.7实现- ConcreteVistor

 

//ConcreteVistor

public class 数学老师 : 老师

{

public override void 做天才学生家访(天才学生 element)

{

Console.WriteLine("\n{0}:孩子啊,你是个天才,一定要学好数学,走遍天下都不怕.", this.GetType().Name);

Console.WriteLine("{0}家访了:{1}", this.GetType().Name, element.GetType().Name);

}

public override void 做调皮学生家访(调皮学生 element)

{

Console.WriteLine("\n{0}:孩子啊,你虽然调皮,但是数学还是要好好学,否则不会数钱的.", this.GetType().Name);

Console.WriteLine("{0}家访了:{1}", this.GetType().Name, element.GetType().Name);

}

}

 

 

public class 英语老师 : 老师

{

public override void 做天才学生家访(天才学生 element)

{

Console.WriteLine("\n{0}:孩子啊,你是个天才,学好英语,将来出国到利比亚.", this.GetType().Name);

Console.WriteLine("{0}家访了:{1}", this.GetType().Name, element.GetType().Name);

}

public override void 做调皮学生家访(调皮学生 element)

{

Console.WriteLine("\n{0}:孩子啊,你虽然调皮,但也要学好英语,否则老外骂你也听不懂啊.", this.GetType().Name);

Console.WriteLine("{0}家访了:{1}", this.GetType().Name, element.GetType().Name);

}

}

25.8实现-ObjectStructure

 

//ObjectStructure

public class 学校

{

private System.Collections.ArrayList elements = new System.Collections.ArrayList();

public void 学生入学(学生 element)

{

this.elements.Add(element);

}

public void 学生退学(学生 element)

{

this.elements.Remove(element);

}

public void 老师入职(老师 vistor)

{

//入职先做一遍家访

foreach (学生 element in this.elements)

{

element.接受家访(vistor);

}

}

}

25.9实现-使用

 

学校 school = new 学校();

学生 s1 = new 天才学生();

学生 s2 = new 调皮学生();

school.学生入学(s1);

school.学生入学(s2);

老师 mathTeacher = new 数学老师();

Console.WriteLine("\n\t数学老师入职,先去家访.\n");

school.老师入职(mathTeacher);

 

 

老师 englishTeacher = new 英语老师();

Console.WriteLine("\n\t英语老师入职,先去家访.\n");

school.老师入职(englishTeacher);

Console.WriteLine("\n\t调皮学生退学.\n");

school.学生退学(s2);

老师 et = new 英语老师();//第3个老师

Console.WriteLine("\n\t新的英语老师入职,先去家访.\n");

school.老师入职(et);

25.10实现-结果

 

25.11实现要点

 

  • 访问者模式有2个参与者
    • 访问者
    • 被访问者
  • 访问者是一组操作的集合,对他所知的被访问者进行操作。
  • 访问者模式有2个重点,
    • 已知被访问者,就是说访问者只能访问有限的对象,如果所访问的对象增加了,那么就要修改访问者了
    • 被访问者必须有个统一的被访问接口。

25.12适用性

 

  • 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
  • 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作"污染"这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。
  • 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。

25.13练习&作业

 

  • 警察检查经营场所
  • 访问者是警察
  • 被访问者是经营场所
  • 警察分为:
    • 民警
    • 特警
  • 经营场所分为:
    • 酒吧
    • 旅馆

25.14总结

 

  • Visitor模式通过所谓双重分发(double dispatch)来实现在不更改Element类层次结构的前提下,在运行时透明地为类层次结构上的各个类动态添加新的操作。
  • 所谓双重分发即Visitor模式中间包括了两个多态分发(注意其中的多态机制):第一个为accept方法的多态辨析;第二个为visit方法的多态辨析。
  • Visitor模式的最大缺点在于扩展类层次结构(增添新的Element子类),会导致Visitor类的改变。因此Vistor模式适用于"Element类层次结构稳定,而其中的操作却经常面临频繁改动"。

25.15代码实现

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace DP23Visitor

{

 

//游客

public abstract class Vistitor

{

public abstract void VisitChinaMuseum(ChinaMuseum museum);

public abstract void VisitEnglandMuseum(EnglandMuseum museum);

}

 

//现有的对象结构(element)

 

public abstract class Museum

{

/// <summary>

/// 接待游客

/// </summary>

/// <param name="visitor"></param>

public abstract void Accept(Vistitor visitor);

}

 

public class ChinaMuseum : Museum

{

public override void Accept(Vistitor visitor)

{

this.Dance();

visitor.VisitChinaMuseum(this);

 

}

 

/// <summary>

/// 具体element特有的方法或行为

/// </summary>

public void Dance()

{

Console.WriteLine("56个民族56朵花,跳56个民族舞蹈");

}

public void Paint()

{

Console.WriteLine("表演中国绘画");

}

}

 

public class EnglandMuseum : Museum

{

public override void Accept(Vistitor visitor)

{

this.Play();

visitor.VisitEnglandMuseum(this);

}

 

/// <summary>

/// 具体element特有的方法或行为

/// </summary>

private void Play()

{

Console.WriteLine("演奏风笛");

}

}

 

 

public class GoodVisitor : Vistitor

{

/// <summary>

/// 录像

/// </summary>

private void Video()

{

Console.WriteLine("全程录像");

}

public override void VisitChinaMuseum(ChinaMuseum museum)

{

Console.WriteLine("崇拜地看舞蹈");

museum.Paint();

Console.WriteLine("为精美的绘画鼓掌");

this.Video();//新增的行为或功能

}

 

public override void VisitEnglandMuseum(EnglandMuseum museum)

{

Console.WriteLine("为风笛的演奏喝彩");

this.Video();

}

}

 

public class BadVisitor : Vistitor

{

public override void VisitChinaMuseum(ChinaMuseum museum)

{

Console.WriteLine("乱吆喝,吹口哨");

}

 

public override void VisitEnglandMuseum(EnglandMuseum museum)

{

Console.WriteLine("中途打电话");

}

}

 

public class Expo

{

private List<Museum> museums = new List<Museum>();

public Expo()

{

this.museums.Add(new ChinaMuseum());

this.museums.Add(new EnglandMuseum());

}

 

public void Welcome(Vistitor visitor)

{

foreach (var museum in this.museums)

{

museum.Accept(visitor);

}

}

}

 

 

class Program

{

static void Main(string[] args)

{

Expo expo = new Expo();

 

Vistitor v1 = new GoodVisitor();

Vistitor v2 = new BadVisitor();

expo.Welcome(v1);

 

Console.WriteLine();

expo.Welcome(v2);

 

 

}

}

}

 

26. GRASP模式

 

26.1 GRASP软件开发模式

 

  • Craig Larman在《Applying UML and Patterns》一书中提出了GRASP设计模式的概念。
  • 作者称其为设计模式,其实,更好的理解应该为设计原则
  • GoF设计模式是针对特定问题而提出的解决方法
  • GRASP则是站在面向对象设计的角度,告诉我们怎么样设计问题空间中的类与它们的行为责任,以及明确类之间的相互关系等等。
  • GRASP可以说是GoF等设计模式的基础。
  • GRASP:
    • General Responsibility Assignment Software patterns
    • 通用职责分配软件模式
  • 核心思想:
    • "职责分配(Responsibility Assignment)"
    • 用职责设计对象:"Designing Objects with Responsibilities"
  • 进一步理解核心思想:
    • 自己干自己的事(职责的分配)
    • 自己干自己的能干的事(职责的分配)
    • 自己只干自己的事(职责的内聚)
  • 五个基本模式
    • 1,信息专家(Information expert)
    • 2,创建者(Creator)
    • 3,高内聚(High Cohesion)
    • 4,低耦合(Low coupling)
    • 5,控制器(Controller)
  • 四个扩展模式
    • 6,多态性(Polymorphism)
    • 7,纯虚构(Pure Fabrication)
    • 8,间接性(Indirection)
    • 9,防止变异(Protected Variations)

 

26.2信息专家(Information expert)

 

  • GRASP模式中解决类的职责分配问题的最基本的模式。
  • 问题:
    当我们为系统发现完对象和职责之后,职责的分配原则(职责将分配给哪个对象执行)是什么?
  • 解决方案:
    职责的执行需要某些信息(information),把职责分配给该信息的拥有者。
    换句话说,某项职责的执行需要某些资源,只有拥有这些资源的对象才有资格执行职责。 --"有能者为之"
  • 满足了面向对象设计的封装性的设计,一般情况下都会满足Information Expert模式。因为Information Expert是
    对类的属性(信息),以及对类的属性的操作的封装,它符合对象封装性的概念。
  • 优点:
    • - 信息的拥有者类同时就是信息的操作者类,可以减少不必要的类之间的关联。
    • - 各类的职责单一明确,容易理解

26.3创建者(Creator)

 

  • GRASP模式中解决类的实例的创建职责问题的模式。
  • 问题:
    类的实例的创建职责,应该分配给什么样的类?或者说类的实例应该由谁创建?
  • 解决方案:
    以下条件之一为真的情况,类A的实例的创建职责就分配给类B。
    1,B包含A
    2,B聚集A
    3,B记录A
    4,B频繁使用A
    5,B有A初始化数据
  • 提倡类的实例(对象)创建职责由聚集或包含该对象的对象创建
  • 优点:
    • - 整个结构清晰易懂
    • - 有利于类或组件的重用
    • - 防止职责的分散
    • - 降低耦合性

26.4高内聚(High Cohesion)

 

  • GRASP模式中为降低类的复杂程度,简化控制而提出的面向对象设计的原则性模式。高内聚(High Cohesion)与低耦合(Low Coupling)模式是GRASP其他模式的根本。
  • 问题:
    怎么做才能降低类的复杂程度,简化控制?
  • 解决方案:
    紧密相关的功能(职责)应该分配给同一个类。 --"各司其职"
  • 所谓内聚,是指单个物体(类)内部的功能聚集度。比如,只包含有相互关联的功能的类,具有高内聚性,同时,它的外部表现(作用,意图)也就明显;反之,如果一个类由一些不相关的功能构成,它的内聚性就低,它的外部表现就不明显,一方面很难理解它的作用和意图,另一方面,一旦需求变化,扩展性就差。
  • 优点:
    • - 聚集相关功能,结构清晰,容易理解
    • - 只聚集相关功能,使得类的职责单一明确,从而降低类的复杂程度,使用简单

26.5低耦合(Low coupling)

 

  • GRASP模式中为降低类的复杂程度,简化控制而提出的面向对象设计的原则性模式。高内聚(High Cohesion)与低耦合(Low Coupling)模式是GRASP其他模式的根本。
  • 问题:
    怎么做才能降低类之间关联程度,能适应需求的变化呢?
  • 解决方案:
    为类分配职责时,应该尽量降低类之间的关联关系(耦合性)。亦即,应该以降低类之间的耦合关系作为职责分配的原则。
  • 所谓耦合,是指多个物体(类)之间的物理或者意思上的关联程度。在面向对象方法中,类是最基本的元素,耦合主要指不同类之间相互关联的紧密程度。面向对象里的关联,主要指一个类对另一个类的调用,聚合(包含),参数传递等关系。比如,所谓2个关联得非常紧密的类(高耦合),是指其中一个类发生变化(修改)时,另一个类也不得不跟着发生变化(修改)。
  • 面向对象设计要求类之间满足"低耦合"原则,它是衡量一个设计是否优良的的一个重要标准,因为"低耦合"有助于使得系统中某一部分的变化对其它部分的影响降到最低程度。
  • 优点:
    • 1,一方面,高内聚要求把紧密关联的功能(职责)聚集在同一个类中,防止功能的扩散和类的无谓增加,从而减少类之间的关联,降低类之间的发生耦合的机率。
    • 2,另一方面,高内聚要求把不相关的功能分散到不同的类,类增加了,势必造成相互关联类的增加,从而增大类之间发生耦合的机率。

26.6控制器(Controller)

 

  • GRASP模式中解决事件处理职责问题的模式。
  • 问题:
    在UI层之外,应该由哪个类来处理(控制)系统操作(事件)呢?或者说,当触发一个系统事件时,应该把对事件的处理职责分配给UI层之外的哪个类呢?
  • 解决方案:
    把系统事件的处理职责分配给Controller(控制器)类。
  • Controller模式提倡用一个专门的类来处理所有的系统事件。或者说Controller模式把所有系统事件的处理职责分配给一个专门的类集中处理。
  • 优点:
    • - 防止同类职责的分散。满足高内聚,低耦合原则。
    • - 有利于共通处理(前处理,后处理等)。
    • - 变化的高适应能力。能够把变化的修改范围控制在最小范围(控制器)之内。

26.7多态性(Polymorphism)

 

  • 问题:
    如何处理基于类型的选择?如何创建可插拔的软件构件?
  • 解决方案:
    当相关选择或行为随类型(类)有所不同时,使用多态操作为变化的行为类型分配职责。

26.8纯虚构(Pure Fabrication)

 

  • 问题:
    当你并不想违背高内聚和低耦合或其它目标,但是基于专家模式所提供的方案又不合适时,哪些对象应该承担这一职责?(很多情况下,只对领域对象分配职责会导致不良内聚或耦合,或者降低复用潜力)
  • 解决方案:
    对人为制造的类分配一组高内聚的职责,该类并不代表问题领域的概念——虚构的事物,用以支持高内聚,低耦合和复用。
  • 所有GOF设计模式(或其它模式)都是纯虚构。

26.9间接性(Indirection)

 

  • 问题:
    为了避免两个或多个事物之间的直接耦合,应该如何分配职责?如何使对象解耦合,以支持低耦合并提供复用性潜力?
  • 解决方案:
    将职责分配给中介对象,避免它们之间的直接耦合。中介实现了间接性。
  • 大量GOF模式,如适配器、外观等等都是间接性的体现。

 

26.10防止变异(Protected Variations)

 

  • 问题:
    如何设计对象、子系统和系统,使其内部的变化或不稳定性不会对其它元素产生不良影响?
  • 解决方案:
    识别预计变化或不稳定之处,分配职责用以在这些变化之外创建稳定接口。
  • 几乎所有的软件或架构设计技巧,都是防止变异的特例,比如封装、多态、接口、虚拟机、配置文件等

26.11防止变异的核心机制

 

  • 1、数据驱动设计(Data-Driven Design):
    通过外置、读取并判断变化因素,防止数据、元数据或说明性变量等对系统产生影响。
  • 2、服务查询(Service Lookup):
    Data-Driven Design的特例,如JNDI,UDDI,通过使用查询服务的稳定接口,客户能够避免服务位置变化的影响。
  • 3、解释器驱动的设计(Interpreter-Driven Design):
    系统通过外置、读取、解释逻辑而避免了逻辑变化的影响。
  • 4、反射或元级的设计(Reflective or Meta-Level Design)
    可避免逻辑或外部代码变化的影响
  • 5、统一访问(Uniform Access):
    如C#中的XXX.Name
  • 6、标准语言(Standard Language)
  • 7、Liskov替换原则(LSP):
    在对T有任何替换实现或子类(成为S)情况下,引用类型T(某接口或抽象超类)的软件(方法,类……)应该正常或按照预期工作。
  • 8、得墨忒耳定律(Law of Demeter):
    不要经历远距离的对象结构路径去向远距离的间接对象发送消息。
  • 9、开放-封闭原则(OCP):
    模块应该同时(对扩展、可适应性)开放和(对影响客户的更改)封闭。

26.12 GRASP软件开发模式-总结

 

  • GRASP和GoF都是称为软件开发模式,只是描述的内容和角度不同
  • GRASP和GoF是不同类型的模式, 出发点不同。 GRASP是解决类之间如何交互, 如何设计合理, 和具体问题无关。
  • GoF往往是解决一些具体的问题,比如类的具体创建方式等等,而GRASP是解决对象分析的一些基本原则,即你如何去设计你的问题空间中的类和它们的行为,是原则性的东西。
  • GRASP适用于对象分析和设计中,即在RUP的制作分析模型和设计模型阶段,GoF更适用于在实际编码过程中作为更加具体的指导思想

 

 

张波老师课程学习笔记整理

posted @ 2016-06-02 17:19  常想一二,不思八九  阅读(3485)  评论(0)    收藏  举报