0%

在现实生活中,常常会出现这样的事例:一个请求有多个对象可以处理,但每个对象的处理条件或权限不同,如找领导出差报销等。
在计算机软硬件中也有相关例子,如总线网中数据报传送、异常处理。所有这些,如果用责任链模式都能很好解决。

责任链(Chain of Responsibility)模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链。当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

注意:责任链模式也叫职责链模式

在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,所以责任链将请求的发送者和请求的处理者解耦了。

责任链模式是一种对象行为型模式,其主要优点如下:

  • 降低了对象之间的耦合度:该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
  • 增强了系统的可扩展性:可以根据需要增加新的请求处理类,满足开闭原则。
  • 增强了给对象指派职责的灵活性:当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
  • 责任链简化了对象之间的连接:每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
  • 责任分担:每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

其主要缺点如下:

  • 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
  • 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
  • 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。

通常情况下,可以通过数据链表来实现职责链模式的数据结构。

模式的结构#

职责链模式主要包含以下角色:

  • 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
  • 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
  • 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

其结构图如图所示:

客户端可按图所示设置责任链:

模式的实现#

职责链模式的实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
class Program
{
static void Main(string[] args)
{

Handler handler1=new ConcreteHandler1();
Handler handler2=new ConcreteHandler2();
handler1.SetNext(handler2);

handler1.HandleRequest("two");
Console.Read();
}
}


public abstract class Handler
{
private Handler next;
public void SetNext(Handler next)
{
this.next=next;
}
public Handler GetNext()
{
return next;
}

public abstract void HandleRequest(String request);
}


public class ConcreteHandler1 : Handler
{
public override void HandleRequest(String request)
{
if(request.Equals("one"))
{
Console.WriteLine("具体处理者1负责处理该请求!");
}
else
{
if(GetNext()!=null)
{
GetNext().HandleRequest(request);
}
else
{
Console.WriteLine("没有人处理该请求!");
}
}
}
}


public class ConcreteHandler2 : Handler
{
public override void HandleRequest(String request)
{
if(request.Equals("two"))
{
Console.WriteLine("具体处理者2负责处理该请求!");
}
else
{
if(GetNext()!=null)
{
GetNext().HandleRequest(request);
}
else
{
Console.WriteLine("没有人处理该请求!");
}
}
}
}

程序运行结果如下:

1
具体处理者2负责处理该请求!

前边已经讲述了关于责任链模式的结构与特点,下面介绍其应用场景,责任链模式通常在以下几种情况使用:

  • 有多个对象可以处理一个请求,哪个对象处理该请求由运行时刻自动确定。
  • 可动态指定一组对象处理请求,或添加新的处理者。
  • 在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。

职责链模式存在以下两种情况:

  • 纯的职责链模式:一个请求必须被某一个处理者对象所接收,且一个具体处理者对某个请求的处理只能采用以下两种行为之一:自己处理(承担责任);把责任推给下家处理。
  • 不纯的职责链模式:允许出现某一个具体处理者对象在承担了请求的一部分责任后又将剩余的责任传给下家的情况,且一个请求可以最终不被任何接收端对象所接收。

在现实生活以及程序设计中,经常要访问一个聚合对象中的各个元素,如“数据结构”中的链表遍历,通常的做法是将链表的创建和遍历都放在同一个类中,但这种方式不利于程序的扩展,如果要更换遍历方法就必须修改程序源代码,这违背了 “开闭原则”。

既然将遍历方法封装在聚合类中不可取,那么聚合类中不提供遍历方法,将遍历方法由用户自己实现是否可行呢?答案是同样不可取,因为这种方式会存在两个缺点:

  • 暴露了聚合类的内部表示,使其数据不安全。
  • 增加了客户的负担。

“迭代器模式”能较好地克服以上缺点,它在客户访问类与聚合类之间插入一个迭代器,这分离了聚合对象与其遍历行为,对客户也隐藏了其内部细节,且满足“单一职责原则”和“开闭原则”。

迭代器(Iterator)模式的定义:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
迭代器模式是一种对象行为型模式,其主要优点如下:

  • 访问一个聚合对象的内容而无须暴露它的内部表示。
  • 遍历任务交由迭代器完成,这简化了聚合类。
  • 它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。
  • 增加新的聚合类和迭代器类都很方便,无须修改原有代码。
  • 封装性良好,为遍历不同的聚合结构提供一个统一的接口。

其主要缺点是:增加了类的个数,这在一定程度上增加了系统的复杂性。

迭代器模式是通过将聚合对象的遍历行为分离出来,抽象成迭代器类来实现的,其目的是在不暴露聚合对象的内部结构的情况下,让外部代码透明地访问聚合的内部数据。

模式的结构#

迭代器模式主要包含以下角色:

  • 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合对象以及创建迭代器对象的接口。
  • 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
  • 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 HasNext()、First()、Next() 等方法。
  • 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。

其结构图如图所示:

模式的实现#

迭代器模式的实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
class Program
{
static void Main(string[] args)
{
IAggregate ag = new ConcreteAggregate();
ag.Add("中山大学");
ag.Add("华南理工");
ag.Add("韶关学院");
Console.WriteLine("聚合的内容有:");
IIterator it = ag.GetIterator();
while (it.HasNext())
{
Object ob = it.Next();
Console.WriteLine(ob.ToString());
}
Console.WriteLine("First:" + it.First().ToString());

Console.Read();
}
}


public interface IAggregate
{
void Add(Object obj);
void Remove(Object obj);
IIterator GetIterator();
}


public class ConcreteAggregate : IAggregate
{
private List<Object> list=new List<Object>();
public void Add(Object obj)
{
list.Add(obj);
}
public void Remove(Object obj)
{
list.Remove(obj);
}
public IIterator GetIterator()
{
return(new ConcreteIterator(list));
}
}


public interface IIterator
{
Object First();
Object Next();
bool HasNext();
}


public class ConcreteIterator : IIterator
{
private List<Object> list=null;
private int index=-1;
public ConcreteIterator(List<Object> list)
{
this.list=list;
}
public bool HasNext()
{
if(index<list.Count-1)
{
return true;
}
else
{
return false;
}
}
public Object First()
{
index=0;
Object obj=list[index];;
return obj;
}
public Object Next()
{
Object obj=null;
if(this.HasNext())
{
obj=list[++index];
}
return obj;
}
}

程序运行结果如下:

1
2
3
4
5
聚合的内容有:
中山大学
华南理工
韶关学院
First:中山大学

注:在C#中,推荐使用泛型代替Object以降低性能消耗。

前面介绍了关于迭代器模式的结构与特点,下面介绍其应用场景,迭代器模式通常在以下几种情况使用:

  • 当需要为聚合对象提供多种遍历方式时。
  • 当需要为遍历不同的聚合结构提供一个统一的接口时。
  • 当访问一个聚合对象的内容而无须暴露其内部细节的表示时。

由于聚合与迭代器的关系非常密切,所以大多数语言在实现聚合类时都提供了迭代器类,因此大数情况下使用语言中已有的聚合类的迭代器就已经够了。

迭代器模式常常与组合模式结合起来使用,在对组合模式中的容器构件进行访问时,经常将迭代器潜藏在组合模式的容器构成类中。当然,也可以构造一个外部迭代器来对容器构件进行访问,其结构图如图所示:

《大话设计模式》中提到了 24种设计模式:

简单工厂模式,策略模式、装饰模式、代理模式、工厂方法模式、原型模式、模板方法模式、外观模式、建造者模式、观察者模式、抽象工厂模式、状态模式、适配器模式、备忘录模式、组合模式、迭代器模式、单例模式、桥接模式、命令模式、职责链模式、中介者模式、享元模式、解释器模式、访问者模式。

按照类型,可分为3类:

1、 创建型模式:抽象工厂、建造者模式、工厂方法、原型模式、单例模式;

创建型模式抽象了实例化的过程。创建性模式隐藏了这些类的实例是如何被创建和放在一起,整个系统关于这些对象所知道的是由抽象类所定义的接口。这样,创建性模式在创建了什么、谁创建它、她是怎么被创建的、以及何时创建方面提供了灵活性。创建相应数目的原型并克隆她们通常比每次用适合的状态手工实例化该类更方便。

2、 结构型模式:适配器模式、桥接模式、组合模式、装饰者模式、外观模式、享元模式、代理模式;

3、 行为型模式:观察者模式、模板方法、命令模式、状态模式、职责链模式、解释器模式、中介者模式、访问者模式、策略模式、备忘录模式、迭代器模式。

4、 MVC模式:集观察者、组合、策略为一体,是多种模式的综合应用,算是一种架构模式。

下面按照【概念】+【原则】+【场景】+【优点】+【缺点】+【应用】分别简述一下24种设计模式:

抽象工厂模式(Abstract Factory) 提供一个创建一系列相关或互相依赖对象的接口,而无需指定它们具体的类。

原则:LSP 里氏替换原则

场景:创建不同的产品对象,客户端应使用不同的具体工厂。

优点

a)   改变具体工厂即可使用不同的产品配置,使改变一个应用的具体工厂变得很容易。

b)   让具体的创建实例过程与客户端分离,客户端通过抽象接口操作实例,产品的具体类名也被具体工厂的实现分离。

缺点:如果要新增方法,改动极大。

应用

      a)jdk中连接数据库的代码是典型的抽象工厂模式,每一种数据库只需提供一个统一的接口:Driver(工厂类),并实现其中的方法即可。不管是jdbc还是odbc都能够通过扩展产品线来达到连接自身数据库的方法。

      b)java.util.Collection 接口中定义了一个抽象的 iterator() 方法,该方法就是一个工厂方法。对于 iterator() 方法来说 Collection 就是一个抽象工厂

建造者模式(Builder) 【又名,生成器模式】:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

原则:依赖倒转原则

场景:如果需要将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时适用的模式。

优点:使得建造代码与表示代码分离。

缺点:1、增加代码量;2、Builder只是一个替代构造器的选择,不能直接用于降低非构造函数方法的参数数量。

应用:StringBuilder和StringBuffer的append()方法

工厂方法模式(Factory Method) 定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。

原则:开放封闭原则

场景:不改变工厂和产品体系,只是要扩展产品(变化)。

优点:是简单工厂模式的进一步抽象和推广,既保持了简单工厂模式的优点(工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类。对于客户端来说,去除了与具体产品的依赖),而且克服了简单工厂的缺点(违背了开放封闭原则)。

缺点:每增加一个产品,就需要增加一个产品工厂的类,增加了额外的开发。(用反射可以解决)。

应用

1.       Collection中的iterator方法;

2.       java.lang.Proxy#newProxyInstance()

3.       java.lang.Object#toString()

4.       java.lang.Class#newInstance()

5.       java.lang.reflect.Array#newInstance()

6.       java.lang.reflect.Constructor#newInstance()

7.       java.lang.Boolean#valueOf(String)

8.       java.lang.Class#forName()

原型模式(prototype) 【又名,生成器模式】:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

原则

场景:在初始化信息不发生变化的情况,用克隆进行拷贝。

优点:隐藏了对象创建的细节,大大提升了性能。不用重新初始化对象,而是动态的获得对象运行时的状态。

缺点:深复制 or 浅复制 。

应用:JDK中的Date类。

单例模式(Singleton) 保证一个类仅有一个实例,并提供一个访问它的全局访问点。

原则:封装

场景:通常,我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象,一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,而且它可以提供一个访问该实例的方法。

优点:对唯一实例的受控访问。

缺点:饿汉式/懒汉式  多线程同时访问时可能造成多个实例。

应用:java.lang.Runtime; GUI中也有一些(java.awt.Toolkit#getDefaultToolkit() java.awt.Desktop#getDesktop())

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

在GoF的设计模式中,适配器有两种类型,类适配器模式和对象适配器模式。

a)   类适配器模式:通过多重继承对一个接口与另一个接口进行匹配,而C#,Java等语言都不支持多重继承,也就是一个类只有一个父类。

b)   Java一般都指的是 对象适配器模式

场景:适配器是为了复用一些现有的类。系统的数据和行为都正确,但是接口不符,这时采用适配器模式,使原有对象和新接口匹配。

优点:能够复用现存的类,客户端统一调用同一接口,更简单、直接、紧凑。

缺点:适配器模式有点儿“亡羊补牢”的感觉,设计阶段要避免使用。

应用:在Java jdk中,适配器模式使用场景很多,如集合包中Java.util.Arrays#asList()、IO包中java.io.InputStreamReader(InputStream)、java.io.OutputStreamWriter(OutputStream) 等

桥接模式(Bridge) 将抽象部分与它的实现部分分离,使它们都可以独立的变化。

原则:合成/聚合复用原则

场景:实现系统可能有多角度分类,每一种分类都有可能变化,那么就把这种多角度分离出来让它们独立变化,减少它们之间的耦合。

优点:减少各部分的耦合。 分离抽象和实现部分,更好的扩展性,可动态地切换实现、可减少子类的个数。

缺点:1、桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。 2、桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性

应用:Collections类中的sort()方法;AWT;JDBC数据库访问接口API;

组合模式(Composite) 将对象组合成树形结构以表示“部分-整体”的层次结构。

场景:需求中体现部分与整体层次结构时,以及希望用户可以忽略组合对象与单个对象的不同,统一使用组合结构中的所有对象时,就应该考虑使用组合模式了。

优点:组合模式让客户可以一致的使用组合结构和单个对象。

缺点:使设计变得更加抽象,对象的业务规则如果很复杂,则实现组合模式具有很大挑战性,而且不是所有的方法都与叶子对象子类都有关联。

应用:JDK中AWT包和Swing包的设计是基于组合模式,在这些界面包中为用户提供了大量的容器构件(如Container)和成员构件(如Checkbox、Button和TextComponent等),他们都是继承、关联自抽象组件类Component。

装饰模式(Decorator) 动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更灵活。

场景:装饰模式是为了已有功能动态地添加更多功能的一种方式,当系统需要新功能的时候,是向旧类中添加新的代码,这些新的代码通常装饰了原有类的核心职责或主要行为。装饰着模式把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,当需要执行特殊行为时,客户代码就可以在运行时根据需要有选择的、按顺序地使用装饰功能包装对象。

优点:把类中的装饰功能从类中搬移出去,简化原有的类。有效的把类的核心职责和装饰功能区分开,去除相关类中重复的装饰逻辑。

缺点:利用装饰器模式,常常造成设计中有大量的小类,数量实在太多,可能会造成使用此API程序员的困扰。

应用:Java I/O使用装饰模式设计,JDK中还有很多类是使用装饰模式设计的,如:Reader类、Writer类、OutputStream类等。

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

原则:完美的体现了依赖倒转原则和迪米特法则。

场景

a)   设计阶段:需有意识的将不同的两个层分离。

b)   开发阶段:增加外观façade提供一个简单的接口,应对子类的重演和演化。

c)   维护期间:使用façade类,为遗留代码提供清晰简单的接口,让新系统与façade交互,façade与遗留代码交互所有复杂的工作。

优点:1、客户对子系统的使用变得简单了,减少了与子系统的关联对象,实现了子系统与客户之间的松耦合关系。 2、只是提供了一个访问子系统的统一入口,并不影响用户直接使用子系统类 3、降低了大型软件系统中的编译依赖性,并简化了系统在不同平台之间的移植过程。

缺点:1、不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性   2、在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。

享元模式(Flyweight) 运用共享技术有效的支持大量细粒度的对象。

场景:如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大存储开销时就应该考虑使用享元模式;还有就是对象大多数状态都可为外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑使用享元模式。

优点:享元模式可以避免大量非常相似类的开销。程序中,大量细粒度的类实例来表示数据,如果它们除了几个参数外基本相同,那么把它们转移到类实例的外面,在方法调用时将它们传递进来,就可以通过共享大幅度减少单个实例的数目。

缺点:1、由于享元模式需要区分外部状态和内部状态,使得应用程序在某种程度上来说更加复杂化了。2、为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长。

应用:String 类。

代理模式(proxy) 为其他对象提供一种代理以控制对这个对象的访问。

原则:代理模式就是在访问对象时引入一定程度的间接性。(迪米特法则?)

场景

a)   远程代理:为一个对象在不同的地址空间提供局部代表,这样可以隐藏一个对象存在于不同地址空间的事实。【WebService,客户端可以调用代理解决远程访问问题】

b)   虚拟代理:根据需要创建开销很大的对象,通过它来存放实例化需要很长时间地真实对象。【比如Html网页的图片,代理存储的是真实图片的路径和尺寸】

c)   安全代理:用来控制真实对象的访问权限。

d)   智能指引:当调用真实的对象时,代理处理另一些事。【如计算机真实对象的引用次数,代理在访问一个对象的时候回附加一些内务处理,检查对象是否被锁定、是否该释放、是否该装入内存等等】

优点:1)代理模式能将代理对象与真正被调用的对象分离,在一定程度上降低了系统的耦合度。2)代理模式在客户端和目标对象之间起到一个中介作用,这样可以起到保护目标对象的作用。代理对象也可以对目标对象调用之前进行其他操作。

缺点:1)在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。2)增加了系统的复杂度。

应用:java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。

观察者模式(Publish/Subscribe) 【又名 发布-订阅模式】:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,让它们能够自动更新自己。

场景:将一个系统分割成一系列互相协作的类,有一个缺点:需要维护相关对象间的一致性。紧密的耦合会给维护和扩展带来不便。观察者模式就是为了解耦而诞生的,让原本一个对象依赖另一个对象的关系,变成了两方都依赖于抽象,而不再依赖于具体,从而使得各自的变化都不会影响另一边的变化。

优点:解耦。

缺点:如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察者模式是要特别注意这一点。

应用:java.util.Observer ,  java类库实现观察着(Observer)模式的类和接口。

模板方法模式(Template Method) 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。

原则:代码复用平台。

场景:遇到由一系列步骤构成的过程需要执行,这个过程从高层次上看是相同的,但是有些步骤的实现可能不同,这个时候就需要考虑用模板方法模式了。

优点:模板方法模式是通过把不变行为搬移到超类,去除子类中重复代码来实现它的优势,提供了一个代码复用平台,帮助子类摆脱重复的不变行为的纠缠。

缺点:如果父类中可变的基本方法太多,将会导致类的个数增加,系统更加庞大。

应用:AbstractClass抽象类里面的TemplateMethod()就是模板方法。

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

原则:敏捷开发原则

场景:对请求排队或记录请求日志,以及支持可撤销的操作等行为。

优点

a)      命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分割开。

b)      它能较容易的设计一个命令队列。

c)       在需要的情况下,可以较容易的将命令记入日志。

d)      允许接收请求的一方决定是否要否决请求。

e)      可以容易的实现对请求的撤销和重做。

f)        由于加进新的具体命令类不影响其他类,因此增加新的具体命令类很容易。

缺点:会增加系统的复杂性,这里的复杂性应该主要指的是类的数量。

应用

1.       java.util.Timer类中scheduleXXX()方法

2.       java Concurrency Executor execute()方法

3.       java.lang.reflect.Methodinvoke()方法

状态模式(state) 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类。

