《大话设计模式》——学习笔记之”创建型模式”(单例&工厂方法&抽象工厂&建造者&原型)
单例模式
定义: 保证一个类仅有一个实例,并提供一个访问它的全局访问点
Singleton类,定义一个GetInstance操作,允许客户访问它的唯一实例,GetInstance是一个静态方法,主要负责创建自己的唯一实例
class Singleton{
private static Singleton instance;
private Singleton(){
}
public static Singleton GetInstance(){
if(instance == null){
//若实例不存在,则new一个新实例,否则返回已有的实例
instance = new Singleton();
}
return instance;
}
}
优点:
- 保证一个类仅有一个实例,并提供一个访问它的全局访问点
- 可以严格地控制客户怎样访问以及何时访问实例
工厂方法模式
定义: 定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类
先构建一个工厂接口
interface IFactory{
Operation CreateOperation();
}
构建实现工厂接口的工厂类
class AddFactory : IFactory{
public Operation CreateOperation(){
return new OperationAdd();
}
}
class SubFactory : IFactory{
public Operation CreateOperation(){
return new OperationSub();
}
}
...
客户端实现
IFactory operFactory = new AddFactory();
Operation oper = operFactory.CreateOperation();
oper.NumberA = 1;
oper.NumberB = 2;
double result = oper.GetResult();
优点:
- 定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂模式使一个类的实例化延迟到其子类
- 在创建对象时,使用抽象工厂、原型、建造者的设计比使用工厂方法要更灵活,但它们也更加复杂,通常,设计是以使用工厂方法开始,当设计者发现需要更大的灵活性时,设计便会向其他创建型模式演化
- 工厂方法的实现并不能减少工作量,但是它能够在必须处理新情况时,避免使已经很复杂的代码更加复杂
抽象工厂模式
定义:抽象工厂模式(Abstract Factory),提供了一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类
用抽象工厂模式实现不同数据库切换
interface IUser{
void Insert(User user);
User GetUser(int id);
}
class SqlserverUser : IUser{
public void Insert(User user){
Console.WriteLine("在SQL Server中给User表增加一条记录");
}
public User GetUser(int id){
Console.WriteLine("在SQL Server中根据ID得到User表一条记录");
return null;
}
}
class AccessUser : IUser{
public void Insert(User user){
Console.WriteLine("在Access中给User表增加一条记录");
}
public User GetUser(int id){
Console.WriteLine("在Access中根据ID得到User表一条记录");
return null;
}
}
interface IDepartment{
void Insert(Department department);
Department GetDepartment(int id);
}
//用于访问SQL Server的Department
class SqlserverDepartment : IDepartment{
public void Insert(Department department){
Console.WriteLine("在SQL Server中给Department表增加一条记录");
}
public Department GetDepartment(int id){
Console.WriteLine("在SQL Server中根据ID得到Department表一条记录");
return null;
}
}
//用于访问Access的Department
class AccessDepartment : IDepartment{
public void Insert(Department department){
Console.WriteLine("在Access中给Department表增加一条记录");
}
public Department GetDepartment(int id){
Console.WriteLine("在Access中根据ID得到Department表一条记录");
return null;
}
}
class DataAccess{
private static readonly string db = "Sqlserver";
//private static readonly string db = "Access";
public static IUser CreateUser(){
IUser result = null;
switch(db){
case "Sqlserver":
result = new SqlserverUser();
break;
case "Access":
result = new AccessUser();
break;
}
return result;
}
public static IDepartment CreateDepartment(){
IDepartment result = null;
switch(db){
case "Sqlserver":
result = new SqlserverDepartment();
break;
case "Access":
result = new AccessDepartment();
break;
}
return result;
}
}
客户端代码
static void Main(string[] args){
User user = new User();
Department dept = new Department();
//直接得到实际的数据库访问实例,而不存在任何依赖
IUser iu = DataAccess.CreateUser();
iu.Insert(user);
iu.GetUser(1);
//直接得到实际的数据库访问实例,而不存在任何依赖
IDepartment id = DataAccess.CreateDepartment();
id.Insert(dept);
id.GetDepartment(1);
Console.Read();
}
优点:
- 提供一个创建一系列或相关依赖对象的接口,而无需指定它们具体的类
- 具体工厂类在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易
- 让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户端代码中
- 可以解决多个类型产品的创建问题
建造者模式
定义: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
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类,抽象建造者类
abstract class Builder{
public abstract void BuildPartA();
public abstract void BuildPartB();
public abstract Product GetResult();
}
ConcreteBuilder1类,具体建造者类
class ConcreteBuilder1 : Builder{
private Product product = new Product();
public override void BuildPartA(){
product.Add("部件A");
}
public override void BuildPartB(){
product.Add("部件B");
}
public override Product GetResult(){
return product;
}
}
ConcreteBuilder2类,具体建造者类
class ConcreteBuilder2 : Builder{
private Product product = new Product();
public override void BuildPartA(){
product.Add("部件X");
}
public override void BuildPartB(){
product.Add("部件Y");
}
public override Product GetResult(){
return product;
}
}
Director类,指挥者类
class Director{
public void Construct(Builder builder){
//用来指挥建造过程
builder.BuilderPartA();
builder.BuilderPartB();
}
}
客户端代码
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();
...
}
优点:
- 用户只需要指定需要建造的类型,无须关心建造的具体过程和细节
- 建造者隐藏了该产品是如何组装的,所以若需要改变一个产品的内部表示,只需要再定义一个具体的建造者
- 用于创建一些复杂的对象,这些对象内部构建间的建造顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化
- 建造者模式是在当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时适用的模式
原型模式
定义: 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
原型类
abstract class Prototype{
private string id;
public Prototype(string id){
this.id = id;
}
public string Id{
get{return id;}
}
public abstract Prototype Clone();
}
具体原型类
class ConcretePrototype1 : Prototype{
public ConcreteProtype1(string id) : base(id){
}
public override Prototype Clone(){
//创建当前对象的浅表副本,方法是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其副本引用同一对象
return (Prototype)this.MemberwiseClone();
}
}
客户端代码
static void Main(string[] args){
ConcretePrototype1 p1 = new ConcretePrototype1("I");
//通过克隆得到新的实例c1
ConcretePrototype1 c1 = (ConcretePrototype1)p1.Clone();
Console.WriteLine("Cloned: {0}", c1.Id);
Console.Read();
}
应用场景:
- 一般在初始化的信息不发生变化的情况,克隆是最好的办法,这既隐藏了对象创建的细节,又对性能是大大的提高(不用重新初始化对象,而是动态地获得对象运行时的状态)
- 原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节