大话设计模式观后感之创建型模式

大话设计模式观后感之创建型模式

四月 22, 2022

创建型模式

为什么我们需要创建型模式?

创建型模式隐藏了这些类的实例是如何被创建和放在一起,整个系统关于这些对象所知道的是由抽象类所定义的接口。这样,创建型模式在创建了什么、谁创建它、它是怎么被创建的,以及何时创建这些方面提供了很大的灵活性。

当一个系统应该独立于它的产品创建、构成和表示时,应该考虑用创建性模式。建立相应数目的原型并克隆它们通常比每次用合适的状态手工实例化该类更方便一些

松耦合的理解

这个问题首先要谈谈内聚性与耦合性
内聚性描述的是一个例程内部组成部分之间相互联系的紧密程度。
耦合性描述的是一个例程与其他例程之间联系的紧密程度。
软件开发的目标应该是创建这样的例程:内部完整,也就是高内聚,而与其他例程之间的联系则是小巧、直接、可见、灵活的,这就是松耦合

如何理解创建型模式存在的意义?

创建型模式抽象了实例化的过程。它们帮助一个系统独立于如何创建、组合和表示它的那些对象。创建型模式都会将关于该系统使用哪些具体的类的信息封装起来。允许客户用结构和功能差别很大的‘产品’对象配置一个系统配置可以是静态的,即在编译时指定,也可以是动态的,就是运行时再指定。

工厂方法的优势

我觉得她们几位都可能设计出比我更加灵活的代码,但她们的实现也相对就更加复杂。通常设计应该是从工厂方法开始当设计者发现需要更大的灵活性时,设计便会向其他创建型模式演化。当设计者在设计标准之间进行权衡的时候,了解多个创建型模式可以给设计者更多的选择余地.

简单工厂模式

简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。
解决如何实例化对象的问题
不属于23种GOF设计模式之一,不符合开放封闭原则每一次都要修改类。
在这里插入图片描述

工厂方法模式

工厂方法模式,定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
将实例化方法写在concreteCreator类里面
工厂方法模式和简单工厂的区别:简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。

缺点:由于每加一个产品,就需要加一个产品工厂的类,增加了额外的开发量。”在这里插入图片描述
通过加分工厂实例化加法运算类

1
2
3
4
5
6
7
8
interface IFactory{
Operation CreateOperation ();
}
class AddFactory : IFactory{
public Operation Create0peration (){
return new OperationAdd ();
}
}

在这里插入图片描述

抽象工厂模式

原版

抽象工厂模式是提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
多个产品有一个或多个实现方式,就把同产品系列的抽象。工厂和产品都进行抽象
优点

  1. 最大的好处便是易于交换产品系列,由于具体工厂类,例如IFactory factory =new AccessFactory().在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置。
  2. 它让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码中。

缺点修改时需要添加三个类,并需要修改抽象工厂类及抽象工厂子类。
在这里插入图片描述

简单工厂来改进

与其用那么多工厂类,不如直接用一个简单工厂来实现,抛弃了IFactory、SqlserverFactory和 AccessFactory三个工厂类,取而代之的是DataAccess类,由于事先设置了db的值(Sqlserver或Access),所以简单工厂的方法都不需要输入参数,这样在客户端就只需要 DataAccess.CreateUser()和 DataAccess.CreateDepartment()来生成具体的数据库访问类实例,客户端没有出现任何一个SOL Server或 Access的字样,达到了解耦的目的。这样就需要在 DataAccess类中每个方法的swicth 中加 case了。
在这里插入图片描述

男人的浪漫–反射

Assembly.Load(“程序集名称”).CreateInstance(“命名空间.类名称”)
所有在用简单工厂的地方,都可以考虑用反射技术来去除switch或 if,解除分支判断带来的耦合。
在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using Syetem.Reflection//引入反射,必须要写
class DataAccess
{
private static readonly string AssemblyName = "抽象工厂模式";//程序集名称
private static readonly string db = "Sqlserver";//数据库名称,可替换成Access
public static IUser CreateUser()
{
string className = AssemblyName + "." +db +"User";
return (IUser)Assembly.Load (AssemblyName).CreateInstance (className);
}
public static IDepartment CreateDepartment()
{
string className = AssemblyName + "." +db+ "Department";
return (IDepartment)Assembly.Load (AssemblyName).CreateInstance (className) ;
}
}