原则:单一职责原则

场景:当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,可以考虑使用状态模式了。

优点:状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。【消除庞大的条件分支语句】。

缺点:违背开放-封闭原则

应用

1.       java.util.Iterator

2.       javax.faces.lifecycle.LifeCycle#execute()

职责链模式(chain of responsibility) 使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

场景:当客户提交一个请求时,请求是沿链传递直至有一个对象负责处理它。

优点:使得接收者和发送者都没有对方的明确信息,且链中对象自己也不知道链结构,结果是职责链可以简化对象的相互连接,它们只需要保持一个指向其后继者的引用,而不需要保持它所有的候选接收者的引用。开发者可以随时的增加或者修改处理一个请求的结构,增强了给对象指派职责的灵活性

缺点:一个请求极有可能到了链的末端都得不到处理,或者因为没有正确配置而得不到处理。

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

原则:依赖倒转原则

场景:如果一种特定类型问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语句中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。当一个语言需要执行,并且你可将该语言中的句子表示为一个抽象语法树时,可以用解释器模式。

优点:解释器很容易改变和扩展文法,因为该模式使用类来表示文法规则,可以使用继承来改变或扩展文法,也比较容易实现文法。因为定义抽象语法树中各个节点的类的实现大体类似,这些类都易于直接编写。

缺点:解释器模式为文法中的每一条规则至少定义了一个类,因此包含许多规则的文法可能难以管理和维护,建议当文法非常复杂时,使用其他技术(语法分析程序、编译器生成器)。

应用

1.       java.util.Pattern

2.       java.text.Normalizer

3.       java.text.Format

4.       javax.el.ELResolver

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

场景:一般应用于一组对象以定义良好但是复杂的方式进行通信的场合,以及想定制一个分布在多个类的行为,而又不想生成太多子类的场合。【例如,Form窗体,或者aspx页面】。

优点

a)   抽象中介者类(Mediator)减少了抽象同事类(colleague)之间的耦合,是的可以独立的改变和复用各个类。

b)   由于把对象如何协作进行了抽象,将中介作为一个独立的概念并将其封装在一个对象中,这样关注的对象就从对象各自本身的行为转移到它们之间的交互上来,也就是站在一个更宏观的角度去看待系统。

缺点:控制集中化导致了中介者的复杂化。

应用

1.       java.util.Timer

2.       java.util.concurrent.Executor#execute()

3.       java.util.concurrent.ExecutorService#submit()

4.       java.lang.reflect.Method#invoke()

访问者模式 (Vistor) 生成器模式】:(GoF中最复杂的一个模式)表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

场景:访问者模式适合有稳定的数据结构、又有易于变化的算法】访问者模式适用于数据结构相对稳定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,是的操作集合可以相对自由的演化。访问者模式的目的是要把处理从数据结构中分离出来。

优点:增加新的操作很容易。新的操作就是新的访问者。

缺点:很难增加新的数据结构。

应用

1.       javax.lang.model.element.AnnotationValue和AnnotationValueVisitor

2.       javax.lang.model.element.Element和ElementVisitor

3.       javax.lang.model.type.TypeMirror和TypeVisitor

策略模式(strategy) 它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化不会影响到使用算法的用户。

场景:策略模式不仅可以用来封装算法,几乎可以封装缝合类型的规则,不同的业务逻辑都可以考虑用策略模式处理变化。

优点:策略模式的策略类为上下文定义了一系列可供重用的算法或行为,继承有助于析取出这些算法中的公共功能。另外,策略模式简化了单元测试,因为每一个算法都有自己的类,可以通过自己的接口单独测试。当不同的行为堆砌在一个类中,很难避免使用switch语句。但是将这些行为封装在一个一个独立的策略类中,可以在使用这些行为的类中消除条件语句

缺点:基本的策略模式,选择权在客户端,具体实现转给策略模式的上下文对象。这并不好。使用策略模式和工厂类结合,可以减轻客户端的职责。但是还是不够完美,使用反射才能真正快乐。

应用

1.       java.util.Comparator#compare()

2.       javax.servlet.http.HttpServlet

3.       javax.servlet.Filter#doFilter()

备忘录模式(Memento) 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态。

场景:Memento封装要保存的细节,适合功能负责但需要维护或记录属性历史的类,或者是需要保存的属性只是众多属性中的一个小部分。

优点:使用备忘录模式可以把复杂的发起人内部信息对其他的对象屏蔽起来,从而可以恰当地保持封装的边界。

缺点:如果发起人角色的状态需要完整地存储到备忘录对象中,那么在资源消耗上面备忘录对象会很昂贵。

应用

1.       java.util.Date

2.       java.io.Serializable

迭代器模式(Iterator) 提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。

场景:当需要对聚集有多种方式遍历时,可以考虑使用迭代器。

优点迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器来负责,这样既可以做到不暴露集合的内部结构,又可以让外部代码透明的访问集合内部的数据。

缺点:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。

应用:collection容器使用了迭代器模式

设计模式在JDK的应用:http://blog.csdn.net/u013782203/article/details/52214393

一、引言

     C#版本的23种设计模式已经写完了,现在也到了一个该总结的时候了。说起设计模式,我的话就比较多了。刚开始写代码的时候,有需求就写代码来解决需求,如果有新的需求,或者需求变了,我就想当然的修改自己的代码来满足新的需求,这样做感觉是理所当然的,也没感觉有什么不妥的地方。写了两年多代码,偶尔一次,听说了设计模式,据听说设计模式就是软件界的“独孤九剑”,学会之后就可以天下无敌,于是我就开始了我的进修之路。

       想当初,我就像很多的初学编程的人一样,在面试官面前很容易说出面向对象语言的三大特征:继承,多态和封装,其实,我根本不理解这三个词语的意思,或者说理解的很浅薄。当然也有很多概念是不清楚的,比如:OOPL(面向对象语言)是不是就是OO的全部,面向对象的设计模式和设计模式的到底有什么区别,等等相关问题。自从自己学了设计模式,很多概念变得清晰明了,做事有原则了,或者说有准则了,不像以前只是解决了问题就好,根本不管代码写的是否合理。写的代码可以很优美了,结构更合理了,自己开始重新思考代码这个东西。

     学习设计模式并不是一帆风顺的,尤其是刚开始的时候,由于自己很多概念不是很清楚,也走了很多弯路,但是通过自己的坚持,通过自己不断的看,不断的写,对模式的理解也越来越准确了。写代码不是一个很简单的事情,做任何事都是有原则的,写代码也一样,如果自己心中对做的事情没有准则,那就和无头的苍蝇一样,做与不做是一样的。写代码和写好代码是不一样的,如果要写好的代码,考虑的问题更多了,考虑稳定性,扩展性和耦合性,当然也要考虑上游和下游关联的问题,让你做事的时候知道怎么做,也知道为什么这么做,这就是学习设计模式带来的好处。

     好了,说了也不少了,我把我写的模式都罗列出来,每个模式都有链接,可以直接点击进入查看和阅读,希望对大家阅读有益。

系列导航:

创建型:

    C#设计模式(1)——单例模式(Singleton Pattern)

    C#设计模式(2)——工厂方法模式(Factory Pattern)

    C#设计模式(3)——抽象工厂模式(Abstract Pattern)

    C#设计模式(4)——建造模式(Builder Pattern)

    C#设计模式(5)——原型模式(Prototype Pattern)

结构型:

    C#设计模式(6)——适配器模式(Adapter Pattern)

    C#设计模式(7)——桥接模式(Bridge Pattern)

    C#设计模式(8)——装饰模式(Decorator Pattern)

    C#设计模式(9)——组合模式(Composite Pattern)

    C#设计模式(10)——外观模式(Facade Pattern)

    C#设计模式(11)——享元模式(Flyweight Pattern)

    C#设计模式(12)——代理模式(Proxy Pattern)

行为型:

    C#设计模式(13)——模板方法模式(Template Method)

    C#设计模式(14)——命令模式(Command Pattern)

    C#设计模式(15)——迭代器模式(Iterator Pattern)

    C#设计模式(16)——观察者模式(Observer Pattern)

    C#设计模式(17)——中介者模式(Mediator Pattern)

    C#设计模式(18)——状态模式(State Pattern)

    C#设计模式(19)——策略模式(Stragety Pattern)

    C#设计模式(20)——责任链模式(Chain of Responsibility Pattern)

    C#设计模式(21)——访问者模式(Vistor Pattern)

    C#设计模式(22)——备忘录模式(Memento Pattern)

    C#设计模式(23)——解释器模式(Interpreter Pattern)

二、 面向对象的设计原则

       写代码也是有原则的,我们之所以使用设计模式,主要是为了适应变化,提高代码复用率,使软件更具有可维护性和可扩展性。如果我们能更好的理解这些设计原则,对我们理解面向对象的设计模式也是有帮助的,因为这些模式的产生是基于这些原则的。这些规则是:单一职责原则(SRP)、开放封闭原则(OCP)、里氏代替原则(LSP)、依赖倒置原则(DIP)、接口隔离原则(ISP)、合成复用原则(CRP)和迪米特原则(LoD)。下面我们就分别介绍这几种设计原则。

   2.1、单一职责原则(SRP):

  (1)、SRP(Single Responsibilities Principle)的定义:就一个类而言,应该仅有一个引起它变化的原因。简而言之,就是功能要单一。

  (2)、如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其它职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。(敏捷软件开发)

  (3)、软件设计真正要做的许多内容,就是发现职责并把那些职责相互分离。

     小结:单一职责原则(SRP)可以看做是低耦合、高内聚在面向对象原则上的引申,将职责定义为引起变化的原因,以提高内聚性来减少引起变化的原因。责任过多,引起它变化的原因就越多,这样就会导致职责依赖,大大损伤其内聚性和耦合度。

   2.2、开放关闭原则(OCP)

  (1)、OCP(Open-Close Principle)的定义:就是说软件实体(类,方法等等)应该可以扩展(扩展可以理解为增加),但是不能在原来的方法或者类上修改,也可以这样说,对增加代码开放,对修改代码关闭。

  (2)、OCP的两个特征: 对于扩展(增加)是开放的,因为它不影响原来的,这是新增加的。对于修改是封闭的,如果总是修改,逻辑会越来越复杂。

     小结:开放封闭原则(OCP)是面向对象设计的核心思想。遵循这个原则可以为我们面向对象的设计带来巨大的好处:可维护(维护成本小,做管理简单,影响最小)、可扩展(有新需求,增加就好)、可复用(不耦合,可以使用以前代码)、灵活性好(维护方便、简单)。开发人员应该仅对程序中出现频繁变化的那些部分做出抽象,但是不能过激,对应用程序中的每个部分都刻意地进行抽象同样也不是一个好主意。拒绝不成熟的抽象和抽象本身一样重要。

   2.3、里氏代替原则(LSP)

      (1)、LSP(Liskov Substitution Principle)的定义:子类型必须能够替换掉它们的父类型。更直白的说,LSP是实现面向接口编程的基础。

      小结:任何基类可以出现的地方,子类一定可以出现,所以我们可以实现面向接口编程。 LSP是继承复用的基石,只有当子类可以替换掉基类,软件的功能不受到影响时,基类才能真正被复用,而子类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

   2.4、依赖倒置原则(DIP)

     (1)、DIP(Dependence Inversion Principle)的定义:抽象不应该依赖细节,细节应该依赖于抽象。简单说就是,我们要针对接口编程,而不要针对实现编程。

     (2)、高层模块不应该依赖低层模块,两个都应该依赖抽象,因为抽象是稳定的。抽象不应该依赖具体(细节),具体(细节)应该依赖抽象。

      小结:依赖倒置原则其实可以说是面向对象设计的标志,如果在我们编码的时候考虑的是面向接口编程,而不是简单的功能实现,体现了抽象的稳定性,只有这样才符合面向对象的设计。

   2.5 接口隔离原则(ISP)

     (1)、接口隔离原则(Interface Segregation Principle, ISP)指的是使用多个专门的接口比使用单一的总接口要好。也就是说不要让一个单一的接口承担过多的职责,而应把每个职责分离到多个专门的接口中,进行接口分离。过于臃肿的接口是对接口的一种污染。

     (2)、使用多个专门的接口比使用单一的总接口要好。

     (3)、一个类对另外一个类的依赖性应当是建立在最小的接口上的。

     (4)、一个接口代表一个角色,不应当将不同的角色都交给一个接口。没有关系的接口合并在一起,形成一个臃肿的大接口,这是对角色和接口的污染。

     (5)、“不应该强迫客户依赖于它们不用的方法。接口属于客户,不属于它所在的类层次结构。”这个说得很明白了,再通俗点说,不要强迫客户使用它们不用的方法,如果强迫用户使用它们不使用的方法,那么这些客户就会面临由于这些不使用的方法的改变所带来的改变。

       小结:接口隔离原则(ISP)告诉我们,在做接口设计的时候,要尽量设计的接口功能单一,功能单一,使它变化的因素就少,这样就更稳定,其实这体现了高内聚,低耦合的原则,这样做也避免接口的污染。

   2.6 组合复用原则(CRP)

     (1)、组合复用原则(Composite Reuse Principle, CRP)就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分。新对象通过向这些对象的委派达到复用已用功能的目的。简单地说,就是要尽量使用合成/聚合,尽量不要使用继承。

     (2)、要使用好组合复用原则,首先需要区分”Has—A”和“Is—A”的关系。 “Is—A”是指一个类是另一个类的“一种”,是属于的关系,而“Has—A”则不同,它表示某一个角色具有某一项责任。导致错误的使用继承而不是聚合的常见的原因是错误地把“Has—A”当成“Is—A”.例如:鸡是动物,这就是“Is-A”的表现,某人有一个手枪,People类型里面包含一个Gun类型,这就是“Has-A”的表现。

     小结:组合/聚合复用原则可以使系统更加灵活,类与类之间的耦合度降低,一个类的变化对其他类造成的影响相对较少,因此一般首选使用组合/聚合来实现复用;其次才考虑继承,在使用继承时,需要严格遵循里氏替换原则,有效使用继承会有助于对问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,因此需要慎重使用继承复用。

   2.7 迪米特法则(Law of Demeter)

      (1)、迪米特法则(Law of Demeter,LoD)又叫最少知识原则(Least Knowledge Principle,LKP),指的是一个对象应当对其他对象有尽可能少的了解。也就是说,一个模块或对象应尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立,这样当一个模块修改时,影响的模块就会越少,扩展起来更加容易。

      (2)、关于迪米特法则其他的一些表述有:只与你直接的朋友们通信;不要跟“陌生人”说话。

      (3)、外观模式(Facade Pattern)和中介者模式(Mediator Pattern)就使用了迪米特法则。

       小结:迪米特法则的初衷是降低类之间的耦合,实现类型之间的高内聚,低耦合,这样可以解耦。但是凡事都有度,过分的使用迪米特原则,会产生大量这样的中介和传递类,导致系统复杂度变大。所以在采用迪米特法则时要反复权衡,既做到结构清晰,又要高内聚低耦合。

三、创建型模式

    创建型模式就是用来解决对象实例化和使用的客户端耦合的模式,可以让客户端和对象实例化都独立变化,做到相互不影响。创建型模式包括单例模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式。

    3.1、单例模式(Singleton Pattern):解决的是实例化对象的个数的问题,该模式是把对象的数量控制为一个,该模式可以扩展,可以把实例对象扩展为N个对象,N>=2。比如对象池的实现。

       动机(Motivate):在软件系统构建的过程中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性、以及良好的效率。如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?如果指望使用者不使用构造器来重复创建对象,这是不对的。这应该是类设计者的责任,而不是使用者的责任。

       意图(Intent):保证一个类仅有一个实例,并提供一个该实例的全局访问点。

       具体结构图如下所示
      

       示例代码:

复制代码

1 ///


2 /// 单例模式的实现 3 ///

4 public sealed class Singleton 5 {
6 // 定义一个静态变量来保存类的实例
7 private static volatile Singleton uniqueInstance; 8
9 // 定义一个标识确保线程同步
10 private static readonly object locker = new object(); 11
12 // 定义私有构造函数,使外界不能创建该类实例
13 private Singleton() 14 { 15 } 16
17 ///
18 /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点 19 ///

20 ///
21 public static Singleton GetInstance() 22 { 23 // 当第一个线程运行到这里时,此时会对locker对象 “加锁”, 24 // 当第二个线程运行该方法时,首先检测到locker对象为”加锁”状态,该线程就会挂起等待第一个线程解锁 25 // lock语句运行完之后(即线程运行完之后)会对该对象”解锁” 26 // 双重锁定只需要一句判断就可以了
27 if (uniqueInstance == null) 28 { 29 lock (locker) 30 { 31 // 如果类的实例不存在则创建,否则直接返回
32 if (uniqueInstance == null) 33 { 34 uniqueInstance = new Singleton(); 35 } 36 } 37 } 38 return uniqueInstance; 39 } 40 }

复制代码

   

3.2、工厂方法模式(Factory Method Pattern):一种工厂生产一种产品,工厂类和产品类是一一对应的,他们是平行的等级结构,强调的是“单个对象”的变化。

       动机(Motivate):在软件系统的构建过程中,经常面临着“某个对象”的创建工作:由于需求的变化,这个对象(的具体实现)经常面临着剧烈的变化,但是它却拥有比较稳定的接口。如何应对这种变化?如何提供一种“封装机制”来隔离出“这个易变对象”的变化,从而保持系统中“其他依赖对象的对象”不随着需求改变而改变?

       意图(Intent):定义一个创建对象的工厂接口,由其子类决定要实例化的类,将实际创建工作推迟到子类中。

       具体结构图如下所示:

    3.3、抽象工厂模式(Abstract Factory Pattern):该模式关注的是多批多系列相互依赖的产品的变化,比如:SQLConnection,SQLCommand,SqlDataReader,SqlDataAdapter,就是一批相互依赖的对象,他们变化可以产生OledbConnection,OledbCommand,OledbDataReader,OledbDataAdapter

       动机(Motivate):在软件系统中,经常面临着”一系统相互依赖的对象”的创建工作:同时,由于需求的变化,往往存在更多系列对象的创建工作。如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种”封装机制”来避免客户程序和这种”多系列具体对象创建工作”的紧耦合?

       意图(Intent):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

       具体结构图如下所示

           

       代码实例

复制代码

1 ///


2 /// 下面以不同系列房屋的建造为例子演示抽象工厂模式 3 /// 因为每个人的喜好不一样,我喜欢欧式的,我弟弟就喜欢现代的 4 /// 客户端调用 5 ///

6 class Client 7 {
8 static void Main(string[] args)
9 {
10 // 哥哥的欧式风格的房子
11 AbstractFactory europeanFactory= new EuropeanFactory(); 12 europeanFactory.CreateRoof().Create();
13 europeanFactory.CreateFloor().Create();
14 europeanFactory.CreateWindow().Create();
15 europeanFactory.CreateDoor().Create();
16
17
18 //弟弟的现代风格的房子
19 AbstractFactory modernizationFactory = new ModernizationFactory(); 20 modernizationFactory.CreateRoof().Create();
21 modernizationFactory.CreateFloor().Create();
22 modernizationFactory.CreateWindow().Create();
23 modernizationFactory.CreateDoor().Create();
24 Console.Read();
25 }
26 }
27
28 ///
29 /// 抽象工厂类,提供创建不同类型房子的接口 30 ///

