创建型设计模式
创建型模式就是用来创建对象的模式,抽象了实例化的过程。所有的创建型模式都有两个共同点。
第一,它们都将系统使用哪些具体类的信息封装起来;
第二,它们隐藏了这些类的实例是如何被创建和组织的。
创建型模式包括单例模式、简单工厂、工厂方法模式、抽象工厂模式、建造者模式
- 单例模式:解决的是实例化对象的个数问题,比如抽象工厂中的工厂、对象池等,除了Singleton之外,其他创建型模式解决的都是new所带来的耦合关系
- 抽象工厂:创建一系列相互依赖对象,并在运行时改变系列
- 工厂方法:创建单个对象,并在运行时改变
- 原型模式:通过拷贝原型来创建新的对象
- 建造者模式:创建操作对象,并根据操作对象进行生成
单例模式
确保一个类只有一个实例,并提供一个全局访问点。通过单例模式可以保证系统中一个类只有一个实例
单例模式设计的核心是为了控制用户使用new对一个类的实例构造器的任意调用。显然单例模式的要点有三个;一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。
从具体实现角度来说,就是以下三点:
- 单例模式的类只提供私有的构造函数。
这样就限制了在其他类中创建该类的对象的情况。即使该类不是密封类,继承该类的子类中也无法创建父类对象和子类对象,因为在创建的时候需要 调用父类的构造方法,而私有的父类构造方法是子类调用不到的。
- 类定义中含有一个该类的静态私有对象,即静态方法或属性或只读字段返回该对象类型。既然在其他类中无法创建新的实例,唯一的实例只能在本类的定义中提供了。 这就需要一个静态的成员来保证在其他类中能获得该实例,否则就瞎菜了
- 该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象。
操作系统中只能有一个任务管理器,操作文件时,同一时间内只允许一个实例对其操作等,既然现实生活中有这样的应用场景, 自然在软件设计领域必须有这样的解决方案了(因为软件设计也是现实生活中的抽象),所以也就有了单例模式了。
常用场景:应用中有对象需要是全局的且唯一。比如 任务管理器 回收站 网站的计数器 操作系统的文件系统 Web应用的配置对象的读取 共享文件 数据库连接池 多线程的线程池
选择关键点:一个对象在应用中出现多个实例是否会引起逻辑上或者是程序上的错误。
优点:
- 1.实例控制:单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。
- 2.灵活性:因为类控制了实例化过程,所以类可以灵活更改实例化过程。
缺点:
- 开销:虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。
- 可能的开发混淆:使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。 因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
- 3.对象生存期:不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于.NET Framework的语言),只有单例类能够导致实例被取消分配,因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用。
简单工厂
简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一
继承抽象类,由工厂类判断传递参数,来决定实例化那些类,是一个工厂只生产一类的产品,面对的是具体的类。
常用场景:需要在一堆产品中选择其中一个产品。
选择关键点:一种产品是否可根据某个参数决定它的种类。
这里组成:有产品抽象类,有基于产品抽象类的多个具体产品类,和一个工厂具体类,这里并没有把工厂抽象出来
优点:不必使用具体的功能类去创建该类的实例。缺点:新增一个功能类就需要在工厂类中增加一个判断。
public abstract class Apple
{
public abstract void GetPrice();
}
public class YellowApple : Apple
{
public override void GetPrice()
{
Console.WriteLine("黄苹果的价格:5¥");
}
}
public class GreenApple : Apple
{
public override void GetPrice()
{
Console.WriteLine("绿苹果的价格:3¥");
}
}
public class RedApple : Apple
{
public override void GetPrice()
{
Console.WriteLine("红苹果的价格:1¥");
}
}
public class AppleSimpleFacotry
{
public Apple GetAppleInstance(int type)
{
switch (type)
{
case 1: return new RedApple();
case 2: return new YellowApple();
case 3: return new GreenApple();
default:
break;
}
return null;
}
}
//最最基本的调用是
new GreenApple().GetPrice();
new YellowApple().GetPrice();
new GreenApple().GetPrice();
//可以看出调用方(客户端)直接依赖于具体的类,耦合度比较高,不同的类,需要一个个new
//下面是简单工作的调用方法
//可以看出一个具体的工厂,专门能输出具体的类,这里调用放直接调用
//了这个工厂,具体要什么类型的类,给穿个信息,比如这里给传了参数的形式,工厂内部根据不同的参数,
//输出不同具体的类型,可以理解出这个工厂就像一个黑匣子,专门生产Apple产品的,
//客户和具体的类的依赖转为了 中间有了个工厂类,客户端直接调工厂就行了,
//具体内部的对象的产生就交给工厂这个替死鬼了
var factory = new AppleSimpleFacotry();
factory.GetAppleInstance(1).GetPrice();
factory.GetAppleInstance(2).GetPrice();
factory.GetAppleInstance(3).GetPrice();
工厂方法
工厂模式把实现具体产品创建推迟到子类中,此时工厂类不再负责所有产品的创建,而只是给出具体工厂必须实现的接口。这样工厂方法模式就可以允许系统不修改工厂类逻辑的情况下来添加新产品。工厂方法模式遵循了开放-封闭原则,针对扩展是开放的,针对修改是封闭的。其实它是将判断工作转移到了客户端调用之处,这样保证了工厂体系的完整性。
工厂方法是针对每一种产品提供一个工厂类。通过不同的工厂实例来创建不同的产品实例。在同一等级结构中,支持增加任意产品。
工厂模式中,重要的是工厂类,而不是产品类。产品类可以是多种形式,多层继承或者是单个类都是可以的。但要明确的,工厂模式的接口只会返回一种类型的实例,这是在设计产品类的时候需要注意的,最好是有父类或者共同实现的接口。使用工厂模式,返回的实例一定是工厂创建的,而不是从其他对象中获取的。
此模式的核心精神是封装类中不变的部分,提取其中个性化善变的部分为独立类,通过依赖注入以达到解耦、复用和方便后期维护拓展的目的
工厂方法模式:
- 一个抽象产品类,可以派生出多个具体产品类。
- 一个抽象工厂类,可以派生出多个具体工厂类。
- 每个具体工厂类只能创建一个具体产品类的实例。
//这里相比较于简单工厂,我们把工厂内部依赖于具体的类这种依赖关系进行瓦解,抽象出来以降低耦合度
public abstract class AppleFacotry
{
public abstract Apple GetAppleInstance();
}
public class RedAppleFacotry : AppleFacotry
{
public override Apple GetAppleInstance()
{
return new RedApple();
}
}
public class YellowAppleFacotry : AppleFacotry
{
public override Apple GetAppleInstance()
{
return new YellowApple();
}
}
public class GreenAppleFacotry : AppleFacotry
{
public override Apple GetAppleInstance()
{
return new GreenApple();
}
}
//工厂方法 的调用方式如下:
//可以看出这里瓦解了抽象工厂的参数,以遵循开闭原则(OCP),客户端通过调用
//不同的具体的工厂以得到不同的具体产品类
//而我们要增加一个新的具体类也很方便,对扩展开放,我们比如只需要
//新建一个 BlackAppleFacotry 和BlackApple以类似的格式添加就行了,
//不影响原有的东西,做到了对修改关闭.
new RedAppleFacotry().GetAppleInstance().GetPrice();
new GreenAppleFacotry().GetAppleInstance().GetPrice();
new YellowAppleFacotry().GetAppleInstance().GetPrice();
//这里 涉及到的 有 一个抽象工厂类,多个具体工厂类,抽象产品类,多个具体的产品类,
//而多个具体产品类和多个具体的工厂类是一一对应的,如果具体类很多的话,工厂类应该也得多起来
// 目前 还是在一个类型产品下 进行的设计,如果产品类型多起来的话,如果不相互依赖的还好,不同的
//产品类型建不同的一套工厂,大家各顾各的,相互不交往,但是有时候,往往类型之间是有那么点关系的
//有关系的话就有依赖,有依赖就存在耦合。面对多个类型产品时,我们又得进一步抽象,这里关注点在于
//不同具体产品工厂上做了进一步扩展,之前一个具体工厂类只能生成一种具体的产品,现在可以生成多种
//这里就引出了抽象工厂
抽象工厂
为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。
抽象工厂是应对产品族概念的。比如说,每个汽车公司可能要同时生产轿车,货车,客车,那么每一个工厂都要有创建轿车,货车和客车的方法。把几种产品划出共同的东西,把相互依赖的对象抽象出来,只要实现这些接口就可以得到不同的产品。
抽象工厂”模式依赖于“工厂方法”模式的。所以说抽象工厂就像工厂,而工厂方法则像是工厂的一种产品生产线。因此,抽象工厂强调的是前面的动词“抽象”,也就是说,你将工厂方法模式中的工厂方法抽象出来的那个"动作或设计"就是“抽象工程”模式了,抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。。
用了工厂方法模式,你替换生成键盘的工厂方法,就可以把键盘从苹果换到微软。 但是用了抽象工厂模式,你只要换家工厂,就可以同时替换鼠标和键盘一套。如果你要的产品有几十个,当然用抽象工厂模式一次替换全部最方便
1.如果一个后花园只种蔬菜类,那么就用简单工厂就可以了.2.如果后花园蔬菜品种繁多.得用工厂方法才可以,把共有的东西抽象出来.3.如果要扩大后花园的规模,比如一个在北方,一个在南方,这样工厂方法就无法实现了,就应当用抽象工厂,把各种各样的植物,又组成一个后花园.
抽象工厂模式:
- 多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。
- 一个抽象工厂类,可以派生出多个具体工厂类。
- 每个具体工厂类可以创建多个具体产品类的实例。
常用场景:需要一个接口可以提供一个产品组,且不必知道产品的具体种类。
优点:1.它分离了具体的类2.它使得易于交换产品系列3.它有利于产品的一致性
缺点:难以支持新种类的产品
public class BeijingApple : Apple
{
public override void GetPrice()
{
Console.WriteLine("北京的苹果价格");
}
}
public class BeijingOrange : Orange
{
public override void GetWeight()
{
Console.WriteLine("北京的橘子重量");
}
}
public class ShanghaiApple : Apple
{
public override void GetPrice()
{
Console.WriteLine("上海的苹果价格");
}
}
public class ShanghaiOrange : Orange
{
public override void GetWeight()
{
Console.WriteLine("上海的橘子重量");
}
}
public abstract class SuperMacket
{
public abstract Apple GetAppleInstance();
public abstract Orange GetOrangeInstance();
}
public class BeijingMacket : SuperMacket
{
public override Apple GetAppleInstance()
{
return new BeijingApple();
}
public override Orange GetOrangeInstance()
{
return new BeijingOrange();
}
}
public class ShanghaiMacket : SuperMacket
{
public override Apple GetAppleInstance()
{
return new ShanghaiApple();
}
public override Orange GetOrangeInstance()
{
return new ShanghaiOrange();
}
}
//抽象工厂调用方法如下:
//可以看出 一个具体的工厂下,可以创建不同的类型产品的具体的类,比如Apple和Orange,体现了工厂的产品族的概念
//这种工厂是真正的大工厂,可以建不同类型的产品,可以建显示器,建议建鼠标,可以建鼠标,另一个工厂依然可以建
//这些东西, 可以看出 一个工厂对应到不同的产品类型上,而不是对应到了某种具体的产品类型上,现在工厂依赖于不同
//产品的抽象类,并不依赖于具体的产品类了。
var macket1 = new BeijingMacket();
macket1.GetAppleInstance().GetPrice();
macket1.GetOrangeInstance().GetWeight();
var macket2 = new ShanghaiMacket();
macket2.GetAppleInstance().GetPrice();
macket2.GetOrangeInstance().GetWeight();
//当然从这里我们类别于工厂方法,感觉缺失了一些东西,因为他更上层的抽象,下层的具体的依赖少了东西,比如
//我们的红 黄 绿 的苹果 没有体现出来,或者想要实现应该怎么再体现处理呢
可以采用如下思路:
public abstract class SuperMacket
{
public abstract Apple GetAppleInstance(AppleFactory factory);
public abstract Orange GetOrangeInstance(OrangeFactory factory);
}
public class BeijingMacket : SuperMacket
{
public override Apple GetAppleInstance(AppleFactory factory)
{
return factory.GetAppleInstance();
}
public override Orange GetOrangeInstance(OrangeFactory factory)
{
return factory.GetOrangeInstance();
}
}
public class ShanghaiMacket : SuperMacket
{
public override Apple GetAppleInstance(AppleFactory factory)
{
return factory.GetAppleInstance();
}
public override Orange GetOrangeInstance(OrangeFactory factory)
{
return factory.GetOrangeInstance();
}
}
public abstract class AppleFactory
{
public abstract Apple GetAppleInstance();
}
public abstract class OrangeFactory
{
public abstract Orange GetOrangeInstance();
}
建造者模式
在软件系统中,有时需要创建一个复杂对象,并且这个复杂对象由其各部分子对象通过一定的步骤组合而成,建造者模式是将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示。
现实中,公司要派一个人去采购两台电脑,一个是戴尔的,一个是联想的。采购员到商场给公司的 老板讲的时候, 不可能说要什么主机,什么键盘,什么鼠标之类的, 而是直接给老板讲要两台不同表现样式的成品电脑就行了,关于电脑的组装部分由老板, 自己考虑就行了,老板直接交给特定的组装人员就行了。 这里电脑就是一个复杂的对象,包括主机 cpu,显示器,鼠标等等。是通过一定的步骤进行组装的, 电脑的, 组装步骤都是一样的。这就可以应用建造者模式,包括如果采购员再需要惠普的电脑,一样都能适应。 这里 采购员就是客户端 电脑是那个复杂的对象 老板就是Director 不同品牌的组装人员就是各个builder 各个组装人员可以抽象成一个抽象建造者对象
- builder:给出一个抽象接口,以规范产品对象的各个组成成分的建造。这个接口规定要实现复杂对象的哪些部分的创建,并不涉及具体的对象部件的创建。
- ConcreteBuilder:实现Builder接口,针对不同的商业逻辑,具体化复杂对象的各部分的创建。 在建造过程完成后,提供产品的实例。
- Director:调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。
- Product:要创建的复杂对象。
常用场景:需要构建一批构建过程相同但表示不同的产品,而构建过程非常复杂。每个对象都具备自己的功能,但是,它们的创建方式却是一样的。这个时候就需要中间这个建造者类来负责功能对象实例的创建。在调用端只需调用特定的方法即可。
建造者主要是用于创建一些复杂的对象,这些对象内部构建间的建造顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化.当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。或者当构造过程必须允许被构造的对象有不同表示时。
选择关键点:各个产品的构建过程是否相同
使用建造者模式的好处:1.使用建造者模式可以使客户端不必知道产品内部组成的细节。2.具体的建造者类之间是相互独立的,对系统的扩展非常有利。3.由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。
原型模式
只创建一个类实例对象,如果后面需要更多这样的实例,可以通过对原来对象拷贝一份来完成创建,这样在内存中不需要创建多个相同的类实例,从而减少内存的消耗和达到类实例的复用
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆,在下一个请求时返回它的克隆体,在需要的时候更新数据库,以此来减少数据库的调用。
原型模式是用于创建重复的对象,同时又能保证性能。使用原型模式创建对象比直接new一个对象在性能上要好的多,因为Object类的clone方法是一个本地方法,它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。
只有一个实例,以后创建需要从这个实例进行拷贝。分为深拷贝和浅拷贝, MemberwiseClone 方法创建一个浅表副本,具体来说就是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。为了实现深度复制,我们就必须遍历有相互引用的对象构成的图,并需要处理其中的循环引用结构。先将对象序列化到内存流中,此时对象和对象引用的所用对象的状态都被保存到内存中。.Net的序列化机制会自动处理循环引用的情况。然后将内存流中的状态信息反序列化到一个新的对象中。这样一个对象的深度复制就完成了。在原型设计模式中CLONE技术非常关键。
常用场景:需要在运行时动态的创建指定实例种类的对象,或是需要复用其状态,在需要重复地创建相似对象时可以考虑使用原型模式。比如需要在一个循环体内创建对象,假如对象创建过程比较复杂或者循环次数很多的话,使用原型模式不但可以简化创建过程,而且可以使系统的整体性能提高很多
优点:1、性能提高。2、逃避构造函数的约束。
缺点: 1、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。 2、必须实现 Cloneable 接口。 3、逃避构造函数的约束。