单例模式

单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点。
通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。
在这里插入图片描述

单线程的实现

Singleton类,定义一个GetInstance操作,允许客户访问它的唯一实例。GetInstance是一个静态方法,主要负责创建自己的唯一实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Singleton{
private static singleton instance;
private Singleton (){//构造方法让其private,这就堵死了外界利用
//new创建此类实例的可能
)
public static singleton GetInstance (){
//此方法是获得本类实例的唯一全局访问点
if (instance == null){
//若实例不存在,则new一个新实例,
//否则返回已有的实例
instance =new Singleton ();
}
return instance;
}
}

客户端代码

1
2
3
4
5
6
7
static void Main (string[] args)
Singleton s1 = Singleton.GetInstance();Singleton s2 -Singleton.GetInstance ();
if (s1 ==s2)
{
Console.writeLine("两个对象是相同的实例。");
)
Console.Read() ;

多线程单例

lock 是确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。
双重锁定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;
}
}

建造者模式

建造者模式(Builder),将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
如果我们用了建造者模式,那么用户就只需指定需要建造的类型就可以得到它们,而具体建造的过程和细节就不需知道了。
用处创建一些复杂的对象,这些对象内部构建间的建造顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化。
建造者模式是在当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时适用的模式。
好处就是使得建造代码与表示代码分离,由于建造者隐藏了该产品是如何组装的,所以若需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了。
在这里插入图片描述
Product类——产品类,由多个部件组成。

1
2
3
4
5
6
7
8
9
10
11
12
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类——抽象建造者类,确定产品由两个部件 PartA和PartB组成,并声明一个得到产品建造后结果的方法GetResult。

1
2
3
4
5
abstract class Builder{
public abstract void BuildPartA ();
public abstract void BuildPartB ();
public abstract Product GetResult();
}

ConcreteBuilder1类——具体建造者类。

1
2
3
4
5
6
7
8
9
10
11
12
13
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(o){
return product;
}
}

ConcreteBuilder2类——具体建造者类。

1
2
3
4
5
6
7
8
9
10
11
12
13
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类——指挥者类。

1
2
3
4
5
6
class Director{
public void Construct(Builder builder){//用来指挥建造过程
builder .BuildPartA ();
builder. BuildPartB();
}
}

例子
在这里插入图片描述

原型模式

原型模式(Prototype),用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
“原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节。

原版

在这里插入图片描述
原型类

1
2
3
4
5
6
7
8
9
10
abstract class Prototype{
private string id;
public Prototype (string id){
this.id = id;
}
public string Id{
get { return id; )
}
public abstract Prototype Clone (); //抽象类关键就是有这样一个Clone方法
}

具体原型类

1
2
3
4
5
6
7
8
9
10
11
class ConcretePrototypel : Prototype{
public ConcretePrototype1 (string id): base (id){
}
public override Prototype Clone (){
return (Prototype)this.MemberwiseClone() ;
//创建当前对象的浅表副本。方法是创建一个新对象,然后将当前对象
//的非静态字段复制到该新对象。如果字段是值类型的,则对该字段执
//行逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;
//因此,原始对象及其副本引用同对象[MSDN]
}
}

客户端代码

1
2
3
4
5
6
static void Main (string[]args){
ConcretePrototypel p1 = new ConcretePrototype1("I");
ConcretePrototypel cl =(ConcretePrototype1)p1.clone() ;
Console.writeLine ( "cloned: {0}", c1.Id);
Console.Read() ;//克隆类ConcretePrototype1的对象pl就能得到新的实例cl
}

浅复制和深复制

浅复制,被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。
深复制,深复制把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。

Csharp更新

.NET在System命名空间中提供了ICloneable接口,其中就是唯一的一个方法Clone(),这样你就只需要实现这个接口就可以完成原型模式了。
在这里插入图片描述