31 public abstract class AbstractFactory 32 {
33 // 抽象工厂提供创建一系列产品的接口,这里作为例子,只给出了房顶、地板、窗户和房门创建接口
34 public abstract Roof CreateRoof(); 35 public abstract Floor CreateFloor(); 36 public abstract Window CreateWindow(); 37 public abstract Door CreateDoor(); 38 }
39
40 ///
41 /// 欧式风格房子的工厂,负责创建欧式风格的房子 42 ///

43 public class EuropeanFactory : AbstractFactory 44 {
45 // 制作欧式房顶
46 public override Roof CreateRoof() 47 {
48 return new EuropeanRoof(); 49 }
50
51 // 制作欧式地板
52 public override Floor CreateFloor() 53 {
54 return new EuropeanFloor(); 55 }
56
57 // 制作欧式窗户
58 public override Window CreateWindow() 59 {
60 return new EuropeanWindow(); 61 }
62
63 // 制作欧式房门
64 public override Door CreateDoor() 65 {
66 return new EuropeanDoor(); 67 }
68 }
69
70 ///
71 /// 现在风格房子的工厂,负责创建现代风格的房子 72 ///

73 public class ModernizationFactory : AbstractFactory 74 {
75 // 制作现代房顶
76 public override Roof CreateRoof() 77 {
78 return new ModernizationRoof(); 79 }
80
81 // 制作现代地板
82 public override Floor CreateFloor() 83 {
84 return new ModernizationFloor(); 85 }
86
87 // 制作现代窗户
88 public override Window CreateWindow() 89 {
90 return new ModernizationWindow(); 91 }
92
93 // 制作现代房门
94 public override Door CreateDoor() 95 {
96 return new ModernizationDoor(); 97 }
98 }
99
100 ///
101 /// 房顶抽象类,子类的房顶必须继承该类 102 ///

103 public abstract class Roof 104 { 105 ///
106 /// 创建房顶 107 ///

108 public abstract void Create(); 109 } 110
111 ///
112 /// 地板抽象类,子类的地板必须继承该类 113 ///

114 public abstract class Floor 115 { 116 ///
117 /// 创建地板 118 ///

119 public abstract void Create(); 120 } 121
122 ///
123 /// 窗户抽象类,子类的窗户必须继承该类 124 ///

125 public abstract class Window 126 { 127 ///
128 /// 创建窗户 129 ///

130 public abstract void Create(); 131 } 132
133 ///
134 /// 房门抽象类,子类的房门必须继承该类 135 ///

136 public abstract class Door 137 { 138 ///
139 /// 创建房门 140 ///

141 public abstract void Create(); 142 } 143
144 ///
145 /// 欧式地板类 146 ///

147 public class EuropeanFloor : Floor 148 { 149 public override void Create() 150 { 151 Console.WriteLine(“创建欧式的地板”); 152 } 153 } 154
155
156 ///
157 /// 欧式的房顶 158 ///

159 public class EuropeanRoof : Roof 160 { 161 public override void Create() 162 { 163 Console.WriteLine(“创建欧式的房顶”); 164 } 165 } 166
167
168 ///
169 ///欧式的窗户 170 ///

171 public class EuropeanWindow : Window 172 { 173 public override void Create() 174 { 175 Console.WriteLine(“创建欧式的窗户”); 176 } 177 } 178
179
180 ///
181 /// 欧式的房门 182 ///

183 public class EuropeanDoor : Door 184 { 185 public override void Create() 186 { 187 Console.WriteLine(“创建欧式的房门”); 188 } 189 } 190
191 ///
192 /// 现代的房顶 193 ///

194 public class ModernizationRoof : Roof 195 { 196 public override void Create() 197 { 198 Console.WriteLine(“创建现代的房顶”); 199 } 200 } 201
202 ///
203 /// 现代的地板 204 ///

205 public class ModernizationFloor : Floor 206 { 207 public override void Create() 208 { 209 Console.WriteLine(“创建现代的地板”); 210 } 211 } 212
213 ///
214 /// 现代的窗户 215 ///

216 public class ModernizationWindow : Window 217 { 218 public override void Create() 219 { 220 Console.WriteLine(“创建现代的窗户”); 221 } 222 } 223
224 ///
225 /// 现代的房门 226 ///

227 public class ModernizationDoor : Door 228 { 229 public override void Create() 230 { 231 Console.WriteLine(“创建现代的房门”); 232 } 233 }

复制代码

   

3.4、建造者模式(Builder Pattern):该模式要解决的是由多个子部分对象构成的一个复杂对象的创建的问题,该复杂对象构成算法稳定,各个子部分对象易变化的情况。强调组装过程的稳定。

       动机(Motivate):在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。如何应对这种变化?如何提供一种“封装机制”来隔离出“复杂对象的各个部分”的变化,从而保持系统中的“稳定构建算法”不随着需求改变而改变?

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

                                  将一个产品的表示形式与产品的组装过程分割开来,从而可以使同一个组装过程(这个构建过程是稳定的,也就是算法)生成具体不同的表现的产品对象。

       具体结构图如下所示

                

    3.5、原型模式(Prototype Pattern):通过制定实例类型来复制对象

       动机(Motivate):在软件系统中,经常面临着“某些结构复杂的对象”的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口。如何应对这种变化?如何向“客户程序(使用这些对象的程序)”隔离出“这些易变对象”,从而使得“依赖这些易变对象的客户程序”不随着需求改变而改变?

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

       具体结构图如下所示:

四、结构型模式

    结构型模式主要研究的是类和对象的组合的问题。它包括两种类型,一是类结构型模式:指的是采用继承机制来组合实现功能;二是对象结构型模式:指的是通过组合对象的方式来实现新的功能。该系列模式包括:适配器模式、桥接模式、装饰者模式、组合模式、外观模式、享元模式和代理模式。

    4.1、适配器模式(Adapter Pattern):该模式主要关注的是接口转换的问题,将匹配的接口通过适配对接工作。

       动机(Motivate):在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。如何应对这种“迁移的变化”?如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口?

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

                                  适配器模式意在转换接口,它能够使原本不能再一起工作的两个类一起工作,所以经常用来在类库的复用、代码迁移等方面。 适配器模式包括类适配器模式和对象适配器模式,

       具体结构图如下所示:

       类适配器模式:

       对象适配器模式:

    4.2、桥接模式(Bridge Pattern):该模式注重分离接口与其实现,接口是针对客户的,接口的内在实现是通过“实现层次”来完成的,支持多维度变化。

       动机(Motivate):在很多游戏场景中,会有这样的情况:【装备】本身会有的自己固有的逻辑,比如枪支,会有型号的问题,同时现在很多的游戏又在不同的介质平台上运行和使用,这样就使得游戏的【装备】具有了两个变化的维度——一个变化的维度为“平台的变化”,另一个变化的维度为“型号的变化”。如果我们要写代码实现这款游戏,难道我们针对每种平台都实现一套独立的【装备】吗?复用在哪里?如何应对这种“多维度的变化”?如何利用面向对象技术来使得【装备】可以轻松地沿着“平台”和“型号”两个方向变化,而不引入额外的复杂度?

       意图(Intent):将抽象部分与实现部分分离,使它们都可以独立地变化。

                       比如:就拿游戏装备来说,“手枪”,抽象部分是指手枪的型号,这个型号可以是G50,G55,针对不同平台,这些型号有不同的实现,针对这些不同平台的不同型号的实现,重新抽象,抽象的结构就是“实现的层次”,其实,到此,就形成了两个抽象层次,第一个层次,是枪支的型号的层次,另一个层次就是针对其实现的一个层次,这样就做到了抽象和实现的分离。

       具体结构图如下所示

          

       示例代码:

复制代码

1 namespace 桥接模式的实现 2 {
3 ///


4 /// 该抽象类就是抽象接口的定义,该类型就相当于是Abstraction类型 5 ///

6 public abstract class Database 7 {
8 //通过组合方式引用平台接口,此处就是桥梁,该类型相当于Implementor类型
9 protected PlatformImplementor _implementor; 10
11 //通过构造器注入,初始化平台实现
12 protected Database(PlatformImplementor implementor) 13 { 14 this._implementor = implementor; 15 } 16
17 //创建数据库–该操作相当于Abstraction类型的Operation方法
18 public abstract void Create(); 19 } 20
21 ///
22 /// 该抽象类就是实现接口的定义,该类型就相当于是Implementor类型 23 ///

24 public abstract class PlatformImplementor 25 { 26 //该方法就相当于Implementor类型的OperationImpl方法
27 public abstract void Process(); 28 } 29
30 ///
31 /// SqlServer2000版本的数据库,相当于RefinedAbstraction类型 32 ///

33 public class SqlServer2000 : Database 34 { 35 //构造函数初始化
36 public SqlServer2000(PlatformImplementor implementor) : base(implementor) { } 37
38 public override void Create() 39 { 40 this._implementor.Process(); 41 } 42 } 43
44 ///
45 /// SqlServer2005版本的数据库,相当于RefinedAbstraction类型 46 ///

47 public class SqlServer2005 : Database 48 { 49 //构造函数初始化
50 public SqlServer2005(PlatformImplementor implementor) : base(implementor) { } 51
52 public override void Create() 53 { 54 this._implementor.Process(); 55 } 56 } 57
58 ///
59 /// SqlServer2000版本的数据库针对Unix操作系统具体的实现,相当于ConcreteImplementorA类型 60 ///

61 public class SqlServer2000UnixImplementor : PlatformImplementor 62 { 63 public override void Process() 64 { 65 Console.WriteLine(“SqlServer2000针对Unix的具体实现”); 66 } 67 } 68
69 ///
70 /// SqlServer2005版本的数据库针对Unix操作系统的具体实现,相当于ConcreteImplementorB类型 71 ///

72 public sealed class SqlServer2005UnixImplementor : PlatformImplementor 73 { 74 public override void Process() 75 { 76 Console.WriteLine(“SqlServer2005针对Unix的具体实现”); 77 } 78 } 79
80 public class Program 81 { 82 static void Main() 83 { 84 PlatformImplementor SqlServer2000UnixImp = new SqlServer2000UnixImplementor(); 85 //还可以针对不同平台进行扩展,也就是子类化,这个是独立变化的
86
87 Database SqlServer2000Unix = new SqlServer2000(SqlServer2000UnixImp); 88 //数据库版本也可以进行扩展和升级,也进行独立的变化。 89
90 //以上就是两个维度的变化。 91
92 //就可以针对Unix执行操作了
93 SqlServer2000Unix.Create(); 94 } 95 } 96 }

复制代码

   

4.3、装饰模式(Decorator Pattern):该模式注重稳定接口,让接口不变,在此前提下为对象动态(关键点)的扩展功能。如果通过继承,各个功能点的组合就会形成过多的子类,维护起来就是麻烦。

       动机(Motivate):在房子装修的过程中,各种功能可以相互组合,来增加房子的功用。类似的,如果我们在软件系统中,要给某个类型或者对象增加功能,如果使用“继承”的方案来写代码,就会出现子类暴涨的情况。比如:IMarbleStyle是大理石风格的一个功能,IKeepWarm是保温的一个接口定义,IHouseSecurity是房子安全的一个接口,就三个接口来说,House是我们房子,我们的房子要什么功能就实现什么接口,如果房子要的是复合功能,接口不同的组合就有不同的结果,这样就导致我们子类膨胀严重,如果需要在增加功能,子类会成指数增长。这个问题的根源在于我们“过度地使用了继承来扩展对象的功能”,由于继承为类型引入的静态特质(所谓静态特质,就是说如果想要某种功能,我们必须在编译的时候就要定义这个类,这也是强类型语言的特点。静态,就是指在编译的时候要确定的东西;动态,是指运行时确定的东西),使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀(多继承)。如何使“对象功能的扩展”能够根据需要来动态(即运行时)地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响降为最低?

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

       具体结构图如下所示

              

       示例代码:

复制代码

1 namespace 装饰模式的实现 2 {
3 ///


4 /// 该抽象类就是房子抽象接口的定义,该类型就相当于是Component类型,是饺子馅,需要装饰的,需要包装的 5 ///

6 public abstract class House 7 {
8 //房子的装修方法–该操作相当于Component类型的Operation方法
9 public abstract void Renovation(); 10 } 11
12 ///
13 /// 该抽象类就是装饰接口的定义,该类型就相当于是Decorator类型,如果需要具体的功能,可以子类化该类型 14 ///

15 public abstract class DecorationStrategy:House //关键点之二,体现关系为Is-a,有这这个关系,装饰的类也可以继续装饰了
16 { 17 //通过组合方式引用Decorator类型,该类型实施具体功能的增加 18 //这是关键点之一,包含关系,体现为Has-a
19 protected House _house; 20
21 //通过构造器注入,初始化平台实现
22 protected DecorationStrategy(House house) 23 { 24 this._house=house; 25 } 26
27 //该方法就相当于Decorator类型的Operation方法
28 public override void Renovation() 29 { 30 if(this._house!=null) 31 { 32 this._house.Renovation(); 33 } 34 } 35 } 36
37 ///
38 /// PatrickLiu的房子,我要按我的要求做房子,相当于ConcreteComponent类型,这就是我们具体的饺子馅,我个人比较喜欢韭菜馅 39 ///

40 public sealed class PatrickLiuHouse:House 41 { 42 public override void Renovation() 43 { 44 Console.WriteLine(“装修PatrickLiu的房子”); 45 } 46 } 47
48
49 ///
50 /// 具有安全功能的设备,可以提供监视和报警功能,相当于ConcreteDecoratorA类型 51 ///

52 public sealed class HouseSecurityDecorator:DecorationStrategy 53 { 54 public HouseSecurityDecorator(House house):base(house){} 55
56 public override void Renovation() 57 { 58 base.Renovation(); 59 Console.WriteLine(“增加安全系统”); 60 } 61 } 62
63 ///
64 /// 具有保温接口的材料,提供保温功能,相当于ConcreteDecoratorB类型 65 ///

66 public sealed class KeepWarmDecorator:DecorationStrategy 67 { 68 public KeepWarmDecorator(House house):base(house){} 69
70 public override void Renovation() 71 { 72 base.Renovation(); 73 Console.WriteLine(“增加保温的功能”); 74 } 75 } 76
77 public class Program 78 { 79 static void Main() 80 { 81 //这就是我们的饺子馅,需要装饰的房子
82 House myselfHouse=new PatrickLiuHouse(); 83
84 DecorationStrategy securityHouse=new HouseSecurityDecorator(myselfHouse); 85 securityHouse.Renovation(); 86 //房子就有了安全系统了 87
88 //如果我既要安全系统又要保暖呢,继续装饰就行
89 DecorationStrategy securityAndWarmHouse=new HouseSecurityDecorator(securityHouse); 90 securityAndWarmHouse.Renovation(); 91 } 92 } 93 }

复制代码

复制代码

1 namespace 命令模式的实现 2 {
3 ///


4 /// 俗话说:“好吃不如饺子,舒服不如倒着”。今天奶奶发话要吃他大孙子和孙媳妇包的饺子。今天还拿吃饺子这件事来说说命令模式的实现吧。 5 ///

6 class Client 7 {
8 static void Main(string[] args)
9 { 10 //奶奶想吃猪肉大葱馅的饺子
11 PatrickLiuAndWife liuAndLai = new PatrickLiuAndWife();//命令接受者
12 Command command = new MakeDumplingsCommand(liuAndLai);//命令
13 PaPaInvoker papa = new PaPaInvoker(command); //命令请求者 14
15 //奶奶发布命令
16 papa.ExecuteCommand(); 17
18
19 Console.Read(); 20 } 21 } 22
23 //这个类型就是请求者角色–也就是我爸爸的角色,告诉奶奶要吃饺子
24 public sealed class PaPaInvoker 25 { 26 //我爸爸从奶奶那里接受到的命令
27 private Command _command; 28
29 //爸爸开始接受具体的命令
30 public PaPaInvoker(Command command) 31 { 32 this._command = command; 33 } 34
35 //爸爸给我们下达命令
36 public void ExecuteCommand() 37 { 38 _command.MakeDumplings(); 39 } 40 } 41
42 //该类型就是抽象命令角色–Commmand,定义了命令的抽象接口,任务是包饺子
43 public abstract class Command 44 { 45 //真正任务的接受者
46 protected PatrickLiuAndWife _worker; 47
48 protected Command(PatrickLiuAndWife worker) 49 { 50 _worker = worker; 51 } 52
53 //该方法就是抽象命令对象Command的Execute方法
54 public abstract void MakeDumplings(); 55 } 56
57 //该类型是具体命令角色–ConcreteCommand,这个命令完成制作“猪肉大葱馅”的饺子
58 public sealed class MakeDumplingsCommand : Command 59 { 60 public MakeDumplingsCommand(PatrickLiuAndWife worker) : base(worker) { } 61
62 //执行命令–包饺子
63 public override void MakeDumplings() 64 { 65 //执行命令—包饺子
66 _worker.Execute(“今天包的是农家猪肉和农家大葱馅的饺子”); 67 } 68 } 69
70 //该类型是具体命令接受角色Receiver,具体包饺子的行为是我们夫妻俩来完成的
71 public sealed class PatrickLiuAndWife 72 { 73 //这个方法相当于Receiver类型的Action方法
74 public void Execute(string job) 75 { 76 Console.WriteLine(job); 77 } 78 } 79 }

复制代码

   

4.4、组合模式(Composite Pattern):该模式着重解决统一接口的问题,将“一对多”的关系转化为“一对一”的关系,让使用叶子节点和树干节点保持接口一致。

       动机(Motivate):客户代码过多地依赖于对象容器(对象容器是对象的容器,细细评味)复杂的内部实现结构,对象容器内部实现结构(而非抽象接口)的变化将引起客户代码的频繁变化,带来了代码的维护性、扩展性等方面的弊端。如何将“客户代码与复杂的对象容器内部结构”解耦?如何让对象容器自己来实现自身的复杂结构,从而使得客户代码就像处理简单对象一样来处理复杂的对象容器?

       意图(Intent):将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。

       具体结构图如下所示:

    4.5、外观模式(Facade Pattern):该模式注重简化接口,简化组件系统与外部客户程序的依赖关系,是Lod(迪米特原则,也叫:最少知识原则)原则的最好的体现,

       动机(Motivate):在软件系统开发的过程中,当组件的客户(即外部接口,或客户程序)和组件中各种复杂的子系统有了过多的耦合,随着外部客户程序和各子系统的演化,这种过多的耦合面临很多变化的挑战。如何简化外部客户程序和系统间的交互接口?如何将外部客户程序的演化和内部子系统的变化之间的依赖相互解耦?

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

       具体结构图如下所示:

    4.6、享元模式(Flyweight Pattern):该模式注重保留接口,在内部使用共享技术对对象存储进行优化

       动机(Motivate):在软件系统中,采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中,从而带来很高的运行时代价——主要指内存需求方面的代价。如何在避免大量细粒度对象问题的同时,让外部客户程序仍然能够透明地使用面向对象的方式来进行操作?

       意图(Intent):运用共享技术有效地支持大量细粒度的对象。

                                 在.NET类库中,String类的实现就使用了享元模式,String类采用字符串驻留池的来使字符串进行共享。

       具体结构图如下所示

           

    4.7、代理模式(Proxy Pattern):该模式注重假借代理接口,控制对真实对象的访问,通过增加间接层来实现灵活控制,比如可以在访问正真对象之前进行权限验证,生活中的明星的代理就是很好的例子,要想接触明星,先要和他的经纪人打交道。

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

       意图(Intent):为其他对象提供一种代理以控制对这个对象的访问。

       具体结构图如下所示:

             

