行为性设计模式:对象之间的通信。
职责链:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。
命令模式:用简单的对象表示软件命令的执行,支持登录和取消操作。
解释器模式:提供一个如何把语言元素包括在程序中的定义。
迭代器模式:提供一种顺序访问一个类中一系列数据的方式。
中介者模式:定义了如何用一个对象简化对象之间的通信,使对象之间不必相互了解。
备忘录模式:定义了如何保存一个类的实例内容,以便以后恢复它。
观察者模式:定义了一种把改动通知给多个对象的方式。
状态模式:允许一个对象在其内部状态改变时修改它的行为。
策略模式:将算法封装到类中。
模板方法模式:提供算法的一个抽象定义。
访问者模式:在不改变类的前提下,为一个类添加多种操作。
职责链: 允许多个类处理同一个请求,而不必了解彼此的功能。他在类之间提供一个松散的耦合。类之间唯一的联系就是相互之间的传递请求。请求在类之间传递,直到其中一个类处理它为止。当一个对象向多个对象发送相同的信息时,就需要一种策略来确定由哪个对象对所发送的信息进行处理,而这样的处理对象也只能有一个。使用case语句或if语句的方法会给程序的维护带来很大难度,这就需要职责链模式来完成。职责链模式将发送对象和接收对象进行了解耦,以更好的应对变化。职责链模式将接收对象形成一个链,发送对象将信息发送给接收对象链中的一个对象,这时,信息就沿着对象链向下传送,直到有一个对象对信息进行处理。
意图:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系.
将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止.
角色及职责:
实现:
职责链由其Parent属性而形成,使其信息一直向上发送,直到被处理。
利用其构造函数形成的职责链。消息发送时,如果接收对象不能处理就直接发送给其obj属性进行处理即可,如果其存在的话。
优点:松散耦合
缺点:效率低下,扩展性差
适用范围:
附注:如果请求传递到职责链的末尾依然得不到处理,应该有一个合理的缺省机制,这也是每一个接受对象的责任,而不是发出请求的对象的责任。应用了chain of responsibility模式后,对象的职责分派将更具灵活性。我们可以在运行时动态添加修改请求的处理职责。
命令模式:把申请特定操作的请求封装到一个对象中,并给对象一个众所周知的接口。命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。每一个命令都是一个操作:请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行操作。命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。
意图:把发出命令的责任和执行命令的责任分割开,委派给不同的对象。
角色及职责:
实现:轻:命令只是提供了一个请求者和接收者之间的耦合而己,命令代表请求者实现请求。重:命令实现所有的细节,包括请求所代表的操作,而不再需要接收者了。命令类动态地决定调用哪一个接收者类。其次是否支持undo和redo。若一个命令类提供一个方法,比如叫unExecute(),以恢复其操作的效果,那么命令类就可以支持undo和redo。具体命令类需要存储状态信息,包括:接收者对象实际上实施请求所代表的操作、 对接收者对象所作的操作所需要的参数、接收者类的最初的状态。接收者必须提供适当的方法,使命令类可以通过调用这个方法,以便接收者类恢复原有状态。如果只需要提供一层的undo和redo,那么系统只需要存储最后被执行的那个命令对象。如果需要支持多层的undo和redo,那么系统就需要存储曾经被执行过的命令的清单,清单能允许的最大的长度便是系统所支持的undo和redo的层数。沿着清单逆着执行清单上的命令的反命令(unExecute())便是undo;沿着清单顺着执行清单上的命令便是redo。
适用范围:
优点:
缺点:
附注:可以使用Map代替命令类。在invoke中可以轻松实现跟踪与日志。
解释器模式:定义语言的文法,并且建立一个解释器来解释该语言中的句子。它属于类的行为模式。这里的语言意思是使用规定格式和语法的代码。如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。而且当文法简单、效率不是关键问题的时候效果最好。
意图:3
角色及职责:
a) 实现与文法中的终结符相关联的解释操作
b) 而且句子中的每个终结符需要该类的一个实例与之对应
a) 文法中的每条规则R::=R1R2…Rn都需要一个非终结符表带式角色
b) 对于从R1到Rn的每个符号都维护一个抽象表达式角色的实例变量
c) 实现解释操作,解释一般要递归地调用表示从R1到Rn的那些对象的解释操作
a) 构建(或者被给定)表示该文法定义的语言中的一个特定的句子的抽象语法树
b) 调用解释操作
优点:解释器模式提供了一个简单的方式来执行语法,而且容易修改或者扩展语法。一般系统中很多类使用相似的语法,可以使用一个解释器来代替为每一个规则实现一个解释器。而且在解释器中不同的规则是由不同的类来实现的,这样使得添加一个新的语法规则变得简单。
缺点:解释器模式对于复杂文法难以维护。每一个规则要对应一个处理类,而且类递归调用抽象表达式角色,关系混乱。
适用范围:
附注:解释器模式并没有说明如何创建一个抽象语法树,因此它的实现可以多种多样,在上面我们是直接在Test中提供的,当然还有更好、更专业的实现方式。对于终结符,GOF建议采用享元模式来共享它们的拷贝,因为它们要多次重复出现。但是考虑到享元模式的使用局限性,故系统中终结符重复的足够多的时候再考虑享元模式。
迭代器模式:提供一种方式可以连续的访问几个对象的所有元素而无须关注内在的描述方式。
角色及职责:
功能:
分类:
实现:
适用范围:
中介者模式:用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它 们之间的交互。简单点来说,将原来两个直接引用或者依赖的对象拆开,在中间加入一个"中介"对象,使得两头的对象分别和"中介"对象引用或者依赖。由于中介者的行为与要使用的数据与具体业务紧密相关,抽象中介者角色提供一个能方便很多对象使用的接口是不太现实的。所以抽象中介者角色往往是不存在 的,或者只是一个标示接口。如果有幸能够提炼出真正带有行为的抽象中介者角色,我想同事角色对具体中介者角色的选择也是策略的一种应用。
意图:用一个中介对象来封装一系列的对象交互。
角色及职责:
中介者模式和外观模式的对比:
优点:
缺点:
备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态
角色及职责:
优点:使用备忘录模式,可以避免暴露一些只应由源发器管理却又必须存储在源发器之外的信息,而且能够在对象需要时恢复到先前的状态。
缺点:使用备忘录可能代价很高。如果源发器在生成备忘录时必须复制并存储大量的信息,或者客户非常频繁地创建备忘录和恢复源发器状态,可能会导致非常大的开销。
适用范围:主要搭配命令方式使用。
观察者模式:定义了一种把改动通知给多个对象的方式。
意图:降观察着雨被观察者完美的分开。
角色及职责:
适用范围:
状态模式:允许一个对象在其内部状态改变时修改它的行为。
意图:
角色及职责:
实现:
策略模式:将算法封装到类中。定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
意图:使得类独立于客户而存在。在运行时根据需要透明地更改对象的算法,将算法与对象本身解耦。
角色及职责:
适用范围:
优点:
缺点:
模板方法模式:提供算法的一个抽象定义。在一个方法里定义算法的骨架,将一些步骤延迟到其子类。
意图:
角色及职责:
访问者模式:在不改变类的前提下,为一个类添加多种操作。(最常见的操作模式,KJ70必须要用到)
意图:将算法与对象结构分离。
基本思想:先我们拥有一个由许多对象构成的对象结构,这些对象的类都拥有一个accept方法用来接受访问者对象;访问者是一个接口,它拥有一个visit方法,这个方法对访问到的对象结构中不同类型的元素做出不同的反应;在对象结构的一次访问过程中,我们遍历整个对象结构,对每一个元素都实施accept方法,在每一个元素的accept方法中回调访问者的visit方法,从而使访问者得以处理对象结构的每一个元素。我们可以针对对象结构设计不同的实在的访问者类来完成不同的操作。访问者模式使得我们可以在传统的单分派语言(如Smalltalk、Java和C++)中模拟双分派技术。对于支持多分派的语言(如CLOS),访问者模式已经内置于语言特性之中了,从而不再重要。
角色及职责:
适用范围:
预料到对于基类有可能添加新的操作,但无法预料具体操作种类。
对象池模式:一个对象池是一组已经初始化的可以使用的对象, 而不需要在需要时创建和销毁. 池的客户从池中获取一个对象, 并对其进行操作. 客户使用完对象后, 把对象交还给池, 而不是销毁它. 这是一种特殊的工厂对象. 在初始化类实例代价高, 经常实例化类, 一次实例化数少的情况下, 对象池能带来显著的性能提升. 从池中取对象的时间是可预测的, 而新建一个对象所需的时间是不确定的
结构模式描述的是如何和组合类和对象已获得更大的结构。类模式和对象模式之间的区别是:类描述的如何通过继承提供更有用的接口,而对象描述的是通过使用对象的组合或将对象包涵在别的对象里面以获得更有用的结构。
适配器模式:可以将类的一个借口匹配另一个接口
组合模式:对象的组合
代理模式:一个简单的对象代替一个复杂的稍后会被调用的复杂对象
外观模式:一个类表示一个子系统
享元模式:用于共享对象,其中每个实例都不保存自己的状态。而是将状态保存在外部
桥接模式:将对象的接口与实现分离
装饰模式:动态给对象添加职责
适配器模式:将一个类的程序设计接口转换成另一个接口。
定义:编写一个具有所需要接口的类,由他和拥有不同接口的类进行通信
意图:复用已存在的接口与所需接口不一致的类
实现:
两者比较:
角色及职责:
UML图:
补充:更高层次的适配器
从左图可以看出:Adapter将Adaptee接口适配为客户Client需要的接口Target,这样在整个系统中所有实现Adaptee接口的类都可以通过Adapter适配为Target对象,从而避免为每一个类都写一个适配器。后面会给大家带来一个JDK中使用此中适配的例子。我们不仅仅可以象上面一样对接口进行适配,也可以对抽象类进行适配!主要是根据系统的需求,确定此时的场景是否适合使用适配器模式!
适用范围:
桥接模式:
意图:将抽象部分与实现部分分离,使得她们两个部分可以独立的变化。
等级结构:
角色及职责:
优势和缺陷:
桥接模式可以从接口分离实现功能,使得设计更具有扩展性,这样,客户调用方法是根本不需要知道实现的细节。桥接默哀是减少了子类,如果程序中要在2个操作系统中实现查看6种图像格式,那么就会有2*6个类。使用桥接模式时候就会变成2+6个类了,,它使代码变得更清洁了,生成的执行程序更小了。但是桥接模式的缺陷是抽象类与实现类的双向连接使得运行速度更慢了。
适用场景:
组合模式:组合模式有时候又叫做部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
意图:将对象组合成树形结构以表示"部分-整体"的层次结构。Composite模式使得用户对单个对象和组合对象的使用具有一致性。
透明模式
安全模式
分类:
效果及实现要点:
适用性:
装饰模式:动态地给对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更灵活。装饰模式是为已有的功能动态地添加更多功能的一种方式。在起初的设计当中,当系统需要新功能的时候,是向旧的类中添加新的代码,这些新加的代码,通常装饰了原有类的核心职责或主要行为,在主类中加入了新的字段,新的方法和新的逻辑,从而增加了主类的复杂度,而这些新加入的东西仅仅是为了满足一些只在某种特定情况下才会执行的特殊行为的需要。而装饰模式却提供了一个非常好的解决方案,它把每个要装饰的功能都放在单纯的类中,并让这个类包装它所要装饰的对象(就是抽象类Decorator中的Component字段),因此,当需要执行特殊行为时,客户代码就可以在运行时根据需要有选择地、按顺序地使用装饰功能包装对象了。
角色及职责:
特点:
适用范围:
优点:
缺点:
模式实现的
装饰模式通常要求针对抽象编程。装饰模式对客户端的透明性要求程序不要声明一个ConcreteDecorator类型的变量,而应当声明一个Component类型的变量。换言之,下面的做法是对的:
Component c = new ConcreteComponent();
Component c1 = new ConcreteDecorator1(c);
Component c2 = new ConcreteDecorator(c1);
而下面的做法是不对的:
ConcreteComponent c = new ConcreteDecorator();
这就是前面所说的,装饰模式对客户端是完全透明的含义。
然而,纯粹的装饰模式很难找到。装饰模式的用意是在不改变接口的前提下,增强所考虑的类的性能。在增强性能的时候,往往需要建立新的公开的方法。即便是在孙大圣的系统里,也需要新的方法。比如齐天大圣类并没有飞行的能力,而雀儿有。这就意味着雀儿应当有一个新的fly()方法。这就导致了大多数的装饰模式的实现都是"半透明"(semi-transparent)的,而不是完全"透明"的。换言之,允许装饰模式改变接口,增加新的方法。即声明ConcreteDecorator类型的变量,从而可以调用ConcreteDecorator类中才有的方法。
外观模式:
意图:为子系统提供了一个更高层次、更简单的接口,从而降低了子系统的复杂度和依赖。这使得子系统更易于使用和管理。对外提供一个统一的接口用来访问子系统中的一群接口。外观是一个能为子系统和客户提供简单接口的类。当正确的应用外观,客户不再直接和子系统中的类交互,而是与外观交互。外观承担与子系统中类交互的责任。实际上,外观是子系统与客户的接口,这样外观模式降低了子系统和客户的耦合度。外观对象隔离了客户和子系统对象,从而降低了耦合度。当子系统中的类进行改变时,客户端不会像以前一样受到影响。
角色及职责:
适用范围:
享元模式:用一个共享来避免大量拥有相同内容对象的开销。这种开销中最常见、直观的就是内存的损耗。享元模式以共享的方式高效的支持大量的细粒度对象。
对象的状态:
适用范围:
分类:
角色及职责:
1>:抽象享元(Flyweight)角色:此角色是所有的具体享元类的超类,为这些类规定出需要实现的公共接口。那些需要外蕴状态(External State)的操作可以通过调用商业方法以参数形式传入
/// <summary>
/// "Flyweight"
/// </summary>
abstract class Flyweight
{
// Methods
/// <summary>
/// 抽象享元对象的商业方法
/// </summary>
/// <param name="extrinsicstate">外蕴状态</param>
abstract public void Operation(int extrinsicstate);
}
2>:具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。享元对象的内蕴状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享的。
/// <summary>
/// "ConcreteFlyweight"
/// </summary>
class ConcreteFlyweight : Flyweight
{
private string intrinsicstate = "A";
// Methods
override public void Operation(int extrinsicstate)
{
Console.WriteLine("ConcreteFlyweight: intrinsicstate {0}, extrinsicstate {1}",intrinsicstate, extrinsicstate);
}
}
3>:享元工厂(FlyweightFactory)角色:本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象调用一个享元对象的时候,享元工厂角色会检查系统中是否已经有一个符合要求的享元对象。如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个合适的享元对象。
注意:客户端不可以直接实例化享元类,必须通过享元工厂类来创建,因为享元工厂类在系统中只能有一个,所以可以结合单件模式来改善。当客户端需要单纯享元对象时,需要调用享元工厂的Singleton()方法,此时工厂会取得所有的单纯享元对象,然后传入所需的单纯享元对象的内蕴状态,工厂方法负责产生所需要的享元对象。
/// <summary>
/// "FlyweightFactory"
/// </summary>
class FlyweightFactory
{
// Fields
private Dictionary<string, Flyweight> flyweights = new Dictionary<string, Flyweight>();
private static readonly FlyweightFactory instance = new FlyweightFactory();
/// <summary>
/// Constructors
/// </summary>
private FlyweightFactory()
{
}
// Methods
/// <summary>
/// 从享元工厂中生产出一个具体的享元对象
/// </summary>
/// <param name="key">内蕴状态</param>
/// <returns></returns>
public Flyweight GetFlyweight(string key)
{
return ((Flyweight)flyweights[key]);
}
/// <summary>
/// 享元工厂单例方法
/// </summary>
/// <returns></returns>
public static FlyweightFactory Singleton()
{
return FlyweightFactory.instance;
}
/// <summary>
/// 向享元工厂对象增加一个享元对象
/// </summary>
/// <param name="sKey">内蕴状态</param>
/// <param name="_Flyweight">具体享元对象</param>
public void AddFlyweight(string sKey, Flyweight _Flyweight)
{
flyweights.Add(sKey , _Flyweight);
}
public Flyweight factory(string sKey)
{
if (flyweights.ContainsKey(sKey))
{
return this.GetFlyweight(sKey);
}
else
{
this.AddFlyweight(sKey, new ConcreteFlyweight());
return this.GetFlyweight(sKey);
}
}
}
4>:客户端(Client)角色:需要维护一个对所有享元对象的引用;需要自行存储所有享元对象外蕴状态。
// 初始化外蕴状态值
int extrinsicstate = 22;
//享元工厂对象使用单例
FlyweightFactory f = FlyweightFactory.Singleton () ;
//调用过程
//向享元工厂对象请求一个内蕴状态为"X"的单纯享元对象
Flyweight fx = f.factory("X");
//调用X的商业方法,X的外蕴状态值为21
fx.Operation(--extrinsicstate);
Flyweight fy = f.factory("Y");
fy.Operation(--extrinsicstate);
Flyweight fz = f.factory("Z");
fz.Operation(--extrinsicstate);
1>:抽象享元角色:此角色是所有的具体享元类的超类,为这些类规定出需要实现的公共接口。那些需要外蕴状态(External State)的操作可以通过方法的参数传入。抽象享元的接口使得享元变得可能,但是并不强制子类实行共享,因此并非所有的享元对象都是可以共享的。
2>:具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。享元对象的内蕴状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享。有时候具体享元角色又叫做单纯具体享元角色,因为复合享元角色是由单纯具体享元角色通过复合而成的。
3>:复合享元(UnsharableFlyweight)角色:复合享元角色所代表的对象是不可以共享的,但是一个复合享元对象可以分解成为多个本身是单纯享元对象的组合。复合享元角色又称作不可共享的享元对象。
4>:享元工厂(FlyweightFactoiy)角色:本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象请求一个享元对象的时候,享元工厂角色需要检查系统中是否已经有一个符合要求的享元对象,如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个新的合适的享元对象。
5>:客户端(Client)角色:本角色还需要自行存储所有享元对象的外蕴状态。
优点: 大幅度地降低内存中对象的数量。
缺点:
总结:
享元模式一般是解决系统性能问题的,所以经常用于底层开发,在项目开发中并不常用.
代理模式:
意图:为其他对象提供一种代理以控制对这个对象的访问。
角色及职责:
适用范围:
实现:
在调用处理器的invoke()方法中采取处理,一方面将调用传递给真是对象,另一方面执行各种所需要的操作。
工厂方法模式:提供一个简单的决策类,根据条件生成产品。
抽象工厂模式:提供一个创建并返回一系列产品的接口。
单件模式:某个类只能有一个实例。提供一个全局访问点。(可拓展到有限个实例)
生成器模式:将一个复杂对象的构建于呈现分开,以便根据不同需要创建不同的形式。
原型模式:先实例化一个类,然后克隆或者拷贝该类来构建新的实例。可以用共有方法进一步修改这些实例。
难点:抽象工厂模式与生成器模式的比较
简单工厂模式:专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。它又称为静态工厂方法模式,属于类的创建型模式。
简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。
意图:提供一个类,由它负责根据一定的条件创建某一具体类的实例
角色及其职责:
模式的特点:
工厂方法模式: 工厂方法模式的对简单工厂模式进行了抽象。有一个抽象的Factory类(可以是抽象类和接口),这个类将不在负责具体的产品生产,而是只制定一些规范,具体的生产工作推延到其子类去完成。
意图:定义一个用户创建对象的接口,让子类决定实例化哪一个类,工厂方法模式使一个类的实例化延迟到其子类。
优点:实现了开闭原则,可以在不改变工厂的前提下增加新产品。
实现要点:
效果:
适用性:
附注:
抽象工厂模式:提供创建对象的接口。与工厂方法类似,但此处返回的一系列相关产品。实现过程同样推延到子系列类去实现。与工厂方法的区别在于他们的层次模型。工厂方法的抽象基类只有儿子,而抽象工厂模式却是有孙子,而且每个儿子的儿子们之间有相互关联依赖关系。
意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
角色及职责:
附注:简单工厂、工厂方法、抽象工厂比较
简单工厂,工厂方法,抽象工厂都属于设计模式中的创建型模式。其主要功能都是帮助我们把对象的实例化部分抽取了出来,优化了系统的架构,并且增强了系统的扩展性。
小结:
区别:
以上三种工厂方法在等级结构和产品族这两个方向上的支持程度不同。所以要根据情况考虑应该使用哪种方法。
单件模式:保证某个类有且仅有一个实例。
意图:单件模式保证应用只有一个全局惟一的实例,并且提供一个访问它的全局访问点。
结构:包括防止其他对象创建实例的私有构造函数、保存惟一实例的私有变量和全局访问接口等。
效果:单件提供了全局惟一的访问入口,因此易于控制可能发生的冲突。单件是对类静态函数的一种改进,首先它避免了全局变量对系统的污染;其次正常类可以有子类,可以定义虚函数,具有多态性。而类中的静态方法是不能定义为虚函数的,因此不具有多态性。单件模式可以扩展为多件,即允许有受控的多个实例存在。
适用场合:当类只能有一个实例存在,并且可以在全局访问时。这个惟一的实例应该可以通过子类实现扩展,并且用户无须更改代码即可使用。我们前面介绍的工厂类经常被实例化为全局惟一的单件,可能的单件还有管理日志的对象、关键字生成对象和外部设备接口对象等。
实现:
比较:
实现:
public sealed class Singleton
{
static Singleton instance=null;
Singleton()
{
}
public static Singleton Instance
{
get
{
if (instance==null)
{
instance = new Singleton();
}
return instance;
}
}
}
这种方式的实现对于线程来说并不是安全的,因为在多线程的环境下有可能得到Singleton类的多个实例。如果同时有两个线程去判断(instance == null),并且得到的结果为真,这时两个线程都会创建类Singleton的实例,这样就违背了Singleton模式的原则。实际上在上述代码中,有可能在计算出表达式的值之前,对象实例已经被创建,但是内存模型并不能保证对象实例在第二个线程创建之前被发现。
该实现方式主要有两个优点:
public sealed class Singleton
{
static Singleton instance=null;
static readonly object padlock = new object();
Singleton()
{
}
public static Singleton Instance
{
get
{
lock (padlock)
{
if (instance==null)
{
instance = new Singleton();
}
return instance;
}
}
}
}
这种方式的实现对于线程来说是安全的。我们首先创建了一个进程辅助对象,线程在进入时先对辅助对象加锁然后再检测对象是否被创建,这样可以确保只有一个实例被创建,因为在同一个时刻加了锁的那部分程序只有一个线程可以进入。这种情况下,对象实例由最先进入的那个线程创建,后来的线程在进入时(instence == null)为假,不会再去创建对象实例了。但是这种实现方式增加了额外的开销,损失了性能。
public sealed class Singleton
{
static Singleton instance=null;
static readonly object padlock = new object();
Singleton()
{
}
public static Singleton Instance
{
get
{
if (instance==null)
{
lock (padlock)
{
if (instance==null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
}
这种实现方式对多线程来说是安全的,同时线程不是每次都加锁,只有判断对象实例没有被创建时它才加锁,有了我们上面第一部分的里面的分析,我们知道,加锁后还得再进行对象是否已被创建的判断。它解决了线程并发问题,同时避免在每个Instance 属性方法的调用中都出现独占锁定。它还允许您将实例化延迟到第一次访问对象时发生。实际上,应用程序很少需要这种类型的实现。大多数情况下我们会用静态初始化。这种方式仍然有很多缺点:无法实现延迟初始化。
public sealed class Singleton
{
static readonly Singleton instance=new Singleton();
static Singleton() //只要调用类名就调用.注意此处static前面不可以加权限修饰符。且该构造函数只在第一次引用该类成员的时候调用(参见msdn中关于金泰构造函数讲解部分)
{
}
Singleton()
{
}
public static Singleton Instance
{
get
{
return instance;
}
}
}
看到上面这段富有戏剧性的代码,我们可能会产生怀疑,这还是Singleton模式吗?在此实现中,将在第一次引用类的任何成员时创建实例。公共语言运行库负责处理变量初始化。该类标记为 sealed 以阻止发生派生,而派生可能会增加实例。此外,变量标记为 readonly,这意味着只能在静态初始化期间(此处显示的示例)或在类构造函数中分配变量。
该实现与前面的示例类似,不同之处在于它依赖公共语言运行库来初始化变量。它仍然可以用来解决 Singleton 模式试图解决的两个基本问题:全局访问和实例化控制。公共静态属性为访问实例提供了一个全局访问点。此外,由于构造函数是私有的,因此不能在类本身以外实例化 Singleton 类;因此,变量引用的是可以在系统中存在的唯一的实例。
由于 Singleton 实例被私有静态成员变量引用,因此在类首次被对 Instance 属性的调用所引用之前,不会发生实例化。
这种方法唯一的潜在缺点是,您对实例化机制的控制权较少。在 Design Patterns 形式中,您能够在实例化之前使用非默认的构造函数或执行其他任务。由于在此解决方案中由 .NET Framework 负责执行初始化,因此您没有这些选项。在大多数情况下,静态初始化是在 .NET 中实现 Singleton 的首选方法。
public sealed class Singleton
{
Singleton()
{
}
public static Singleton Instance
{
get
{
return Nested.instance;
}
}
class Nested
{
static Nested()
{
}
internal static readonly Singleton instance = new Singleton();
}
}
这里,初始化工作有Nested类的一个静态成员来完成,这样就实现了延迟初始化,并具有很多的优势,是值得推荐的一种实现方式。
实现要点:
优点 :
缺点 :
适用性 :
生成器模式:在软件设计中,有时候面临着一个非常复杂的对象的创建工作。这个复杂的对象通常可以分成几个较小的部分,由各个子对象组合出这个复杂对象的过程相对来说比较稳定,但是子对象的创建过程各不相同并且可能面临变化。根据OOD中的OCP原则,我们自然应该对这些子对象的创建过程进行变化封装。这就是生成器模式的思路。定义一个抽象的建造者的角色(Builder),规定所有具体的建造者都应该具有的功能——这些功能就是如何创建复杂对象的某个特定部分(子对象),而具体如何创建子对象有具体的创建者实现。再定义一个指导者的角色,它把创建者作为工具,知道如何使用这个工具来创建复杂对象。这样,客户在需要创建这个复杂对象的时候,只需要给指导者一个具体的创建者就可以了。至于具体创建者如何创建子对象的细节以及这些子对象之间的差异,不是指导者,也不是客户关心的。
意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示(GoF)。
角色及指责:
适用范围:生成器模式用于分步骤构建一个复杂的对象,如何划分步骤是一个稳定的算法,而复杂对象的各个部分的创建过程则经常变化。通过把各部件的创建过程封装到生成器中,使得复杂对象的创建过程和最终表象实现分分离。使用生成器模式,隐藏了具体产品的表示方法、内部结构和装配过程。通过定义一个新的生成器,就可以改变产品的内部表示。生成器模式中的指导者角色控制着生成器生成部件的过程。因此,通过指导者,可以实现对复杂产品生成步骤进行精细的控制——这一点在复杂产品部件的生成必须遵循一定的次序时显得十分方便。
优点:采用生成器模式可以轻松地改变产品的内部表示。生成器模式将构造代码和表示代码分开。构造过程可以更精细地控制,生成器模式强调的是产品的构造过程,产品各部分具有依赖关系非常重要。需要注意的是,不同生成器产生的对象可能不属于同一类型,因此使用生成器的客户必须知道产品的具体类型。这意味着生成器经常不能互换,不同的生成器针对的客户程序也不相同。
附注:与抽象工厂模式的比较
生成器模式关注于将构造对象的过程和构造的各个部分分开,而抽象工厂关注于构建一个产品系列。实际上,最大的区别是生成器模式创建的产品不一定有共同的父类,只要有类似的构造过程即可。实际上我们常见到的文件资源管理器的实现完全可以使用生成器模式。
生成器模式和工厂模式很相象,但是它们有很明显的区别。那就是工厂模式只是根据给的参数不同,工厂"生产"并返回不同的对象。生成器模式除了根据不同参数"生产"不同对象外,这些不同的对象还包含着不同的数据。生成器模式比工厂模式复杂就复杂在多"数据"这一部分。
注意问题:
原型模式:
定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。
——————————————————————————————————————
意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
角色及职责:
优点:引入Prototype模式后不再需要一个与具体产品等级结构平行的工厂方法类,减少了类的构造,同时客户程序可以在运行时刻建立和删除原型(自定义界面时,此点尤其重要)。
要实现深拷贝,可以通过序列化的方式。抽象类及具体类都必须标注为可序列化的[Serializable]
实现要点:
效果:
适用性:
附注:
Prototype模式同工厂模式,同样对客户隐藏了对象的创建工作,但是,与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的,达到了"隔离类对象的使用者和具体类型(易变类)之间的耦合关系"的目的。
此处引入的知识点式浅拷贝与深拷贝的问题:
概念:
a.浅拷贝(Shallow Copy影子克隆):只复制对象的基本类型,对象类型,仍属于原来的引用。
b.深拷贝(Deep Copy 深度克隆):不紧复制对象的基本类,同时也复制原对象中的对象.完全产生新对象。
实现机制:
2.深拷贝与浅拷贝实现机制:
对于值类型:
a.浅拷贝: 通过赋值等操作直接实现,将对象中的值类型的字段拷贝到新的对象中。
b.深拷贝:通过赋值等操作直接实现,将对象中的值类型的字段拷贝到新的对象中。 和浅拷贝相同
对于引用类型:
a.浅拷贝: MemberwiseClone 方法创建一个浅副本,方法是创建一个新对象,如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制引用原始对象,与原对象引用同一对象。
b.深拷贝:拷贝对象应用,也拷贝对象实际内容,也就是创建了一个新的改变新对象
不会影响到原始对象的内容
这种情况需要为其实现ICloneable接口中提供的Clone方法。
差别就是在对于引用类型的实现深拷贝和浅拷贝的时候的机制不同,前者是MemberwiseClone 方法实现,后者是通过继承实现ICloneable接口中提供的Clone方法,实现对象的深拷贝。