一、单件模式
动机(Motivation):
在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性、以及良好的效率。
如何绕过常规的构造器,提供一种机制来保证一个类只创建一个实例?
这应该是类设计者的责任,而不是类使用者的责任。
意图:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
------<<设计模式>>GOF
适用性:
(1)当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
(2)当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
代码实现:
(1)单线程Singleton实现
以上代码在单线程情况下不会出现任何问题。但是在多线程的情况下却不是安全的。
如两个线程同时运行到 if (instance == null)判断是否被实例化,一个线程判断为True后,在进行创建
instance = new SingleThread_Singleton();之前,另一个线程也判断(instance == null),结果也为True.
这样就就违背了Singleton模式的原则(保证一个类仅有一个实例)。
怎样在多线程情况下实现Singleton?
(2)多线程Singleton实现:
class MultiThread_Singleton { private static volatile MultiThread_Singleton instance = null; private static object lockHelper = new object(); private MultiThread_Singleton() { } public static MultiThread_Singleton Instance { get { if (instance == null) { lock (lockHelper) { if (instance == null) { instance = new MultiThread_Singleton(); } } } return instance; } }
此程序对多线程是安全的,使用了一个辅助对象lockHelper,保证只有一个线程创建实例(如果instance为空,保证只有一个线程instance = new MultiThread_Singleton();创建唯一的一个实例)。
(3)静态Singleton实现
class Static_Singleton { public static readonly Static_Singleton instance = new Static_Singleton(); private Static_Singleton() { } } 以上代码展开等同于 class Static_Singleton { public static readonly Static_Singleton instance; static Static_Singleton() { instance = new Static_Singleton(); } private Static_Singleton() { } }
由此可以看出,完全符合Singleton的原则。
优点: 简洁,易懂
缺点: 不可以实现带参数实例的创建。
二、适配器模式
适配(转换)的概念无处不在......
适配,即在不改变原有实现的基础上,将原先不兼容的接口转换为兼容的接口。
动机(Motivate):
在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。
那么如何应对这种“迁移的变化”?如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口?这就是本文要说的Adapter 模式。
意图(Intent):
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
-------《设计模式》GOF
适用性:
1.系统需要使用现有的类,而此类的接口不符合系统的需要。
2.想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。这些源类不一定有很复杂的接口。
3.(对对象适配器而言)在设计里,需要改变多个已有子类的接口,如果使用类的适配器模式,就要针对每一个子类做一个适配器,而这不太实际。
示意性代码实例:
interface IStack { void Push(object item); void Pop(); object Peek(); } //对象适配器(Adapter与Adaptee组合的关系) public class Adapter : IStack //适配对象 { ArrayList adaptee;//被适配的对象 public Adapter() { adaptee = new ArrayList(); } public void Push(object item) { adaptee.Add(item); } public void Pop() { adaptee.RemoveAt(adaptee.Count - 1); } public object Peek() { return adaptee[adaptee.Count - 1]; } } 类适配器 public class Adapter :ArrayList, IStack { public void Push(object item) { this.Add(item); } public void Pop() { this.RemoveAt(this.Count - 1); } public object Peek() { return this[this.Count - 1]; } }
Adapter模式的几个要点:
Adapter模式主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况”,在遗留代码复用、类库迁移等方面非常有用。
GOF23定义了两种Adapter模式的实现结构:对象适配器和类适配器。但类适配器采用“多继承”的实现方式,带来不良的高耦合,所以一般不推荐使用。对象适配器采用“对象组合”的方式,更符合松耦合精神。
Adapter模式可以实现的非常灵活,不必拘泥于GOF23中定义的两种结构。例如,完全可以将Adapter模式中的“现存对象“作为新的接口方法参数,来达到适配的目的。
Adapter模式本身要求我们尽可能地使用”面向接口的编程"风格,这样才能在后期很方便的适配。
.NET框架中的Adapter应用:
(1)在.Net中复用com对象:
Com 对象不符合.net对象的接口
使用tlbimp.exe来创建一个Runtime Callable Wrapper(RCW)以使其符合.net对象的接口。
(2).NET数据访问类(Adapter变体):
各种数据库并没有提供DataSet接口
使用DBDataAdapter可以将任何各数据库访问/存取适配到一个DataSet对象上。
(3)集合类中对现有对象的排序(Adapter变体);
现有对象未实现IComparable接口
实现一个排序适配器(继承IComparer接口),然后在其Compare方法中对两个对象进行比较。
三、访问者模式
动机:
在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给子类带来很繁重的变更负担,甚至破坏原有设计。
如何在不更改类层次结构的前提下,在运行时根据需要透明地为类层次结构上的各个类动态添加新的操作,从而避免上述问题?
意图:
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这引起元素的新操作。
适用性:
1.一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
2.需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作"污染"这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。
3.定义对象结构的类很少改变,但经常需要在结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。
代码实现:
// MainApp startup application class MainApp { static void Main() { // Setup employee collection Employees e = new Employees(); e.Attach(new Clerk()); e.Attach(new Director()); e.Attach(new President()); // Employees are 'visited' e.Accept(new IncomeVisitor()); e.Accept(new VacationVisitor()); // Wait for user Console.Read(); } } // "Visitor" interface IVisitor { void Visit(Element element); } // "ConcreteVisitor1" class IncomeVisitor : IVisitor { public void Visit(Element element) { Employee employee = element as Employee; // Provide 10% pay raise employee.Income *= 1.10; Console.WriteLine("{0} {1}'s new income: {2:C}", employee.GetType().Name, employee.Name, employee.Income); } } // "ConcreteVisitor2" class VacationVisitor : IVisitor { public void Visit(Element element) { Employee employee = element as Employee; // Provide 3 extra vacation days Console.WriteLine("{0} {1}'s new vacation days: {2}", employee.GetType().Name, employee.Name, employee.VacationDays); } } class Clerk : Employee { // Constructor public Clerk() : base("Hank", 25000.0, 14) { } } class Director : Employee { // Constructor public Director() : base("Elly", 35000.0, 16) { } } class President : Employee { // Constructor public President() : base("Dick", 45000.0, 21) { } } // "Element" abstract class Element { public abstract void Accept(IVisitor visitor); } // "ConcreteElement" class Employee : Element { string name; double income; int vacationDays; // Constructor public Employee(string name, double income, int vacationDays) { this.name = name; this.income = income; this.vacationDays = vacationDays; } // Properties public string Name { get{ return name; } set{ name = value; } } public double Income { get{ return income; } set{ income = value; } } public int VacationDays { get{ return vacationDays; } set{ vacationDays = value; } } public override void Accept(IVisitor visitor) { visitor.Visit(this); } } // "ObjectStructure" class Employees { private ArrayList employees = new ArrayList(); public void Attach(Employee employee) { employees.Add(employee); } public void Detach(Employee employee) { employees.Remove(employee); } public void Accept(IVisitor visitor) { foreach (Employee e in employees) { e.Accept(visitor); } Console.WriteLine(); } 157 }
运行结果:
Visoitr模式的几个要点:
1.Visitor模式通过所谓双重分发(double dispatch)来实现在不更改Element类层次结构的前提下,在运行时透明地为类层次结构上的各个类动态添加新的操作。
2.所谓双重分发却Visotor模式中间包括了两个多态分发(注意其中的多态机制);第一个为accept方法的多态辨析;第二个为visitor方法的多态辨析。
3.Visotor模式的最大缺点在于扩展类层次结构(增添新的Element子类),会导致Visitor类的改变。因此Visiotr模式适用"Element"类层次结构稳定,而其中的操作却经常面临频繁改动".