五、行为型模式

    行为型模式主要讨论的是在不同对象之间划分责任和算法的抽象化的问题。行为型模式又分为类的行为模式和对象的行为模式两种。

    类的行为模式——使用继承关系在几个类之间分配行为。

对象的行为模式——使用对象聚合的方式来分配行为。

    行为型模式包括11种模式:模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、状态模式、策略模式、责任链模式、访问者模式、解释器模式和备忘录模式。

    5.1、模板方法模式(Template Method Pattern):该模式注重对算法结构的封装,定义算法骨架,并且稳定,但是支持算法子步骤的变化。

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

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

       具体结构图如下所示

             

    5.2、命令模式(Command Pattern):该模式注重将请求封装为对象,通过将一组行为抽象为对象,实现行为请求者和行为实现者之间的解耦。也可以实现“撤销/重做”的功能,类似“宏”的功能实现也很容易。

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

       意图(Intent):将一个请求封装为一个对象,从而使你可用不同的请求对客户(客户程序,也是行为的请求者)进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。命令模式的实现可以提供命令的撤销和恢复功能。

       具体结构图如下所示

            

       代码实例

复制代码

1 namespace 命令模式的实现 2 {
3 ///


4 /// 俗话说:“好吃不如饺子,舒服不如倒着”。今天奶奶发话要吃他大孙子和孙媳妇包的饺子。今天还拿吃饺子这件事来说说命令模式的实现吧。 5 ///

6 class Client 7 {
8 static void Main(string[] args)
9 { 10 //奶奶想吃猪肉大葱馅的饺子
11 PatrickLiuAndWife liuAndLai = new PatrickLiuAndWife();//命令接受者
12 Command command = new MakeDumplingsCommand(liuAndLai);//命令
13 PaPaInvoker papa = new PaPaInvoker(command); //命令请求者 14
15 //奶奶发布命令
16 papa.ExecuteCommand(); 17
18
19 Console.Read(); 20 } 21 } 22
23 //这个类型就是请求者角色–也就是我爸爸的角色,告诉奶奶要吃饺子
24 public sealed class PaPaInvoker 25 { 26 //我爸爸从奶奶那里接受到的命令
27 private Command _command; 28
29 //爸爸开始接受具体的命令
30 public PaPaInvoker(Command command) 31 { 32 this._command = command; 33 } 34
35 //爸爸给我们下达命令
36 public void ExecuteCommand() 37 { 38 _command.MakeDumplings(); 39 } 40 } 41
42 //该类型就是抽象命令角色–Commmand,定义了命令的抽象接口,任务是包饺子
43 public abstract class Command 44 { 45 //真正任务的接受者
46 protected PatrickLiuAndWife _worker; 47
48 protected Command(PatrickLiuAndWife worker) 49 { 50 _worker = worker; 51 } 52
53 //该方法就是抽象命令对象Command的Execute方法
54 public abstract void MakeDumplings(); 55 } 56
57 //该类型是具体命令角色–ConcreteCommand,这个命令完成制作“猪肉大葱馅”的饺子
58 public sealed class MakeDumplingsCommand : Command 59 { 60 public MakeDumplingsCommand(PatrickLiuAndWife worker) : base(worker) { } 61
62 //执行命令–包饺子
63 public override void MakeDumplings() 64 { 65 //执行命令—包饺子
66 _worker.Execute(“今天包的是农家猪肉和农家大葱馅的饺子”); 67 } 68 } 69
70 //该类型是具体命令接受角色Receiver,具体包饺子的行为是我们夫妻俩来完成的
71 public sealed class PatrickLiuAndWife 72 { 73 //这个方法相当于Receiver类型的Action方法
74 public void Execute(string job) 75 { 76 Console.WriteLine(job); 77 } 78 } 79 }

复制代码

   

5.3、迭代器模式(Iterator Pattern):该模式注重封装对集合的操作,支持集合实例的变化,屏蔽集合对象内部复杂结构,提供客户程序对它的透明遍历。

       动机(Motivate):在软件构建过程中,集合对象内部结构常常变化各异。但对于这些集合对象,我们希望在不暴露其内部结构的同时,可以让外部客户代码透明地访问其中包含的元素;同时这种“透明遍历”也为“同一种算法在多种集合对象上进行操作”提供了可能。使用面向对象技术将这种遍历机制抽象为“迭代器对象”为“应对变化中的集合对象”提供了一种优雅的方式。

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

       具体结构图如下所示

            

    5.4、观察者模式(Observer Pattern):该模式注重的是变化通知,变化通知指目标对象发生变化,依赖的对象就能获得通知并进行相应操作,这是一种“一对多”的关系。

       动机(Motivate):在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。

       意图(Intent):定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。

       具体结构图如下所示

              

    5.5、中介者模式(Mediator Pattern):该模式注重封装对象间的交互,通过封装一系列对象之间的复杂交互,使他们不需要显式相互引用,实现解耦。

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

       意图(Intent):定义了一个中介对象来封装一系列对象之间的交互关系。中介者使各个对象之间不需要显式地相互引用,从而使耦合性降低,而且可以独立地改变它们之间的交互行为。

       具体的结构图如下所示

           

       代码实例

复制代码

1 namespace 中介者模式的实现 2 {
3 //抽象中介者角色
4 public interface Mediator 5 {
6 void Command(Department department); 7 }
8
9 //总经理–相当于具体中介者角色
10 public sealed class President : Mediator 11 {
12 //总经理有各个部门的管理权限
13 private Financial _financial; 14 private Market _market; 15 private Development _development; 16
17 public void SetFinancial(Financial financial) 18 {
19 this._financial = financial; 20 }
21 public void SetDevelopment(Development development) 22 {
23 this._development = development; 24 }
25 public void SetMarket(Market market) 26 {
27 this._market = market; 28 }
29
30 public void Command(Department department) 31 {
32 if (department.GetType() == typeof(Market))
33 {
34 _financial.Process();
35 }
36 }
37 }
38
39 //同事类的接口
40 public abstract class Department 41 {
42 //持有中介者(总经理)的引用
43 private Mediator mediator; 44
45 protected Department(Mediator mediator) 46 {
47 this.mediator = mediator; 48 }
49
50 public Mediator GetMediator 51 {
52 get { return mediator; } 53 private set { this.mediator = value; } 54 }
55
56 //做本部门的事情
57 public abstract void Process(); 58
59 //向总经理发出申请
60 public abstract void Apply(); 61 }
62
63 //开发部门
64 public sealed class Development : Department 65 {
66 public Development(Mediator m) : base(m) { }
67
68 public override void Process() 69 {
70 Console.WriteLine(“我们是开发部门,要进行项目开发,没钱了,需要资金支持!”);
71 }
72
73 public override void Apply() 74 {
75 Console.WriteLine(“专心科研,开发项目!”);
76 }
77 }
78
79 //财务部门
80 public sealed class Financial : Department 81 {
82 public Financial(Mediator m) : base(m) { }
83
84 public override void Process() 85 {
86 Console.WriteLine(“汇报工作!没钱了,钱太多了!怎么花?”);
87 }
88
89 public override void Apply() 90 {
91 Console.WriteLine(“数钱!”);
92 }
93 }
94
95 //市场部门
96 public sealed class Market : Department 97 {
98 public Market(Mediator mediator) : base(mediator) { }
99
100 public override void Process() 101 { 102 Console.WriteLine(“汇报工作!项目承接的进度,需要资金支持!”); 103 GetMediator.Command(this); 104 } 105
106 public override void Apply() 107 { 108 Console.WriteLine(“跑去接项目!”); 109 } 110 } 111
112
113 class Program 114 { 115 static void Main(String[] args) 116 { 117 President mediator = new President(); 118 Market market = new Market(mediator); 119 Development development = new Development(mediator); 120 Financial financial = new Financial(mediator); 121
122 mediator.SetFinancial(financial); 123 mediator.SetDevelopment(development); 124 mediator.SetMarket(market); 125
126 market.Process(); 127 market.Apply(); 128
129 Console.Read(); 130 } 131 } 132 }

复制代码

   

5.6、状态模式(State Pattern):该模式注重封装与状态相关的行为(定义状态类,会把和一个状态相关的操作都放到这个类里面),支持状态的变化,从而在其内部状态改变时改变它的行为。

       动机(Motivate):在软件构建过程中,某些对象的状态如果改变,其行为也会随之而发生变化,比如文档处于只读状态,其支持的行为和读写状态支持的行为就可能完全不同。如何在运行时根据对象的状态来透明地更改对象的行为?而不会为对象操作和状态转化之间引入紧耦合?

       意图(Intent):允许一个对象在其内部状态改变时改变它的行为。从而使对象看起来似乎修改了其行为。

       具体结构图如下所示

              

       代码实例

复制代码

1 namespace 状态模式的实现 2 {
3 //环境角色—相当于Context类型
4 public sealed class Order 5 {
6 private State current; 7
8 public Order() 9 {
10 //工作状态初始化为上午工作状态
11 current = new WaitForAcceptance(); 12 IsCancel = false;
13 }
14 private double minute; 15 public double Minute 16 {
17 get { return minute; } 18 set { minute = value; } 19 }
20
21 public bool IsCancel { get; set; }
22
23 private bool finish; 24 public bool TaskFinished 25 {
26 get { return finish; } 27 set { finish = value; } 28 }
29 public void SetState(State s) 30 {
31 current = s; 32 }
33 public void Action() 34 {
35 current.Process(this);
36 }
37 }
38
39 //抽象状态角色—相当于State类型
40 public interface State 41 {
42 //处理订单
43 void Process(Order order); 44 }
45
46 //等待受理–相当于具体状态角色
47 public sealed class WaitForAcceptance : State 48 {
49 public void Process(Order order) 50 {
51 System.Console.WriteLine(“我们开始受理,准备备货!”);
52 if (order.Minute < 30 && order.IsCancel) 53 {
54 System.Console.WriteLine(“接受半个小时之内,可以取消订单!”);
55 order.SetState(new CancelOrder()); 56 order.TaskFinished = true;
57 order.Action();
58 }
59 order.SetState(new AcceptAndDeliver()); 60 order.TaskFinished = false;
61 order.Action();
62 }
63 }
64
65 //受理发货—相当于具体状态角色
66 public sealed class AcceptAndDeliver : State 67 {
68 public void Process(Order order) 69 {
70 System.Console.WriteLine(“我们货物已经准备好,可以发货了,不可以撤销订单!”);
71 if (order.Minute < 30 && order.IsCancel) 72 {
73 System.Console.WriteLine(“接受半个小时之内,可以取消订单!”);
74 order.SetState(new CancelOrder()); 75 order.TaskFinished = true;
76 order.Action();
77 }
78 if (order.TaskFinished==false)
79 {
80 order.SetState(new Success()); 81 order.Action();
82 }
83 }
84 }
85
86 //交易成功—相当于具体状态角色
87 public sealed class Success : State 88 {
89 public void Process(Order order) 90 {
91 System.Console.WriteLine(“订单结算”);
92 order.SetState(new ConfirmationReceipt()); 93 order.TaskFinished = true;
94 order.Action();
95 }
96 }
97
98 //确认收货—相当于具体状态角色
99 public sealed class ConfirmationReceipt : State 100 { 101 public void Process(Order order) 102 { 103 System.Console.WriteLine(“检查货物,没问题可以就可以签收!”); 104 order.SetState(new ConfirmationReceipt()); 105 order.TaskFinished = true; 106 order.Action(); 107 } 108 } 109
110 //取消订单—相当于具体状态角色
111 public sealed class CancelOrder : State 112 { 113 public void Process(Order order) 114 { 115 System.Console.WriteLine(“检查货物,有问题,取消订单!”); 116 order.SetState(new CancelOrder()); 117 order.TaskFinished = true; 118 order.Action(); 119 } 120 } 121
122
123 public class Client 124 { 125 public static void Main(String[] args) 126 { 127 //订单
128 Order order = new Order(); 129 order.Minute = 9; 130 order.Action(); 131 //可以取消订单
132 order.IsCancel = true; 133 order.Minute = 20; 134 order.Action(); 135 order.Minute = 33; 136 order.Action(); 137 order.Minute = 43; 138 order.Action(); 139
140 Console.Read(); 141 } 142 } 143 }

复制代码

   

5.7、策略模式(Stragety Pattern):该模式注重封装算法,这里面没有算法骨架,一种算法就是一种解决方案,一种方法策略。支持算法的变化,通过封装一系列算法,可以做到算法的替换来满足客户的需求。

       动机(Motivate): 在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂;而且有时候支持不使用的算法也是一个性能负担。如何在运行时根据需要透明地更改对象的算法?将算法与对象本身解耦,从而避免上述问题?

       意图(Intent):定义一系列算法,把它们一个个封装起来,并且使它们可互相替换。该模式使得算法可独立于使用它的客户而变化。

       具体结构图如下所示

          

    5.8、责任链模式(Chain of Responsibility Pattern):该模式注重封装对象责任,支持责任的变化,通过动态构建职责链,实现业务处理。在现实生活中,请假流程,采购流程等都是职责链模式很好的例子。

       动机(Motivate):在软件构建过程中,一个请求可能被多个对象处理,但是每个请求在运行时只能有一个接受者,如果显示指定,将必不可少地带来请求发送者与接受者的紧耦合。如何使请求的发送者不需要指定具体的接受者,让请求的接受者自己在运行时决定来处理请求,从而使两者解耦。

       意图(Intent):避免请求发送者与接收者耦合在一起,让多个对象都有可能接受请求,将这些对象连接成一条链,并且沿着这条链传递请求,知道有对象处理它为止。

       具体结构图如下所示

            

    5.9、访问者模式(Visitor Pattern):该模式注重封装对象操作变化,支持在运行时为类结构添加新的操作,在类层次结构中,在不改变各类的前提下定义作用于这些类实例的新的操作。

       动机(Motivate):在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给子类带来很繁重的变更负担,甚至破坏原有设计。如何在不更改类层次结构的前提下,在运行时根据需要透明地为类层次结构上的各个类动态添加新的操作,从而避免上述问题?

       意图(Intent):表示一个作用于某对象结构中的各个元素的操作。它可以在不改变各元素的类的前提下定义作用于这些元素的新的操作。

       具体结构图如下所示

            

       代码实例

复制代码

1 namespace Vistor 2 {
3 //抽象图形定义—相当于“抽象节点角色”Element
4 public abstract class Shape 5 {
6 //画图形
7 public abstract void Draw(); 8 //外界注入具体访问者
9 public abstract void Accept(ShapeVisitor visitor); 10 }
11
12 //抽象访问者 Visitor
13 public abstract class ShapeVisitor 14 {
15 public abstract void Visit(Rectangle shape); 16
17 public abstract void Visit(Circle shape); 18
19 public abstract void Visit(Line shape); 20
21 //这里有一点要说:Visit方法的参数可以写成Shape吗?就是这样 Visit(Shape shape),当然可以,但是ShapeVisitor子类Visit方法就需要判断当前的Shape是什么类型,是Rectangle类型,是Circle类型,或者是Line类型。
22 }
23
24 //具体访问者 ConcreteVisitor
25 public sealed class CustomVisitor : ShapeVisitor 26 {
27 //针对Rectangle对象
28 public override void Visit(Rectangle shape) 29 {
30 Console.WriteLine(“针对Rectangle新的操作!”);
31 }
32 //针对Circle对象
33 public override void Visit(Circle shape) 34 {
35 Console.WriteLine(“针对Circle新的操作!”);
36 }
37 //针对Line对象
38 public override void Visit(Line shape) 39 {
40 Console.WriteLine(“针对Line新的操作!”);
41 }
42 }
43
44 //矩形—-相当于“具体节点角色” ConcreteElement
45 public sealed class Rectangle : Shape 46 {
47 public override void Draw() 48 {
49 Console.WriteLine(“矩形我已经画好!”);
50 }
51
52 public override void Accept(ShapeVisitor visitor) 53 {
54 visitor.Visit(this);
55 }
56 }
57
58 //圆形—相当于“具体节点角色”ConcreteElement
59 public sealed class Circle : Shape 60 {
61 public override void Draw() 62 {
63 Console.WriteLine(“圆形我已经画好!”);
64 }
65
66 public override void Accept(ShapeVisitor visitor) 67 {
68 visitor.Visit(this);
69 }
70 }
71
72 //直线—相当于“具体节点角色” ConcreteElement
73 public sealed class Line : Shape 74 {
75 public override void Draw() 76 {
77 Console.WriteLine(“直线我已经画好!”);
78 }
79
80 public override void Accept(ShapeVisitor visitor) 81 {
82 visitor.Visit(this);
83 }
84 }
85
86 //结构对象角色
87 internal class AppStructure 88 {
89 private ShapeVisitor _visitor; 90
91 public AppStructure(ShapeVisitor visitor) 92 {
93 this._visitor = visitor; 94 }
95
96 public void Process(Shape shape) 97 {
98 shape.Accept(_visitor);
99 } 100 } 101
102 class Program 103 { 104 static void Main(string[] args) 105 { 106 //如果想执行新增加的操作
107 ShapeVisitor visitor = new CustomVisitor(); 108 AppStructure app = new AppStructure(visitor); 109
110 Shape shape = new Rectangle(); 111 shape.Draw();//执行自己的操作
112 app.Process(shape);//执行新的操作
113
114
115 shape = new Circle(); 116 shape.Draw();//执行自己的操作
117 app.Process(shape);//执行新的操作
118
119
120 shape = new Line(); 121 shape.Draw();//执行自己的操作
122 app.Process(shape);//执行新的操作
123
124
125 Console.ReadLine(); 126 } 127 } 128 }

复制代码

   

5.10、备忘录模式(Memento Pattern):该模式注重封装对象状态变化,支持状态保存、恢复。现实生活中的手机通讯录备忘录,操作系统备份,数据库备份等都是备忘录模式的应用。

       动机(Motivate):在软件构建过程中,某些对象的状态在转换的过程中,可能由于某种需要,要求程序能够回溯到对象之前处于某个点时的状态。如果使用一些公有接口来让其他对象得到对象的状态,便会暴露对象的细节实现。如何实现对象状态的良好保存与恢复,但同时又不会因此而破坏对象本身的封装性?

       意图(Intent):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态(如果没有这个关键点,其实深拷贝就可以解决问题)。这样以后就可以将该对象恢复到原先保存的状态。

       具体的结构图如下所示

          

       代码实例

