创建型模式表现的是对象的创建过程和用户所使用的对象之间的关系。
创建模式包括:
工厂方法模式
抽象工厂模式
原型模式
单例模式
创建者模式
本篇文章主要介绍单例模式和创建者模式,前面几篇文章介绍了工厂三姐妹,抽象工厂模式,原型模式,都有相应的连接,可以看看。
单例模式
定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
优点:适用于任何类,提供了对唯一实例的受控访问,缩小了命名空间,允许对操作和表示的精化。
缺点:摧毁方法未定义,不能继承,有可能导致内存泄漏。
联系:与其他创建型模式并不矛盾,可以用单例模式来实现其他模式中的对象,如抽象工厂模式、建造者模式和原型模式等。
结构图
实现代码
//单线程实例
class Singleton
{
private static Singleton instance;
//构造方法让其private,这就堵死了外界利用new创建此类实例的可能
private Singleton()
{ }
//GetInstance方法是获得本类实例的唯一全局访问点
public static Singleton GetInstance()
{
//若实例不存在就new一个新的实例,否则返回已有的实例
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
//客户端
class Program
{
static void Main(string[] args)
{
Singleton s1 = Singleton.GetInstance();
Singleton s2 = Singleton.GetInstance();
//比较两次实例化后的对象的结果是实例相同
if (s1 == s2)
{
Console.WriteLine("两个对像是相同的实例。");
}
Console.Read();
}
单例模式因为Singleton类封装它的唯一实例,这样可以严格地控制客户怎样访问它以及何时访问它。简单地说就是对唯一实例的受控访问。
多线程时的单例
多线程同时访问Singleton类,调用GetInstance()方法,会有可能造成创建多个实例。这时可以给多线程加把锁处理。
代码实现
//多线程实例
class Singleton
{
private static Singleton instance;
//程序运行是创建一个静态只读的进程辅助对象
private static readonly object syncRoot = new object();
private Singleton()
{ }
public static Singleton GetInstance()
{
//同一时刻加了锁的那部分程序只有一个线程可以进入
lock (syncRoot)
{
if (instance == null)
{
instance = new Singleton();
}
}
return instance;
}
}
lock是确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,它将一直等待(即被阻止),直到该对象被释放。以上代码使得对象实例由最先进入的哪个线程创建,以后的线程进入时不会再去创建对象实例。以上代码没有直接锁定线程,是因为还不知道线程实例有没有被创建过。
但是每次调用GenInstance方法都要lock,影响性能,引入了双重锁定。
双重锁定
代码实现
//双重锁定
class Singleton
{
private static Singleton instance;
private static readonly object syncRoot = new object();
private Singleton()
{ }
public static Singleton GetInstance()
{
//先判断实例是否存在,不存在再加锁处理
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
我们先判断实例是否存在,不存在就加锁处理,能保证多线程的安全。通过第二重的判断,更体现了单例的模式。
静态初始化
C#与公共语言运行库也提供给了一种静态初始化方法,这种方法不需要开发人员显示地编写线程安全代码,即可解决多线程环境下它是不安全的问题。
解决了单例模式视图解决的两个基本问题:全局访问和实例化控制,公共静态属性为访问提供了一个全局访问点。
创建者模式
定义:讲一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
优点:建造者模式使产品的内部表象可以独立地变化,每一个Builder都相对独立,与其它的Builder无关,模式所建造的最终产品更易于控制。
缺点:不透明
使用时机:主要用于创建一些复杂的对象,这些对象内部构件间的建造顺序通常是稳定的,但对象内部的构件通常面临着复杂的变化。
结构图如下:
代码实现
Product类—产品类
//产品类,由多个部件组成
class Product
{
IList<string> parts = new List<string>();
//添加产品部件
public void Add(string part)
{
parts.Add(part);
}
public void Show()
{
Console.WriteLine("/n产品,创建-----");
//列举所有的产品部件
foreach (string part in parts)
{
Console.WriteLine(part);
}
}
}
Builder类—抽象建造者,是为创建一个Product对象的各个部件指定的抽象接口。
//抽象创建者类,确定产品由两个部件PartA和PartB组成
abstract class Builder
{
public abstract void BuildPartA();
public abstract void BuildPartB();
//声明一个得到产品建造后结果的方法GetResult
public abstract Product GetResult();
}
具体建造者,实现Builder接口,构造和装配各个部件。
//具体建造者类
class ConcreteBuilder1 : Builder
{
private Product product = new Product();
//建造者的两个具体部件A和B
public override void BuildPartA()
{
product.Add("部件A");
}
public override void BuildPartB()
{
product.Add("部件B");
}
public override Product GetResult()
{
return product;
}
}
//具体建造者
class ConcreteBuilder2 : Builder
{
private Product product = new Product();
//建造者的两个部件X和Y
public override void BuildPartA()
{
product.Add("部件X");
}
public override void BuildPartB()
{
product.Add("部件Y");
}
public override Product GetResult()
{
return product;
}
}
Director类—指挥者类,是构件一个使用Builder接口的对象。
//指挥者类
class Director
{
//用来指挥建造过程
public void Construct(Builder builder)
{
builder.BuildPartA();
builder.BuildPartB();
}
}
客户端
class Program
{
static void Main(string[] args)
{
Director director = new Director();
Builder b1 = new ConcreteBuilder1();
Builder b2 = new ConcreteBuilder2();
director.Construct(b1);
//指挥者用ConcreteBuilder1的方法来建造产品
Product p1 = b1.GetResult();
p1.Show();
director.Construct(b2);
//指挥者用ConcreteBuilder2的方法来建造产品
Product p2 = b2.GetResult();
p2.Show();
Console.Read();
}
}
总结
与其他模式的联系:
(1)与抽象工厂模式相比,都可以创建复杂对象,但建造者模式着重于一步步构造一个复杂对象,抽象工厂模式着重于一系列的产品对象,相应地,建造模式是在最后一步返回产品,抽象工厂模式的产品是立即返回的。
(2)可以搭配使用抽象工厂模式和建造模式,客户端通过调用建造者模式,间接地调用抽象工厂的角色,工厂模式返还不同产品族的零件,建造者模式把它们组装起来。
结构型模式关心类和对象之间怎么组织起来形成大的结构,主要使用继承来组织接口实现。
行为型模式关心的是算法以及对象之间的任务分配,它所描述的不仅仅是对象或类的设计模式,还有它们之间的通讯模式。