复制代码

1 namespace MementoPattern 2 {
3 // 联系人–需要备份的数据,是状态数据,没有操作
4 public sealed class ContactPerson 5 {
6 //姓名
7 public string Name { get; set; }
8
9 //电话号码
10 public string MobileNumber { get; set; }
11 }
12
13 // 发起人–相当于【发起人角色】Originator
14 public sealed class MobileBackOriginator 15 {
16 // 发起人需要保存的内部状态
17 private List _personList; 18
19
20 public List ContactPersonList 21 {
22 get
23 {
24 return this._personList;
25 }
26
27 set
28 {
29 this._personList = value; 30 }
31 }
32 //初始化需要备份的电话名单
33 public MobileBackOriginator(List personList) 34 {
35 if (personList != null)
36 {
37 this._personList = personList; 38 }
39 else
40 {
41 throw new ArgumentNullException(“参数不能为空!”);
42 }
43 }
44
45 // 创建备忘录对象实例,将当期要保存的联系人列表保存到备忘录对象中
46 public ContactPersonMemento CreateMemento() 47 {
48 return new ContactPersonMemento(new List(this._personList));
49 }
50
51 // 将备忘录中的数据备份还原到联系人列表中
52 public void RestoreMemento(ContactPersonMemento memento) 53 {
54 this.ContactPersonList = memento.ContactPersonListBack; 55 }
56
57 public void Show() 58 {
59 Console.WriteLine(“联系人列表中共有{0}个人,他们是:”, ContactPersonList.Count);
60 foreach (ContactPerson p in ContactPersonList) 61 {
62 Console.WriteLine(“姓名: {0} 号码: {1}”, p.Name, p.MobileNumber);
63 }
64 }
65 }
66
67 // 备忘录对象,用于保存状态数据,保存的是当时对象具体状态数据–相当于【备忘录角色】Memeto
68 public sealed class ContactPersonMemento 69 {
70 // 保存发起人创建的电话名单数据,就是所谓的状态
71 public List ContactPersonListBack { get; private set; }
72
73 public ContactPersonMemento(List personList) 74 {
75 ContactPersonListBack = personList; 76 }
77 }
78
79 // 管理角色,它可以管理【备忘录】对象,如果是保存多个【备忘录】对象,当然可以对保存的对象进行增、删等管理处理—相当于【管理者角色】Caretaker
80 public sealed class MementoManager 81 {
82 //如果想保存多个【备忘录】对象,可以通过字典或者堆栈来保存,堆栈对象可以反映保存对象的先后顺序
83 //比如:public Dictionary<string, ContactPersonMemento> ContactPersonMementoDictionary { get; set; }
84 public ContactPersonMemento ContactPersonMemento { get; set; }
85 }
86
87 class Program 88 {
89 static void Main(string[] args)
90 {
91 List persons = new List()
92 {
93 new ContactPerson() { Name=”黄飞鸿”, MobileNumber = “13533332222”},
94 new ContactPerson() { Name=”方世玉”, MobileNumber = “13966554433”},
95 new ContactPerson() { Name=”洪熙官”, MobileNumber = “13198765544”}
96 };
97
98 //手机名单发起人
99 MobileBackOriginator mobileOriginator = new MobileBackOriginator(persons); 100 mobileOriginator.Show(); 101
102 // 创建备忘录并保存备忘录对象
103 MementoManager manager = new MementoManager(); 104 manager.ContactPersonMemento = mobileOriginator.CreateMemento(); 105
106 // 更改发起人联系人列表
107 Console.WriteLine(“-—移除最后一个联系人——–”); 108 mobileOriginator.ContactPersonList.RemoveAt(2); 109 mobileOriginator.Show(); 110
111 // 恢复到原始状态
112 Console.WriteLine(“-——恢复联系人列表——“); 113 mobileOriginator.RestoreMemento(manager.ContactPersonMemento); 114 mobileOriginator.Show(); 115
116 Console.Read(); 117 } 118 } 119 }

复制代码

   

5.11、解释器模式(Interpreter Pattern):该模式注重封装特定领域变化,将特定领域的问题表达为某种语法规则下的句子,然后构建一个解释器来解释这样的句子,从而达到解决问题的目的。C#的编译器,中英翻译工具,正则表达式都是解释器应用的很好例子。

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

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

       具体的结构图如下所示

                           

六、总结

    C#版本23种面向对象的设计模式,终于写完了,今天是一个总结性的文章。各个模式都列了出来,每个模式的【动机】、【意图】和【结构图】也写了出来,可以方便大家查看。重新自己写了一遍,感觉很多都不一样了。理解更深刻了,学无止境,继续前进吧。

之前写过Python的设计模式,由于经常不使用Python,回过头来再看Python的设计模式,有时候感觉并不是那么的显而易见,所以使用c#重新将代码编写一遍,更加清晰明了。

这里借用原来的介绍,对模式做简要说明,模式简易说明和类图,请查看 http://www.cnblogs.com/cotton/category/606629.html

设计模式分为三种类型

  • 创建型模式:简单工厂、工厂方法模式、抽象工厂模式、建造者模式、原型模式、单例模式

  • 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。

  • 行为型模式:模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。


创建型模式

一、简单工厂模式

模式说明

简单工厂模式又称之为静态工厂方法,属于创建型模式。在简单工厂模式中,可以根据传递的参数不同,返回不同类的实例。简单工厂模式定义了一个类,这个类专门用于创建其他类的实例,这些被创建的类都有一个共同的父类。

模式结构图

代码示例

namespace DesignPattern
{ public class SimpleFactory
{ public static Operation GetOperation(op op, double a, double b)
{ switch (op)
{ case op.add: return new Add(a, b); case op.sub: return new Sub(a, b); case op.mul: return new Mul(a, b); case op.div: return new Div(a, b); default: return new undef(a, b);
}
}
} public enum op
{
add = ‘+’,
sub = ‘-‘,
mul = ‘*‘,
div = ‘/‘ } public abstract class Operation
{ public double a, b; public Operation(double a, double b)
{ this.a = a; this.b = b;
} public abstract double GetResult();
} public class Add : Operation
{ public Add(double a, double b) : base(a, b) { } public override double GetResult()
{ return a + b;
}
} public class Sub : Operation
{ public Sub(double a, double b) : base(a, b) { } public override double GetResult()
{ return a - b;
}
} public class Mul : Operation
{ public Mul(double a, double b) : base(a, b) { } public override double GetResult()
{ return a * b;
}
} public class Div : Operation
{ public Div(double a, double b) : base(a, b) { } public override double GetResult()
{ try { return a / b;
} catch (DivideByZeroException e)
{ throw e;
}
}
} public class undef : Operation
{ public undef(double a, double b) : base(a, b) { } public override double GetResult()
{ throw new NotImplementedException();
}

}

}

View Code

二、工厂方法模式

模式说明

工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法模式让实例化推迟到子类。

和简单工厂区别在于,每个工厂只管生产自己对应的产品,而简单工厂是一个工厂生产各种产品。

模式结构图

代码示例

namespace DesignPattern
{ public interface ILogger
{ void write(string log);
} public class EventLogger : ILogger
{ public void write(string log)
{
Console.WriteLine(“EventLog:” + log);
}
} public class FileLogger : ILogger
{ public void write(string log)
{
Console.WriteLine(“FileLog:” + log);
}
} public interface ILoggerFactory
{
ILogger CreateLogger();
} public class EventLoggerFactory : ILoggerFactory
{ public ILogger CreateLogger()
{ return new EventLogger();
}
} public class FileLoggerFactory : ILoggerFactory
{ public ILogger CreateLogger()
{ return new FileLogger();
}
}
}

View Code

三、抽象工厂模式

模式说明

抽象工厂模式提供一个接口,用于创建相关或者依赖对象的家族,而不需要明确指定具体类。

抽象工厂允许客户端使用抽象的接口来创建一组相关的产品,而不需要关系实际产出的具体产品是什么。这样一来,客户就可以从具体的产品中被解耦。

和工厂方法主要区别于,抽象工厂内要像像定义中说的一样,‘创建一组相关的产品’。

感觉像是(不知道这样理解对否):简单工厂是一个工厂生产多个产品;工厂方法是拆分成子工厂,分别生产各自产品;抽象工厂整合工厂方法和简单工厂,随着子工厂规模变大,也可以生产多个类似产品。

模式结构图

代码示例

namespace DesignPattern
{ //抽象实体
public abstract class absSalary
{ protected double salary; protected double bonus; protected double tax; public absSalary(double sal, double bns, double t)
{ this.salary = sal; this.bonus = bns; this.tax = t;
} public abstract double CalculateTax();
} public class ChineseSalary : absSalary
{ public ChineseSalary(double sal, double bns, double t)
: base(sal, bns, t)
{
} public override double CalculateTax()
{ return (base.salary + base.bonus - 3500) * base.tax;
}
} public class ForeignerSalary : absSalary
{ public ForeignerSalary(double sal, double bonus, double tax)
: base(sal, bonus, tax)
{
} public override double CalculateTax()
{ return (base.salary + base.bonus - 4000) * base.tax;
}
} public abstract class absSocialSecurity
{ protected double SocialSecurity; public absSocialSecurity()
{ this.SocialSecurity = 0;
} public virtual double GetSocialSecurity()
{ return this.SocialSecurity;
}
} public class ChineseSocialSecurity : absSocialSecurity
{ public ChineseSocialSecurity(double socialsecurity)
: base()
{ base.SocialSecurity = socialsecurity < 1000 ? 1000 : socialsecurity;
}
} public class ForeignerSocialSecurity : absSocialSecurity
{ public ForeignerSocialSecurity(double socialsecurity)
: base()
{ base.SocialSecurity = socialsecurity < 1500 ? 1500 : socialsecurity;
}
} //抽象工厂,生产一系列产品(多个Create方法,分别对应不同产品)
public interface AbstractFactory
{
absSalary CreateSalary(double sal, double bonus, double tax);
absSocialSecurity CreateSocialSecurity(double socialsecurity);
} public class ChineseFactory : AbstractFactory
{ public absSalary CreateSalary(double sal, double bonus, double tax)
{ return new ChineseSalary(sal, bonus, tax);
} public absSocialSecurity CreateSocialSecurity(double socialsecurity)
{ return new ChineseSocialSecurity(socialsecurity);
}
} public class ForeignerFactory : AbstractFactory
{ public absSalary CreateSalary(double sal, double bonus, double tax)
{ return new ForeignerSalary(sal, bonus, tax);
} public absSocialSecurity CreateSocialSecurity(double socialsecurity)
{ return new ForeignerSocialSecurity(socialsecurity);
}
}
}

View Code

四、创建者模式

模式说明

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

建造者模式构建复杂对象就像造汽车一样,是一个一个组件一个一个步骤创建出来的,它允许用户通过制定的对象类型和内容来创建他们,但是用户并不需要知道这个复杂对象是如何构建的,它只需要明白通过这样做我可以得到一个完整的复杂对象实例。

和工厂方法很像,创造者是一个builder内每个方法分别创建产品零部件,而工厂方法是每个factory生产一个产品。如果把builder的零部件当做一个完整产品呢?是不是就像 builder又再一次封装了factory~ 

模式结构图

代码示例

namespace DesignPattern
{ public class Meal
{ private string food; private string drink; public Meal() { } public void setFood(string food)
{ this.food = food;
} public void setDrink(string drink)
{ this.drink = drink;
} public string getFood()
{ return this.food;
} public string getDrink()
{ return this.drink;
}
} //建造者,分别建造不同部件,然后返回整体
public abstract class Builder
{ protected Meal meal = new Meal(); public abstract void buildFood(); public abstract void buildDrink(); public Meal GetMeal()
{ return meal;
}
} public class MealABuilder : Builder
{ public override void buildFood()
{
meal.setFood(“A food”);
} public override void buildDrink()
{
meal.setDrink(“A drink”);
}
} public class MealBBuilder : Builder
{ public override void buildFood()
{
meal.setFood(“B food”);
} public override void buildDrink()
{
meal.setDrink(“B drink”);
}
} public class Waitor
{ public void PrepareMeal(Builder builder)
{
builder.buildDrink();
builder.buildFood();
}
}
}

View Code

五、原型模式

模式说明

所谓原型模式就是用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。

说到复制,就会有深/浅两种复制,这是面向对象的值类型和引用类型的差异,具体不作说明

模式结构图

代码示例

namespace DesignPattern
{
[Serializable] public class other
{ public int value { get; set; } public other()
{
value = 10;
}
}

\[Serializable\] public abstract class ColorPrototype
{ public int red { get; set; } public int green { get; set; } public int blue { get; set; } public other o = new other(); //浅拷贝
    public virtual ColorPrototype Clone()
    { return (ColorPrototype)this.MemberwiseClone();
    }
} public class Red : ColorPrototype
{ public override ColorPrototype Clone()
    { return base.Clone();
    }
}

\[Serializable\] public class Green : ColorPrototype
{ public override ColorPrototype Clone()
    {
        BinaryFormatter formatter \= new BinaryFormatter();
        MemoryStream stream \= new MemoryStream();
        formatter.Serialize(stream, this);
        stream.Position \= 0;
        ColorPrototype obj \= (ColorPrototype)formatter.Deserialize(stream); return obj;
    }
}

}

View Code

六、单例模式

代码示例

namespace DesignPattern
{ public class Singleton
{ private int cnt = 0; private static Singleton instance = null; private volatile static Singleton safeInstance = null; private static readonly object lockedobj = new object(); private Singleton()
{
} public static Singleton GetInstance()
{ if (instance == null) instance = new Singleton(); return instance;
} public static Singleton GetSafeInstance()
{ if (safeInstance == null)
{ lock (lockedobj)
{ if (safeInstance == null)
{
safeInstance = new Singleton();
}
}
} return safeInstance;
} public void count()
{
cnt += 1;
} public int getCnt()
{ return cnt;
}
}

}

View Code


结构型模式

七、适配器模式

模式说明

适配器模式就是将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。

在适配器模式中,我们可以定义一个包装类,包装不兼容接口的对象,这个包装类就是适配器,它所包装的对象就是适配者。

适配器提供给客户需要的接口,适配器的实现就是将客户的请求转换成对适配者的相应的接口的引用。也就是说,当客户调用适配器的方法时,适配器方法内部将调用 适配者的方法,客户并不是直接访问适配者的,而是通过调用适配器方法访问适配者。因为适配器可以使互不兼容的类能够“合作愉快”。

模式结构图

代码示例

注:此处ILogger接口使用了【工厂方法模式】定义的接口

namespace DesignPattern
{ public interface IAdaptor
{ void writelog(string log);
} public class LogAdaptor : IAdaptor
{
ILogger logger; public LogAdaptor(ILogger logger)
{ this.logger = logger;
} public void writelog(string log)
{ this.logger.write(log);
}
}
}

View Code

八、桥接模式

模式说明

桥接模式即将抽象部分与它的实现部分分离开来,使他们都可以独立变化。

桥接模式将继承关系转化成关联关系,它降低了类与类之间的耦合度,减少了系统中类的数量,也减少了代码量。

个人感觉,代理模式、适配器模式和桥接模式相类似,代理模式是一个代理对外表示一个特定的类,适配器模式相当于一个适配器代理多个类,而桥接模式则更加适用于多个对多个的时候

模式结构图

代码示例

namespace DesignPattern
{ public abstract class Color
{ public string name { get; set; }
} public abstract class Shape
{ private Color color; public string name { get; set; } public void SetColor(Color c)
{
color = c;
} public void Draw()
{
Console.WriteLine(“draw shape {0} with color {1}”, this.name, this.color.name);
}
} public class White : Color
{ public White()
{ this.name = “white”;
}
} public class Blue : Color
{ public Blue()
{ this.name = “blue”;
}
} public class Squre : Shape
{ public Squre()
{ this.name = “squre”;
}
} public class Circle : Shape
{ public Circle()
{ this.name = “circle”;
}
}
}

View Code

九、装饰者模式

模式说明

装饰者模式装饰者模式可以动态地给一个对象增加一些额外的职责。就增加功能来说,装饰者模式相比生成子类更为灵活。

模式结构图

代码示例

namespace DesignPattern
{ public abstract class Car
{ public string color { get; set; } public int compartment { get; set; } public void run()
{
Console.WriteLine(color + “ “ + compartment + “ compartment “ + this.GetType().Name + “ is running!”);
}
} public class Benz:Car
{ public Benz()
{ base.color = “black”; base.compartment = 1;
}
} public class QQ:Car
{ public QQ()
{ base.color = “black”; base.compartment = 1;
}
} public abstract class Decorator : Car
{ public Car car; public Decorator(Car car)
{ this.car = car;
}
} public class ColorDecorator:Decorator
{ //一般在构造函数内完成属性的修改(装饰),这里单独加了一个decorate方法
public ColorDecorator(Car car):base(car)
{
} public Car decorate(string color)
{ base.car.color = color; return base.car;
}
} public class CompartmentDecorator : Decorator
{ public CompartmentDecorator(Car car)
: base(car)
{
} public Car decorate(int compartment)
{ base.car.compartment = compartment; return base.car;
}
}
}

View Code

十、组合模式

模式说明

组合模式组合多个对象形成树形结构以表示“整体-部分”的结构层次。

组合模式对单个对象(叶子对象)和组合对象(组合对象)具有一致性,它将对象组织到树结构中,可以用来描述整体与部分的关系。同时它也模糊了简单元素(叶 子对象)和复杂元素(容器对象)的概念,使得客户能够像处理简单元素一样来处理复杂元素,从而使客户程序能够与复杂元素的内部结构解耦。

模式结构图

代码示例

namespace DesignPattern
{ public abstract class File
{ protected string name; public File(string name)
{ this.name = name;
} public abstract void Display();
} public class Folder : File
{
IList list; public Folder(string name)
: base(name)
{
list = new List();
} public void AddFile(File file)
{
list.Add(file);
} public void RemoveFile(File file)
{
list.Remove(file);
} public override void Display()
{
Console.WriteLine(“folder:” + this.name); foreach (File f in list)
{
f.Display();
}
}
} public class ImageFile : File
{ public ImageFile(string name)
: base(name)
{
} public override void Display()
{
Console.WriteLine(“ImageFile:” + this.name);
}
}
}

View Code

十一、外观模式

模式说明

所谓外观模式就是提供一个统一的接口,用来访问子系统中的一群接口。

模式结构图

代码示例

namespace DesignPattern
{ public class Facade
{
Light _light = new Light();
TV _tv = new TV(); public void off()
{
_light.on();
_tv.off();
} public void on()
{
_tv.on();
_light.off();
}
} class Light
{ public void on()
{
Console.WriteLine(“light on!”);
} public void off()
{
Console.WriteLine(“light off!”);
}
} class TV
{ public void on()
{
Console.WriteLine(“tv on!”);
} public void off()
{
Console.WriteLine(“tv off!”);
}
}
}

View Code

十二、享元模式

模式说明

所谓享元模式就是运行共享技术有效地支持大量细粒度对象的复用。系统使用少量对象,而且这些都比较相似,状态变化小,可以实现对象的多次复用。

FlyweightFactory内定义的实体是不变的(共享的),传入参数是状态变化。

缓存形式,传入参数已经被缓存则直接返回,否则创建参数对应实体,放入缓存并返回该新实体

模式结构图

代码示例

namespace DesignPattern
{ public class FlyweightFactory
{ static Dictionary<string, IFlyweight> pendic = new Dictionary<string, IFlyweight>(); public IFlyweight getPen(string color)
{ if (pendic.ContainsKey(color))
{ return pendic[color];
} else {
IFlyweight pen = new ConcreteFlyweight(color);
pendic.Add(color, pen); return pen;
}
} public void Display()
{ foreach (KeyValuePair<string,IFlyweight> pair in pendic)
{
Console.WriteLine(pair.Value.GetType().FullName + “:” + pair.Key);
}
}
} public interface IFlyweight
{ string GetColor();
}; public class ConcreteFlyweight : IFlyweight
{ string color; public ConcreteFlyweight(string color)
{ this.color = color;
} public string GetColor()
{ return this.color;
}
}
}

View Code

十三、代理模式

模式说明

代理模式就是给一个对象提供一个代理,并由代理对象控制对原对象的引用。

在代理模式中,“第三者”代理主要是起到一个中介的作用,它连接客户端和目标对象。

模式结构图

代码示例

namespace DesignPattern
{ public class Girl
{ public string name { get; set; } public Girl(string name)
{ this.name = name;
}
} public class Boy
{ private Girl girl; public string name { get; set; } public Boy(string name, Girl girl)
{ this.name = name; this.girl = girl;
} public void GiveFlower()
{
Console.WriteLine(“boy {0} give flower to girl {1}”, this.name, this.girl.name);
}
} public class Proxy
{ private Boy boy; public Proxy(Boy boy)
{ this.boy = boy;
} public void GiveFlower()
{ this.boy.GiveFlower();
}
}
}

View Code


行为型模式

十四、迭代器模式

代码示例

namespace DesignPattern
{ public class Persons : IEnumerable
{ string[] m_Names; public Persons(params string[] Names)
{
m_Names = new string[Names.Length];

        Names.CopyTo(m\_Names, 0);
    } public IEnumerator GetEnumerator()
    { foreach (string s in m\_Names)
        { yield return s;
        }
    } public int Length { get { return m\_Names.Length; } } public string this\[int i\]
    { get { return m\_Names\[i\];
        } set {
            m\_Names\[i\] \= value;
        }
    }
}

}

View Code

十五、解释器模式

模式说明

所谓解释器(Interpreter)就是将一系列指令转化成代码,能够执行的代码。Interpreter本来就有翻译的意思。GoF给它的定义是:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

模式结构图

代码示例

namespace DesignPattern
{ public class Context
{ private string msg; public Context(string msg)
{ this.msg = msg;
} public string GetMsg()
{ return this.msg;
}
} public interface Interpreter
{ string Interprete(Context context);
} public class UpperInterpreter : Interpreter
{ public string Interprete(Context context)
{ string msg = context.GetMsg(); return msg.ToUpperInvariant();
}
} public class LowerInterpreter : Interpreter
{ public string Interprete(Context context)
{ string msg = context.GetMsg(); return msg.ToLowerInvariant();
}
}
}

View Code

十六、命令模式

模式说明

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

模式结构图

代码示例

namespace DesignPattern
{ //接受命令的对象
public class CDMachine
{ public void on()
{
Console.WriteLine(“CD Machine turns on!”);
} public void off()
{
Console.WriteLine(“CD Machine turns off!”);
}
} //定义命令
public abstract class Command
{ public abstract void Execute(CDMachine cdMachine);
} public class TurnonCommand : Command
{ public override void Execute(CDMachine cdMachine)
{
cdMachine.on();
}
} public class TurnoffCommand : Command
{ public override void Execute(CDMachine cdMachine)
{
cdMachine.off();
}
} //发送命令的对象
public class Controller
{ //遥控的功能 — 可发送的命令
private TurnonCommand turnonCommand; private TurnoffCommand turnoffCommand; public Controller(TurnonCommand turnonCommand, TurnoffCommand turnoffCommand)
{ this.turnonCommand = turnonCommand; this.turnoffCommand = turnoffCommand;
} public void turnOn(CDMachine cdMachine)
{ this.turnonCommand.Execute(cdMachine);
} public void turnOff(CDMachine cdMachine)
{ this.turnoffCommand.Execute(cdMachine);
}
}
}

View Code

十七、中介者模式

模式说明

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

模式结构图

代码示例

namespace DesignPattern
{ public abstract class Person
{ public string name; public Mediator mediator; public Person(string name, Mediator mediator)
{ this.name = name; this.mediator = mediator;
} public void Contact(string msg)
{ //参数 this 代表 消息来自我
this.mediator.SendMsg(msg, this);
} internal void GetMsg(string msg)
{
Console.WriteLine(this.name + “ 收到消息:” + msg);
}
} public class HouseOwner : Person
{ public HouseOwner(string name, Mediator mediator) : base(name, mediator) { }
} public class Tenant : Person
{ public Tenant(string name, Mediator mediator) : base(name, mediator) { }
} public interface Mediator
{ void SendMsg(string msg, Person p);
} public class ConcreteMediator : Mediator
{
HouseOwner houseOwner;
Tenant tenant; public ConcreteMediator()
{
} public void SetHouseOwner(HouseOwner houseOwner)
{ this.houseOwner = houseOwner;
} public void SetTenant(Tenant tenant)
{ this.tenant = tenant;
} public void SendMsg(string msg, Person p)
{ if (p.GetType() == houseOwner.GetType())
{
tenant.GetMsg(msg);
} else {
houseOwner.GetMsg(msg);
}
}
}
}

View Code

十八、备忘录模式

模式说明

所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。

模式结构图

代码示例

namespace DesignPattern
{ public class Memonto
{ public int blood { get; set; } public int magic { get; set; }
} public class Caretaker
{ private Memonto memonto; public void SetMemonto(Memonto memonto)
{ this.memonto = memonto;
} public Memonto getMemonto()
{ return this.memonto;
}
} public class Original
{ public int blood { get; set; } public int magic { get; set; } public Memonto SaveMemonto()
{ return new Memonto() { blood = this.blood, magic = this.magic };
} public void RestoreMemonto(Memonto memonto)
{ this.blood = memonto.blood; this.magic = memonto.magic;
} public void display()
{
Console.WriteLine(“blood:” + this.blood + “\tmagic:” + this.magic);
}
}
}

View Code

十九、观察者模式

模式说明

定义了一种一对多的关系,让多个观察对象同时监听一个主题对象,当主题对象状态发生变化时会通知所有观察者。

模式结构图

代码示例

public interface Observer
{ void Update(Subject subject);
} public abstract class Subject
{
List obsList = new List(); public void AddObserver(Observer observer)
{
obsList.Add(observer);
} public void RemoveObserver(Observer observer)
{
obsList.Remove(observer);
} public void notity()
{ foreach (Observer o in obsList)
{
o.Update(this);
}
} private string _state; public void SetState(string state)
{ this._state = state;
} public string GetState()
{ return this._state;
}
} public class ConcreteSubject : Subject
{
} public class ConcreteObserver1 : Observer
{ public void Update(Subject subject)
{
Console.WriteLine(“ConcreteObserver1 get notice:” + subject.GetState());
}
} public class ConcreteObserver2 : Observer
{ public void Update(Subject subject)
{
Console.WriteLine(“ConcreteObserver2 get notice:” + subject.GetState());
}
}

View Code

//事件委托的方式
public delegate void updateDelegate(Subject subject); public class EventSubjet : Subject
{ public event updateDelegate UpdateHandler; public void EventNotify()
{
OnUpdate();
} private void OnUpdate()
{ if (UpdateHandler != null)
{
UpdateHandler(this);
}
}
}

View Code

二十、状态模式

模式说明

当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

模式结构图

代码示例

namespace DesignPattern
{ public interface IState
{ void display();
} public class WorkState:IState
{ public void display()
{
Console.WriteLine(“Working State”);
}
} public class RestState:IState
{ public void display()
{
Console.WriteLine(“Rest State”);
}
} public class Programmer
{
IState _state; public void Doing(DateTime dt)
{ if(dt.Hour<7 || dt.Hour > 22)
{
_state = new RestState();
} else {
_state = new WorkState();
}
_state.display();
}
}
}

View Code

二十一、模板模式

模式说明

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

模式结构图

代码示例

namespace DesignPattern
{ public abstract class Template
{ protected void boilWater()
{
Console.WriteLine(“boil water”);
} protected virtual void brew()
{
Console.WriteLine(“brew”);
} protected void pourInCup()
{
Console.WriteLine(“pour into cup”);
} protected virtual void addOther()
{
Console.WriteLine(“add other”);
} public void makeBeverage()
{
boilWater();
brew();
pourInCup();
addOther();
}
} public class Tea : Template
{ protected override void brew()
{
Console.WriteLine(“tea”);
} protected override void addOther()
{
Console.WriteLine(“add lemon”);
}
} public class Coffee : Template
{ protected override void brew()
{
Console.WriteLine(“coffee”);
} protected override void addOther()
{
Console.WriteLine(“add sugar”);
}
}
}

View Code

二十二、策略模式

模式说明

定义算法家族并且分别封装,它们之间可以相互替换而不影响客户端。

模式结构图

代码示例

namespace DesignPattern
{ public abstract class OrderStrategy
{ public List<int> orderList; public abstract void Order(); public void display()
{ foreach (int i in orderList)
{
Console.Write(i + “\t”);
}
Console.WriteLine();
}
} public class BubbleStrategy : OrderStrategy
{ public override void Order()
{ for (int i = 0; i < orderList.Count; i++)
{ for (int j = i + 1; j < orderList.Count; j++)
{ if (orderList[i] < orderList[j])//冒泡降序 小的冒上去
{ int temp = orderList[i];
orderList[i] = orderList[j];
orderList[j] = temp;
}
}
}
}
} public class SelectionStrategy : OrderStrategy
{ public override void Order()
{ for (int i = 0; i < orderList.Count; i++)
{ int smallvalue = orderList[i]; int smallposition = i; for (int j = i + 1; j < orderList.Count; j++)
{ if (orderList[j] < smallvalue)
{
smallposition = j;
smallvalue = orderList[j];
}
} //将最小值放到当前要排序的位置
orderList[smallposition] = orderList[i];
orderList[i] = smallvalue;
}
}
} public class InsertionStrategy : OrderStrategy
{ public override void Order()
{ for (int i = 1; i < orderList.Count; i++)
{ int temp = orderList[i];//当前要插入的值,相当于位置I是个空白位置,供对比进行后移
int j = i; //j之前的序列已经排序好,选一个位置把当前值插入

            while (j > 0)
            { //i从1开始,是因为这里j要比较前一个值
                if (temp < orderList\[j - 1\]) //插入过程中,每次比较的值大于当前值则向后移动

{
orderList[j] = orderList[j-1];
j--;
} else { break;
}
} //找到位置(break)或者循环正常结束(说明当前值最小)则赋值。
orderList[j] = temp;
}
}
} public class StrategyManager
{
OrderStrategy strategy; public void SetStrategy(OrderStrategy strategy)
{ this.strategy =strategy;
} public void Sort(List<int> list)
{ this.strategy.orderList = list; this.strategy.Order(); this.strategy.display();
}
}
}

View Code

二十三、访问者模式

模式说明

访问者模式即表示一个作用于某对象结构中的各元素的操作,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

模式结构图

代码示例

namespace DesignPattern
{ public interface Element
{ void accept(Visitor visitor);
} public class ConcreteElementA : Element
{ string name; public void SetName(string name)
{ this.name = name;
} public string GetName()
{ return this.name;
} public void accept(Visitor visitor)
{
visitor.visitElementA(this);
}
} public class ConcreteElementB : Element
{ int ID; public void SetID(int id)
{ this.ID = id;
} public int GetID()
{ return this.ID;
} public void accept(Visitor visitor)
{
visitor.visitElementB(this);
}
} public interface Visitor
{ void visitElementA(ConcreteElementA ea); void visitElementB(ConcreteElementB eb);
} public class ConcreteVisitorA : Visitor
{ public void visitElementA(ConcreteElementA ea)
{
Console.WriteLine(“ConcreteVisitorA visit ElemantA:” + ea.GetName());
} public void visitElementB(ConcreteElementB eb)
{
Console.WriteLine(“ConcreteVisitorA visit ElemantB:” + eb.GetID());
}
} public class ConcreteVisitorB : Visitor
{ public void visitElementA(ConcreteElementA ea)
{
Console.WriteLine(“ConcreteVisitorB visit ElemantA:” + ea.GetName());
} public void visitElementB(ConcreteElementB eb)
{
Console.WriteLine(“ConcreteVisitorB visit ElemantB:” + eb.GetID());
}
} public class objectStructure
{
List elementlist = new List(); public void Attach(Element e)
{
elementlist.Add(e);
} public void Dettach(Element e)
{
elementlist.Remove(e);
} public void Accept(Visitor visit)
{ foreach(Element e in elementlist)
{
e.accept(visit);
}
}
}
}

View Code

二十四、责任链模式

模式说明

避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止,这就是职责链模式。

模式结构图

代码示例

namespace DesignPattern
{ public class Request
{ int days; string name; public Request(int days, string name)
{ this.days = days; this.name = name;
} public int GetDays()
{ return days;
} public string GetName()
{ return name;
}

} public abstract class Responsibility
{ protected Responsibility responsibility; public Responsibility(Responsibility responsibility)
    { this.responsibility = responsibility;
    } public abstract void HandleRequest(Request request);
} public class Leader : Responsibility
{ public Leader(Responsibility responsibility)
        : base(responsibility)
    { } public override void HandleRequest(Request request)
    { if (request.GetDays() < 3)
        {
            Console.WriteLine("Leader passed {0}'s {1} days request", request.GetName(), request.GetDays());
        } else { this.responsibility.HandleRequest(request);
        }
    }
} public class Department : Responsibility
{ public Department(Responsibility responsibility)
        : base(responsibility)
    { } public override void HandleRequest(Request request)
    { if (request.GetDays() < 8)
        {
            Console.WriteLine("Department passed {0}'s {1} days request", request.GetName(), request.GetDays());
        } else { this.responsibility.HandleRequest(request);
        }
    }
} //责任链终端必须处理
public class Boss : Responsibility
{ public Boss() : base(null) { } public override void HandleRequest(Request request)
    { if (request.GetDays() < 15)
        {
            Console.WriteLine("Boss passed {0}'s {1} days request", request.GetName(), request.GetDays());
        } else {
            Console.WriteLine("Boss refused {0}'s {1} days request", request.GetName(), request.GetDays());
        }
    }
}

}

View Code

主程序(注:调用顺序与该列表顺序不一致)

namespace DesignPattern
{ class Program
{ static void Main(string[] args)
{ //简单工厂
Console.WriteLine(“简单工厂”);
Console.WriteLine(SimpleFactory.GetOperation(op.add, 1.1, 2.2).GetResult()); //工厂方法
Console.WriteLine(“\n工厂方法”);
ILoggerFactory factorymethod = new EventLoggerFactory();
ILogger iLogger = factorymethod.CreateLogger();
iLogger.write(“123”);

        factorymethod \= new FileLoggerFactory();
        iLogger \= factorymethod.CreateLogger();
        iLogger.write("321"); //抽象工厂
        Console.WriteLine("\\n抽象工厂");
        AbstractFactory absFactory \= new ChineseFactory();
        absSalary chSalary \= absFactory.CreateSalary(10000, 8000, 0.12);
        absSocialSecurity chScSc \= absFactory.CreateSocialSecurity(1200);
        Console.WriteLine(chSalary.CalculateTax());
        Console.WriteLine(chScSc.GetSocialSecurity());

        absFactory \= new ForeignerFactory();
        chSalary \= absFactory.CreateSalary(10000, 8000, 0.12);
        chScSc \= absFactory.CreateSocialSecurity(1200);
        Console.WriteLine(chSalary.CalculateTax());
        Console.WriteLine(chScSc.GetSocialSecurity()); //创造者模式
        Console.WriteLine("\\n创造者模式");
        Waitor waiter \= new Waitor();
        Builder b1 \= new MealABuilder();
        Builder b2 \= new MealBBuilder();

        waiter.PrepareMeal(b1);
        Meal ma \= b1.GetMeal();
        Console.WriteLine(ma.getFood() \+ "\\t" + ma.getDrink());

        waiter.PrepareMeal(b2);
        Meal mb \= b2.GetMeal();
        Console.WriteLine(mb.getFood() \+ "\\t" + mb.getDrink()); //原型模式
        Console.WriteLine("\\n原型模式");
        Red r \= new Red();
        r.o.value \= 20;//改变引用值
        ColorPrototype RCopy = r.Clone();
        RCopy.o.value \= 30;
        Console.WriteLine(r.o.value);//30 浅拷贝,指向同一个应用对象,一处改变,都改变

Green g = new Green();
g.o.value = 20;
ColorPrototype GCopy = g.Clone();
GCopy.o.value = 30;
Console.WriteLine(g.o.value);//20 深拷贝,引用对象独立 //单例模式
Console.WriteLine(“\n单例模式”);
Task[] tArr = new Task[]{
Task.Run(() => Singleton.GetInstance().count()),
Task.Run(() => Singleton.GetInstance().count()),
Task.Run(() => Singleton.GetInstance().count()),
Task.Run(() => Singleton.GetInstance().count()),
Task.Run(() => Singleton.GetInstance().count()),
Task.Run(() => Singleton.GetInstance().count()),
Task.Run(() => Singleton.GetInstance().count()),
Task.Run(() => Singleton.GetInstance().count()),
Task.Run(() => Singleton.GetInstance().count()),
Task.Run(() => Singleton.GetInstance().count())
};
Singleton.GetInstance().count();
Task.WaitAll(tArr);
Console.WriteLine(“danger:” + Singleton.GetInstance().getCnt());

        Task\[\] tArrSafe \= new Task\[\]{
        Task.Run(() \=> Singleton.GetSafeInstance().count()),
        Task.Run(() \=> Singleton.GetSafeInstance().count()),
        Task.Run(() \=> Singleton.GetSafeInstance().count()),
        Task.Run(() \=> Singleton.GetSafeInstance().count()),
        Task.Run(() \=> Singleton.GetSafeInstance().count()),
        Task.Run(() \=> Singleton.GetSafeInstance().count()),
        Task.Run(() \=> Singleton.GetSafeInstance().count()),
        Task.Run(() \=> Singleton.GetSafeInstance().count()),
        Task.Run(() \=> Singleton.GetSafeInstance().count()),
        Task.Run(() \=> Singleton.GetSafeInstance().count())
        };
        Singleton.GetSafeInstance().count();
        Task.WaitAll(tArrSafe);
        Console.WriteLine("safe:" + Singleton.GetSafeInstance().getCnt()); //迭代器
        Console.WriteLine("\\n迭代器");
        Persons ps \= new Persons(new string\[\] { "1", "2", "3" }); foreach (string name in ps)
        {
            Console.WriteLine(name);
        } for (var i = 0; i < ps.Length; i++)
        {
            Console.WriteLine(ps\[i\]);
        } //适配器模式
        Console.WriteLine("\\n适配器模式");
        EventLogger eLog \= new EventLogger();
        FileLogger fLog \= new FileLogger();
        LogAdaptor adapter \= new LogAdaptor(eLog);
        adapter.writelog("123123");
        adapter \= new LogAdaptor(fLog);
        adapter.writelog("123123"); //代理模式
        Console.WriteLine("\\n代理模式");
        Girl girl \= new Girl("Han MeiMei");
        Boy boy \= new Boy("Li Lei", girl);
        Proxy proxy \= new Proxy(boy);
        proxy.GiveFlower(); //桥接模式
        Console.WriteLine("\\n桥接模式");
        Color blue \= new Blue();
        Color white \= new White();
        Shape squre \= new Squre();
        Shape circle \= new Circle();
        squre.SetColor(blue);
        squre.Draw();
        circle.SetColor(white);
        circle.Draw(); //组合模式
        Console.WriteLine("\\n组合模式");
        Folder folder1 \= new Folder("study");
        File img \= new ImageFile("img");
        folder1.AddFile(img);
        Folder folder2 \= new Folder("c#");
        folder2.AddFile(img);
        folder1.AddFile(folder2);
        folder1.Display(); //解释器模式
        Console.WriteLine("\\n解释器模式");
        Context context \= new Context("ABcdeFG");
        UpperInterpreter ui \= new UpperInterpreter(); string inprResut = ui.Interprete(context);
        Console.WriteLine("up:" + inprResut);

        LowerInterpreter li \= new LowerInterpreter();
        inprResut \= li.Interprete(context);
        Console.WriteLine("low:" + inprResut); //外观模式
        Console.WriteLine("\\n外观模式");
        Facade facade \= new Facade();
        Console.WriteLine("电视打开,电灯关闭!");
        facade.on();
        Console.WriteLine("3秒后电灯打开,电视关闭");
        Thread.Sleep(3000);
        facade.off(); //享元模式
        Console.WriteLine("\\n享元模式");
        FlyweightFactory flyfactory \= new FlyweightFactory();
        IFlyweight flyweidht \= flyfactory.getPen("red");
        Console.WriteLine(flyweidht.GetColor());
        flyweidht \= flyfactory.getPen("blue");
        Console.WriteLine(flyweidht.GetColor());
        flyweidht \= flyfactory.getPen("red");
        Console.WriteLine(flyweidht.GetColor());
        flyfactory.Display(); //责任链模式
        Console.WriteLine("\\n责任链模式");
        Request request \= new Request(20, "wang");
        Boss boss \= new Boss();
        Department department \= new Department(boss);
        Leader leader \= new Leader(department);
        leader.HandleRequest(request); //命令模式
        Console.WriteLine("\\n命令模式");
        CDMachine cd \= new CDMachine();
        TurnoffCommand off \= new TurnoffCommand();
        TurnonCommand on \= new TurnonCommand();
        Controller ctrl \= new Controller(on, off); //遥控器发送命令到cd机

ctrl.turnOn(cd);
ctrl.turnOff(cd); //中介者模式
Console.WriteLine(“\n中介者模式”); //中介单独存在 //Mediator mediator = new ConcreteMediator();
ConcreteMediator mediator = new ConcreteMediator(); //房主和租客寻找中介
HouseOwner houseOwner = new HouseOwner(“houseowner”, mediator);
Tenant tenant = new Tenant(“tenant”, mediator); //中介给他们搭建链接
mediator.SetHouseOwner(houseOwner);
mediator.SetTenant(tenant);

        houseOwner.Contact("出租房");
        tenant.Contact("租房"); //备忘录模式
        Console.WriteLine("\\n备忘录模式");
        Caretaker caretaker \= new Caretaker();
        Original original \= new Original();
        original.blood \= 100;
        original.magic \= 100;
        original.display();
        caretaker.SetMemonto(original.SaveMemonto());
        original.blood \= 50;
        original.magic \= 50;
        original.display();
        original.RestoreMemonto(caretaker.getMemonto());
        original.display(); //观察者模式
        Console.WriteLine("\\n观察者模式");
        Subject subject \= new ConcreteSubject();
        subject.SetState("start");
        Observer o1 \= new ConcreteObserver1();
        Observer o2 \= new ConcreteObserver2();
        subject.AddObserver(o1);
        subject.AddObserver(o2);
        subject.notity();
        subject.SetState("change");
        subject.notity(); //Subject eventSubject = new EventSubjet();
        EventSubjet eventSubject = new EventSubjet();
        eventSubject.UpdateHandler += o1.Update;
        eventSubject.UpdateHandler += o2.Update;
        eventSubject.SetState("event");
        eventSubject.EventNotify(); //状态模式
        Console.WriteLine("\\n状态模式");
        Programmer programmer \= new Programmer();
        Console.WriteLine(DateTime.Now \+ "程序员正在做什么呢?");
        programmer.Doing(DateTime.Now);
        Console.WriteLine(DateTime.Now.AddHours(\-10) + "程序员正在做什么呢?");
        programmer.Doing(DateTime.Now.AddHours(\-10)); //策略模式
        Console.WriteLine("\\n策略模式");
        BubbleStrategy bubble \= new BubbleStrategy();
        SelectionStrategy selection \= new SelectionStrategy();
        InsertionStrategy insertion \= new InsertionStrategy(); var list = new List<int\>() { 3, 1, 6, 2, 5 };
        StrategyManager manager \= new StrategyManager();
        manager.SetStrategy(bubble);
        manager.Sort(list);
        manager.SetStrategy(selection);
        manager.Sort(list);
        manager.SetStrategy(insertion);
        manager.Sort(list); //模板模式
        Console.WriteLine("\\n模板模式");
        Template tea \= new Tea();
        tea.makeBeverage();
        Template coffee \= new Coffee();
        coffee.makeBeverage(); //访问者模式
        Console.WriteLine("\\n访问者模式");
        ConcreteElementA elementA \= new ConcreteElementA();
        elementA.SetName("ea");
        ConcreteElementB elementB \= new ConcreteElementB();
        elementB.SetID(2);
        objectStructure structure \= new objectStructure();
        structure.Attach(elementA);
        structure.Attach(elementB);

        Visitor visitorA \= new ConcreteVisitorA();
        Visitor visitorB \= new ConcreteVisitorB();

        structure.Accept(visitorA);
        structure.Accept(visitorB); //装饰者模式
        Console.WriteLine("\\n装饰者模式");
        Car car \= new Benz();
        car \= new ColorDecorator(car).decorate("red");
        car.run();
        car \= new CompartmentDecorator(car).decorate(3);
        car.run();
    }
}

}

View Code


该博文出发点是使用面向对象的语言,使用简单的示例简要概括说明各个模式的应用,已备不时之需。

提交了一次竟然没通过,从首页移除

重新发送一遍,是不是能通过呢? 

 为避免某某些折叠代码不能正常打开,附源码下载地址

  经过这段时间对设计模式的学习,自己的感触还是很多的,因为我现在在写代码的时候,经常会想想这里能不能用什么设计模式来进行重构。所以,学完设计模式之后,感觉它会慢慢地影响到你写代码的思维方式。这里对设计模式做一个总结,一来可以对所有设计模式进行一个梳理,二来可以做一个索引来帮助大家收藏。

  PS: 其实,很早之前我就看过所有的设计模式了,但是并没有写博客,但是不久就很快忘记了,也没有起到什么作用,这次以博客的形式总结出来,发现效果还是很明显的,因为通过这种总结的方式,我对它理解更深刻了,也记住的更牢靠了,也影响了自己平时实现功能的思维。所以,我鼓励大家可以通过做笔记的方式来把自己学到的东西进行梳理,这样相信可以理解更深,更好,我也会一直写下来,之后打算写WCF一系列文章。

  其实WCF内容很早也看过了,并且博客园也有很多前辈写的很好,但是,我觉得我还是需要自己总结,因为只有这样,知识才是自己的,别人写的多好,你看了之后,其实还是别人了,所以鼓励大家几点(对于这几点,也是对自己的一个提醒):

  1. 要动手实战别人博客中的例子;
  2. 实现之后进行总结,可以写博客也可以自己记录云笔记等;
  3. 想想能不能进行扩展,进行举一反三。

系列导航:

C#设计模式(1)——单例模式

C#设计模式(2)——简单工厂模式

C#设计模式(3)——工厂方法模式

C#设计模式(4)——抽象工厂模式

C#设计模式(5)——建造者模式(Builder Pattern)

C#设计模式(6)——原型模式(Prototype Pattern)

C#设计模式(7)——适配器模式(Adapter Pattern)

C#设计模式(8)——桥接模式(Bridge Pattern)

C#设计模式(9)——装饰者模式(Decorator Pattern)

  C#设计模式(10)——组合模式(Composite Pattern)

  C#设计模式(11)——外观模式(Facade Pattern)

  C#设计模式(12)——享元模式(Flyweight Pattern)

  C#设计模式(13)——代理模式(Proxy Pattern)

  C#设计模式(14)——模板方法模式(Template Method)

  C#设计模式(15)——命令模式(Command Pattern)

  C#设计模式(16)——迭代器模式(Iterator Pattern)

  C#设计模式(17)——观察者模式(Observer Pattern)

  C#设计模式(18)——中介者模式(Mediator Pattern)

  C#设计模式(19)——状态者模式(State Pattern)

  C#设计模式(20)——策略者模式(Stragety Pattern)

  C#设计模式(21)——责任链模式

  C#设计模式(22)——访问者模式(Vistor Pattern)

  C#设计模式(23)——备忘录模式(Memento Pattern)

  使用设计模式的根本原因是适应变化,提高代码复用率,使软件更具有可维护性和可扩展性。并且,在进行设计的时候,也需要遵循以下几个原则:单一职责原则、开放封闭原则、里氏代替原则、依赖倒置原则、接口隔离原则、合成复用原则和迪米特法则。下面就分别介绍了每种设计原则。

2.1 单一职责原则

  就一个类而言,应该只有一个引起它变化的原因。如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会影响到其他的职责,另外,把多个职责耦合在一起,也会影响复用性。

2.2 开闭原则(Open-Closed Principle)

  开闭原则即OCP(Open-Closed Principle缩写)原则,该原则强调的是:一个软件实体(指的类、函数、模块等)应该对扩展开放,对修改关闭。即每次发生变化时,要通过添加新的代码来增强现有类型的行为,而不是修改原有的代码。

  符合开闭原则的最好方式是提供一个固有的接口,然后让所有可能发生变化的类实现该接口,让固定的接口与相关对象进行交互。

2.3 里氏代替原则(Liskov Substitution Principle)

  Liskov Substitution Principle,LSP(里氏代替原则)指的是子类必须替换掉它们的父类型。也就是说,在软件开发过程中,子类替换父类后,程序的行为是一样的。只有当子类替换掉父类后,此时软件的功能不受影响时,父类才能真正地被复用,而子类也可以在父类的基础上添加新的行为。为了就来看看违反了LSP原则的例子,具体代码如下所示:

复制代码

public class Rectangle
{ public virtual long Width { get; set; } public virtual long Height { get; set; }
} // 正方形
public class Square : Rectangle
{ public override long Height
{ get { return base.Height;
} set { base.Height = value; base.Width = value;
}
} public override long Width
{ get { return base.Width;
} set { base.Width = value; base.Height = value;
}
}
} class Test
{ public void Resize(Rectangle r)
{ while (r.Height >= r.Width)
{
r.Width += 1;
}
} var r = new Square() { Width = 10, Height = 10 }; new Test().Resize(r);
}

复制代码

  上面的设计,正如上面注释的一样,在执行SmartTest的resize方法时,如果传入的是长方形对象,当高度大于宽度时,会自动增加宽度直到超出高度。但是如果传入的是正方形对象,则会陷入死循环。此时根本原因是,矩形不能作为正方形的父类,既然出现了问题,可以进行重构,使它们俩都继承于四边形类。重构后的代码如下所示:

复制代码

// 四边形
public abstract class Quadrangle
{ public virtual long Width { get; set; } public virtual long Height { get; set; }
} // 矩形
public class Rectangle : Quadrangle
{ public override long Height { get; set; } public override long Width { get; set; }

} // 正方形
public class Square : Quadrangle
{ public long \_side; public Square(long side)
    {
        \_side \= side;
    }
} class Test
{ public void Resize(Quadrangle r)
    { while (r.Height >= r.Width)
        {
            r.Width += 1;
        }
    } static void Main(string\[\] args)
    { var s = new Square(10); new Test().Resize(s);
    }
}

复制代码

2.4 依赖倒置原则

  依赖倒置(Dependence Inversion Principle, DIP)原则指的是抽象不应该依赖于细节,细节应该依赖于抽象,也就是提出的 “面向接口编程,而不是面向实现编程”。这样可以降低客户与具体实现的耦合。

2.5 接口隔离原则

  接口隔离原则(Interface Segregation Principle, ISP)指的是使用多个专门的接口比使用单一的总接口要好。也就是说不要让一个单一的接口承担过多的职责,而应把每个职责分离到多个专门的接口中,进行接口分离。过于臃肿的接口是对接口的一种污染。

2.6 合成复用原则

  合成复用原则(Composite Reuse Principle, CRP)就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分。新对象通过向这些对象的委派达到复用已用功能的目的。简单地说,就是要尽量使用合成/聚合,尽量不要使用继承。

  要使用好合成复用原则,首先需要区分”Has—A”和“Is—A”的关系。

  “Is—A”是指一个类是另一个类的“一种”,是属于的关系,而“Has—A”则不同,它表示某一个角色具有某一项责任。导致错误的使用继承而不是聚合的常见的原因是错误地把“Has—A”当成“Is—A”.例如:

实际上,雇员、经历、学生描述的是一种角色,比如一个人是“经理”必然是“雇员”。在上面的设计中,一个人无法同时拥有多个角色,是“雇员”就不能再是“学生”了,这显然不合理,因为现在很多在职研究生,即使雇员也是学生。

  上面的设计的错误源于把“角色”的等级结构与“人”的等级结构混淆起来了,误把“Has—A”当作”Is—A”。具体的解决方法就是抽象出一个角色类:

2.7 迪米特法则

  迪米特法则(Law of Demeter,LoD)又叫最少知识原则(Least Knowledge Principle,LKP),指的是一个对象应当对其他对象有尽可能少的了解。也就是说,一个模块或对象应尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立,这样当一个模块修改时,影响的模块就会越少,扩展起来更加容易。

  关于迪米特法则其他的一些表述有:只与你直接的朋友们通信;不要跟“陌生人”说话。

  外观模式(Facade Pattern)和中介者模式(Mediator Pattern)就使用了迪米特法则。

   创建型模式就是用来创建对象的模式,抽象了实例化的过程。所有的创建型模式都有两个共同点。第一,它们都将系统使用哪些具体类的信息封装起来;第二,它们隐藏了这些类的实例是如何被创建和组织的。创建型模式包括单例模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式。

  • 单例模式:解决的是实例化对象的个数的问题,比如抽象工厂中的工厂、对象池等,除了Singleton之外,其他创建型模式解决的都是 new 所带来的耦合关系。
  • 抽象工厂:创建一系列相互依赖对象,并能在运行时改变系列。
  • 工厂方法:创建单个对象,在Abstract Factory有使用到。
  • 原型模式:通过拷贝原型来创建新的对象。

  工厂方法,抽象工厂, 建造者都需要一个额外的工厂类来负责实例化“一个对象”,而Prototype则是通过原型(一个特殊的工厂类)来克隆“易变对象”。

  下面详细介绍下它们。

3.1  单例模式

   单例模式指的是确保某一个类只有一个实例,并提供一个全局访问点。解决的是实体对象个数的问题,而其他的建造者模式都是解决new所带来的耦合关系问题。其实现要点有:

  • 类只有一个实例。问:如何保证呢?答:通过私有构造函数来保证类外部不能对类进行实例化
  • 提供一个全局的访问点。问:如何实现呢?答:创建一个返回该类对象的静态方法

  单例模式的结构图如下所示:

3.2 工厂方法模式

   工厂方法模式指的是定义一个创建对象的工厂接口,由其子类决定要实例化的类,将实际创建工作推迟到子类中。它强调的是”单个对象“的变化。其实现要点有:

  • 定义一个工厂接口。问:如何实现呢?答:声明一个工厂抽象类
  • 由其具体子类创建对象。问:如何去实现呢?答:创建派生于工厂抽象类,即由具体工厂去创建具体产品,既然要创建产品,自然需要产品抽象类和具体产品类了。

  其具体的UML结构图如下所示:

  在工厂方法模式中,工厂类与具体产品类具有平行的等级结构,它们之间是一一对应关系。

3.3 抽象工厂模式

   抽象工厂模式指的是提供一个创建一系列相关或相互依赖对象的接口,使得客户端可以在不必指定产品的具体类型的情况下,创建多个产品族中的产品对象,强调的是”系列对象“的变化。其实现要点有:

  • 提供一系列对象的接口。问:如何去实现呢?答:提供多个产品的抽象接口
  • 创建多个产品族中的多个产品对象。问:如何做到呢?答:每个具体工厂创建一个产品族中的多个产品对象,多个具体工厂就可以创建多个产品族中的多个对象了。

  具体的UML结构图如下所示:

  

3.4 建造者模式

   建造者模式指的是将一个产品的内部表示与产品的构造过程分割开来,从而可以使一个建造过程生成具体不同的内部表示的产品对象。强调的是产品的构造过程。其实现要点有:

  • 将产品的内部表示与产品的构造过程分割开来。问:如何把它们分割开呢?答:不要把产品的构造过程放在产品类中,而是由建造者类来负责构造过程,产品的内部表示放在产品类中,这样不就分割开了嘛。

  具体的UML结构图如下所示:

  

3.5 原型工厂模式

   原型模式指的是通过给出一个原型对象来指明所要创建的对象类型,然后用复制的方法来创建出更多的同类型对象。其实现要点有:

  • 给出一个原型对象。问:如何办到呢?答:很简单嘛,直接给出一个原型类就好了。
  • 通过复制的方法来创建同类型对象。问:又是如何实现呢?答:.NET可以直接调用MemberwiseClone方法来实现浅拷贝

  具体的UML结构图如下所示:

   结构型模式,顾名思义讨论的是类和对象的结构 ,主要用来处理类或对象的组合。它包括两种类型,一是类结构型模式,指的是采用继承机制来组合接口或实现;二是对象结构型模式,指的是通过组合对象的方式来实现新的功能。它包括适配器模式、桥接模式、装饰者模式、组合模式、外观模式、享元模式和代理模式。

  • 适配器模式注重转换接口,将不吻合的接口适配对接 
  • 桥接模式注重分离接口与其实现,支持多维度变化 
  • 组合模式注重统一接口,将“一对多”的关系转化为“一对一”的关系 
  • 装饰者模式注重稳定接口,在此前提下为对象扩展功能 
  • 外观模式注重简化接口,简化组件系统与外部客户程序的依赖关系 
  • 享元模式注重保留接口,在内部使用共享技术对对象存储进行优化 
  • 代理模式注重假借接口,增加间接层来实现灵活控制

4.1 适配器模式

   适配器模式意在转换接口,它能够使原本不能再一起工作的两个类一起工作,所以经常用来在类库的复用、代码迁移等方面。例如DataAdapter类就应用了适配器模式。适配器模式包括类适配器模式和对象适配器模式,具体结构如下图所示,左边是类适配器模式,右边是对象适配器模式。

4.2 桥接模式

   桥接模式旨在将抽象化与实现化解耦,使得两者可以独立地变化。意思就是说,桥接模式把原来基类的实现化细节再进一步进行抽象,构造到一个实现化的结构中,然后再把原来的基类改造成一个抽象化的等级结构,这样就可以实现系统在多个维度的独立变化,桥接模式的结构图如下所示。

4.3 装饰者模式

   装饰者模式又称包装(Wrapper)模式,它可以动态地给一个对象添加一些额外的功能,装饰者模式较继承生成子类的方式更加灵活。虽然装饰者模式能够动态地将职责附加到对象上,但它也会造成产生一些细小的对象,增加了系统的复杂度。具体的结构图如下所示。

4.4 组合模式

   组合模式又称为部分—整体模式。组合模式将对象组合成树形结构,用来表示整体与部分的关系。组合模式使得客户端将单个对象和组合对象同等对待。如在.NET中WinForm中的控件,TextBox、Label等简单控件继承与Control类,同时GroupBox这样的组合控件也是继承于Control类。组合模式的具体结构图如下所示。

4.5 外观模式

  在系统中,客户端经常需要与多个子系统进行交互,这样导致客户端会随着子系统的变化而变化,此时可以使用外观模式把客户端与各个子系统解耦。外观模式指的是为子系统中的一组接口提供一个一致的门面,它提供了一个高层接口,这个接口使子系统更加容易使用。如电信的客户专员,你可以让客户专员来完成冲话费,修改套餐等业务,而不需要自己去与各个子系统进行交互。具体类结构图如下所示:

4.6 享元模式

   在系统中,如何我们需要重复使用某个对象时,此时如果重复地使用new操作符来创建这个对象的话,这对系统资源是一个极大的浪费,既然每次使用的都是同一个对象,为什么不能对其共享呢?这也是享元模式出现的原因。

  享元模式运用共享的技术有效地支持细粒度的对象,使其进行共享。在.NET类库中,String类的实现就使用了享元模式,String类采用字符串驻留池的来使字符串进行共享。更多内容参考博文:http://www.cnblogs.com/artech/archive/2010/11/25/internedstring.html。享元模式的具体结构图如下所示。

4.7 代理模式

   在系统开发中,有些对象由于网络或其他的障碍,以至于不能直接对其访问,此时可以通过一个代理对象来实现对目标对象的访问。如.NET中的调用Web服务等操作。

  代理模式指的是给某一个对象提供一个代理,并由代理对象控制对原对象的访问。具体的结构图如下所示。

  

  注:外观模式、适配器模式和代理模式区别?

  解答:这三个模式的相同之处是,它们都是作为客户端与真实被使用的类或系统之间的一个中间层,起到让客户端间接调用真实类的作用,不同之处在于,所应用的场合和意图不同。

  代理模式与外观模式主要区别在于,代理对象无法直接访问对象,只能由代理对象提供访问,而外观对象提供对各个子系统简化访问调用接口,而适配器模式则不需要虚构一个代理者,目的是复用原有的接口。外观模式是定义新的接口,而适配器则是复用一个原有的接口。

  另外,它们应用设计的不同阶段,外观模式用于设计的前期,因为系统需要前期就需要依赖于外观,而适配器应用于设计完成之后,当发现设计完成的类无法协同工作时,可以采用适配器模式。然而很多情况下在设计初期就要考虑适配器模式的使用,如涉及到大量第三方应用接口的情况;代理模式是模式完成后,想以服务的方式提供给其他客户端进行调用,此时其他客户端可以使用代理模式来对模块进行访问。

  总之,代理模式提供与真实类一致的接口,旨在用来代理类来访问真实的类,外观模式旨在简化接口,适配器模式旨在转换接口。

   行为型模式是对在不同对象之间划分责任和算法的抽象化。行为模式不仅仅关于类和对象,还关于它们之间的相互作用。行为型模式又分为类的行为模式和对象的行为模式两种。

  • 类的行为模式——使用继承关系在几个类之间分配行为。
  • 对象的行为模式——使用对象聚合的方式来分配行为。

  行为型模式包括11种模式:模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、状态模式、策略模式、责任链模式、访问者模式、解释器模式和备忘录模式。

  • 模板方法模式:封装算法结构,定义算法骨架,支持算法子步骤变化。
  • 命令模式:注重将请求封装为对象,支持请求的变化,通过将一组行为抽象为对象,实现行为请求者和行为实现者之间的解耦。
  • 迭代器模式:注重封装特定领域变化,支持集合的变化,屏蔽集合对象内部复杂结构,提供客户程序对它的透明遍历。
  • 观察者模式:注重封装对象通知,支持通信对象的变化,实现对象状态改变,通知依赖它的对象并更新。
  • 中介者模式:注重封装对象间的交互,通过封装一系列对象之间的复杂交互,使他们不需要显式相互引用,实现解耦。
  • 状态模式:注重封装与状态相关的行为,支持状态的变化,通过封装对象状态,从而在其内部状态改变时改变它的行为。
  • 策略模式:注重封装算法,支持算法的变化,通过封装一系列算法,从而可以随时独立于客户替换算法。
  • 责任链模式:注重封装对象责任,支持责任的变化,通过动态构建职责链,实现事务处理。
  • 访问者模式:注重封装对象操作变化,支持在运行时为类结构添加新的操作,在类层次结构中,在不改变各类的前提下定义作用于这些类实例的新的操作。
  • 备忘录模式:注重封装对象状态变化,支持状态保存、恢复。
  • 解释器模式:注重封装特定领域变化,支持领域问题的频繁变化,将特定领域的问题表达为某种语法规则下的句子,然后构建一个解释器来解释这样的句子,从而达到解决问题的目的。

5.1 模板方法模式

   在现实生活中,有论文模板,简历模板等。在现实生活中,模板的概念是给定一定的格式,然后其他所有使用模板的人可以根据自己的需求去实现它。同样,模板方法也是这样的。

  模板方法模式是在一个抽象类中定义一个操作中的算法骨架,而将一些具体步骤实现延迟到子类中去实现。模板方法使得子类可以不改变算法结构的前提下,重新定义算法的特定步骤,从而达到复用代码的效果。具体的结构图如下所示。

以生活中做菜为例子实现的模板方法结构图

5.2 命令模式

   命令模式属于对象的行为模式,命令模式把一个请求或操作封装到一个对象中,通过对命令的抽象化来使得发出命令的责任和执行命令的责任分隔开。命令模式的实现可以提供命令的撤销和恢复功能。具体的结构图如下所示。

5.3 迭代器模式

   迭代器模式是针对集合对象而生的,对于集合对象而言,必然涉及到集合元素的添加删除操作,也肯定支持遍历集合元素的操作,此时如果把遍历操作也放在集合对象的话,集合对象就承担太多的责任了,此时可以进行责任分离,把集合的遍历放在另一个对象中,这个对象就是迭代器对象。

  迭代器模式提供了一种方法来顺序访问一个集合对象中各个元素,而又无需暴露该对象的内部表示,这样既可以做到不暴露集合的内部结构,又可以让外部代码透明地访问集合内部元素。具体的结构图如下所示。

5.4 观察者模式

   在现实生活中,处处可见观察者模式,例如,微信中的订阅号,订阅博客和QQ微博中关注好友,这些都属于观察者模式的应用。

  观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己的行为。具体结构图如下所示:

5.5 中介者模式

   在现实生活中,有很多中介者模式的身影,例如QQ游戏平台,聊天室、QQ群和短信平台,这些都是中介者模式在现实生活中的应用。

  中介者模式,定义了一个中介对象来封装一系列对象之间的交互关系。中介者使各个对象之间不需要显式地相互引用,从而使耦合性降低,而且可以独立地改变它们之间的交互行为。具体的结构图如下所示:

5.6 状态模式

   每个对象都有其对应的状态,而每个状态又对应一些相应的行为,如果某个对象有多个状态时,那么就会对应很多的行为。那么对这些状态的判断和根据状态完成的行为,就会导致多重条件语句,并且如果添加一种新的状态时,需要更改之前现有的代码。这样的设计显然违背了开闭原则,状态模式正是用来解决这样的问题的。

  状态模式——允许一个对象在其内部状态改变时自动改变其行为,对象看起来就像是改变了它的类。具体的结构图如下所示:

5.7 策略模式

   在现实生活中,中国的所得税,分为企业所得税、外商投资企业或外商企业所得税和个人所得税,针对于这3种所得税,每种所计算的方式不同,个人所得税有个人所得税的计算方式,而企业所得税有其对应计算方式。如果不采用策略模式来实现这样一个需求的话,我们会定义一个所得税类,该类有一个属性来标识所得税的类型,并且有一个计算税收的CalculateTax()方法,在该方法体内需要对税收类型进行判断,通过if-else语句来针对不同的税收类型来计算其所得税。这样的实现确实可以解决这个场景,但是这样的设计不利于扩展,如果系统后期需要增加一种所得税时,此时不得不回去修改CalculateTax方法来多添加一个判断语句,这样明白违背了“开放——封闭”原则。此时,我们可以考虑使用策略模式来解决这个问题,既然税收方法是这个场景中的变化部分,此时自然可以想到对税收方法进行抽象,这也是策略模式实现的精髓所在。

  策略模式是对算法的包装,是把使用算法的责任和算法本身分割开,委派给不同的对象负责。策略模式通常把一系列的算法包装到一系列的策略类里面。用一句话慨括策略模式就是——“将每个算法封装到不同的策略类中,使得它们可以互换”。下面是策略模式的结构图:

  

5.8 责任链模式

   在现实生活中,有很多请求并不是一个人说了就算的,例如面试时的工资,低于1万的薪水可能技术经理就可以决定了,但是1万~1万5的薪水可能技术经理就没这个权利批准,可能需要请求技术总监的批准。

  责任链模式——某个请求需要多个对象进行处理,从而避免请求的发送者和接收之间的耦合关系。将这些对象连成一条链子,并沿着这条链子传递该请求,直到有对象处理它为止。具体结构图如下所示:

5.9 访问者模式

   访问者模式是封装一些施加于某种数据结构之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构则可以保存不变。访问者模式适用于数据结构相对稳定的系统, 它把数据结构和作用于数据结构之上的操作之间的耦合度降低,使得操作集合可以相对自由地改变。具体结构图如下所示:

5.10 备忘录模式

   生活中的手机通讯录备忘录,操作系统备份点,数据库备份等都是备忘录模式的应用。备忘录模式是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以把该对象恢复到原先的状态。具体的结构图如下所示:

5.11 解释器模式

   解释器模式是一个比较少用的模式,所以我自己也没有对该模式进行深入研究,在生活中,英汉词典的作用就是实现英文和中文互译,这就是解释器模式的应用。

  解释器模式是给定一种语言,定义它文法的一种表示,并定义一种解释器,这个解释器使用该表示来解释器语言中的句子。具体的结构图如下所示:

   23种设计模式,其实前辈们总结出来解决问题的方式,它们追求的宗旨还是保证系统的低耦合高内聚,指导它们的原则无非就是封装变化,责任单一,面向接口编程等设计原则。之后,我会继续分享自己WCF的学习过程,尽管博客园中有很多WCF系列,之前觉得没必要写,觉得会用就行了,但是不写,总感觉知识不是自己的,感觉没有深入,所以还是想写这样一个系列,希望各位博友后面多多支持。

  PS: 很多论坛都看到初学者问,WCF现在还有没有必要深入学之类的问题,因为他们觉得这些技术可能会过时,说不定到时候微软又推出了一个新的SOA的实现方案了,那岂不是白花时间深入学了,所以就觉得没必要深入去学,知道用就可以了。对于这个问题,我之前也有这样同样的感觉,但是现在我觉得,尽管WCF技术可能会被替换,但深入了解一门技术,重点不是知道一些更高深API的调用啊,而是了解它的实现机制和思维方式,即使后面这个技术被替代了,其背后机制也肯定是相似的。所以深入了解了一个技术,你就会感觉新的技术熟悉,对其感觉放松。并且,你深入了解完一门技术之后,你面试时也敢说你很好掌握了这门技术,而不至于说平时使用的很多,一旦深入问时却不知道背后实现原理。这也是我要写WCF系列的原因。希望这点意见对一些初学者有帮助。

一、引言

在软件开发过程中,有些对象有时候会由于网络或其他的障碍,以至于不能够或者不能直接访问到这些对象,如果直接访问对象给系统带来不必要的复杂性,这时候可以在客户端和目标对象之间增加一层中间层,让代理对象代替目标对象,然后客户端只需要访问代理对象,由代理对象去帮我们去请求目标对象并返回结果给客户端,这样的一个解决思路就是今天要介绍的代理模式。

二、代理模式的详细介绍

代理模式按照使用目的可以分为以下几种:

  • 远程(Remote)代理:为一个位于不同的地址空间的对象提供一个局域代表对象。这个不同的地址空间可以是本电脑中,也可以在另一台电脑中。最典型的例子就是——客户端调用Web服务或WCF服务。
  • 虚拟(Virtual)代理:根据需要创建一个资源消耗较大的对象,使得对象只在需要时才会被真正创建。
  • Copy-on-Write代理:虚拟代理的一种,把复制(或者叫克隆)拖延到只有在客户端需要时,才真正采取行动。
  • 保护(Protect or Access)代理:控制一个对象的访问,可以给不同的用户提供不同级别的使用权限。
  • 防火墙(Firewall)代理:保护目标不让恶意用户接近。
  • 智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等。
  • Cache代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以这些结果。

在哦上面所有种类的代理模式中,虚拟代理、远程代理、智能引用代理和保护代理较为常见的代理模式。下面让我们具体看看代理模式的具体定义。

2.1 定义

代理模式——就是给某一个对象提供一个代理,并由代理对象控制对原对象的引用。在一些情况下,一个客户不想或者不能直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。例如电脑桌面的快捷方式就是一个代理对象,快捷方式是它所引用的程序的一个代理

2.2 代理模式实现

看完代理模式的描述之后,下面以一个生活中的例子来解释下代理模式,在现实生活中,如果有同事出国或者朋友出国的情况下,我们经常会拖这位朋友帮忙带一些电子产品或化妆品等东西,这个场景中,出国的朋友就是一个代理,他(她)是他(她)朋友的一个代理,由于他朋友不能去国外买东西,他却可以,所以朋友们都托他帮忙带一些东西的。下面就以这个场景来实现下代理模式,具体代码如下:

复制代码

// 客户端调用
class Client
{ static void Main(string[] args)
{ // 创建一个代理对象并发出请求
Person proxy = new Friend();
proxy.BuyProduct();
Console.Read();
}
} // 抽象主题角色
public abstract class Person
{ public abstract void BuyProduct();
} //真实主题角色
public class RealBuyPerson : Person
{ public override void BuyProduct()
{
Console.WriteLine(“帮我买一个IPhone和一台苹果电脑”);
}
} // 代理角色
public class Friend:Person
{ // 引用真实主题实例
RealBuyPerson realSubject; public override void BuyProduct()
{
Console.WriteLine(“通过代理类访问真实实体对象的方法”); if (realSubject == null)
{
realSubject = new RealBuyPerson();
} this.PreBuyProduct(); // 调用真实主题方法
realSubject.BuyProduct(); this.PostBuyProduct();
} // 代理角色执行的一些操作
public void PreBuyProduct()
{ // 可能不知一个朋友叫这位朋友带东西,首先这位出国的朋友要对每一位朋友要带的东西列一个清单等
Console.WriteLine(“我怕弄糊涂了,需要列一张清单,张三:要带相机,李四:要带Iphone………..”);
} // 买完东西之后,代理角色需要针对每位朋友需要的对买来的东西进行分类
public void PostBuyProduct()
{
Console.WriteLine(“终于买完了,现在要对东西分一下,相机是张三的;Iphone是李四的……….”);
}
}

复制代码

在上面的代码中都有相应的注释,这里也不多解释了。

2.3 代理模式的类图结构

看完代理模式的实现之后,下面就以上面的例子来分析下代理模式的类图结构。具体的类图如下所示:

在上面类图中,代理模式所涉及的角色有三个:

抽象主题角色(Person):声明了真实主题和代理主题的公共接口,这样一来在使用真实主题的任何地方都可以使用代理主题。

代理主题角色(Friend):代理主题角色内部含有对真实主题的引用,从而可以操作真实主题对象;代理主题角色负责在需要的时候创建真实主题对象;代理角色通常在将客户端调用传递到真实主题之前或之后,都要执行一些其他的操作,而不是单纯地将调用传递给真实主题对象。例如这里的PreBuyProduct和PostBuyProduct方法就是代理主题角色所执行的其他操作。

真实主题角色(RealBuyPerson):定义了代理角色所代表的真是对象。

附:在实际开发过程中,我们在客户端添加服务引用的时候,在客户程序中会添加一些额外的类,在客户端生成的类扮演着代理主题角色,我们客户端也是直接调用这些代理角色来访问远程服务提供的操作。这个是远程代理的一个典型例子。

三、代理模式的优缺点

全面分析完代理模式之后,让我们看看这个模式的优缺点:

优点:

  1. 代理模式能够将调用用于真正被调用的对象隔离,在一定程度上降低了系统的耦合度;
  2. 代理对象在客户端和目标对象之间起到一个中介的作用,这样可以起到对目标对象的保护。代理对象可以在对目标对象发出请求之前进行一个额外的操作,例如权限检查等。

缺点:

  1.  由于在客户端和真实主题之间增加了一个代理对象,所以会造成请求的处理速度变慢
  2. 实现代理类也需要额外的工作,从而增加了系统的实现复杂度。

五、总结

到这里,代理模式的介绍就结束了,代理模式提供了对目标对象访问的代理。并且到这里,结构型模式的介绍也结束了,结构型模式包括:适配器模式桥接模式装饰者模式组合模式外观模式享元模式和代理模式,下面开始介绍行为型模式的第一个模式:模板方法模式。

  本专题所有源码:设计模式之代理模式源码