C#.Net 设计模式学习笔记之创建型 (一)
1、抽象工厂(Abstract Factory)模式
常规的对象创建方法:
//创建一个Road对象 Road road =new Road();
new 的问题:
实现依赖,不能应对“具体实例化类型”的变化。
解决思路:
封装变化点-----哪里变化,封装哪里
潜台词: 如果没有变化,当然不需要额外的封装!
工厂模式的缘起
变化点在“对象创建”,因此就封装“对象创建”
面向接口编程----依赖接口,而非依赖实现
最简单的解决方法:
class RoadFactory { public static Road CreateRoad() { return new Road(); } }
//创建一个Road对象
Road road=roadFactory.CreateRoad();
创建一系列相互依赖对象的创建工作:
假设一个游戏开场景:
我们需要构造"道路"、"房屋"、"地道","从林"...等等对象
工厂方法如下:
class RoadFactory { public static Road CreateRoad() { return new Road(); } public static Building CreateBuilding() { return new Building(); } public static Tunnel CreateTunnel() { return new Tunnel(); } public static Jungle CreateJungle() { return new Jungle(); } }
调用方式如下:
Road road = RoadFactory.CreateRoad(); Building building = RoadFactory.CreateBuilding(); Tunnel tunnel = RoadFactory.CreateTunnel(); Jungle jungle = RoadFactory.CreateJungle();
如上可见简单工厂的问题:
不能应对"不同系列对象"的变化。比如有不同风格的场景---对应不同风格的道路,房屋、地道....
如何解决:
使用面向对象的技术来"封装"变化点。
动机(Motivate):
在软件系统中,经常面临着"一系统相互依赖的对象"的创建工作:同时,由于需求的变化,往往存在更多系列对象的创建工作。
如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种"封装机制"来避免客户程序和这种"多系列具体对象创建工作"的紧耦合?
意图(Intent):
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
----摘自《设计模式》
结构图(Struct):
适用性:
1.一个系统要独立于它的产品的创建、组合和表示时。
2.一个系统要由多个产品系统中的一个来配置时。
3.当你要强调一系列相关的产品对象的设计以便进行联合使用时。
4.当你提供一个产品类库,而只想显示它们的接口不是实现时。
生活例子:
结构图代码实现:
abstract class AbstractFactory { public abstract AbstractProductA CreateProductA(); public abstract AbstractProductB CreateProductB(); } abstract class AbstractProductA { public abstract void Interact(AbstractProductB b); } abstract class AbstractProductB { public abstract void Interact(AbstractProductA a); } class Client { private AbstractProductA AbstractProductA; private AbstractProductB AbstractProductB; public Client(AbstractFactory factory) { AbstractProductA = factory.CreateProductA(); AbstractProductB = factory.CreateProductB(); } public void Run() { AbstractProductB.Interact(AbstractProductA); AbstractProductA.Interact(AbstractProductB); } } class ConcreteFactory1:AbstractFactory { public override AbstractProductA CreateProductA() { return new ProductA1(); } public override AbstractProductB CreateProductB() { return new ProductB1(); } } class ConcreteFactory2:AbstractFactory { public override AbstractProductA CreateProductA() { return new ProdcutA2(); } public override AbstractProductB CreateProductB() { return new ProductB2(); } } class ProductA1:AbstractProductA { public override void Interact(AbstractProductB b) { Console.WriteLine(this.GetType().Name + "interact with" + b.GetType().Name); } } class ProductB1:AbstractProductB { public override void Interact(AbstractProductA a) { Console.WriteLine(this.GetType().Name + "interact with" + a.GetType().Name); } } class ProdcutA2:AbstractProductA { public override void Interact(AbstractProductB b) { Console.WriteLine(this.GetType().Name + "interact with" + b.GetType().Name); } } class ProductB2:AbstractProductB { public override void Interact(AbstractProductA a) { Console.WriteLine(this.GetType().Name + "interact with" + a.GetType().Name); } } public static void Main() { // Abstractfactory1 AbstractFactory factory1 = new ConcreteFactory1(); Client c1 = new Client(factory1); c1.Run(); // Abstractfactory2 AbstractFactory factory2 = new ConcreteFactory2(); Client c2 = new Client(factory2); c2.Run(); }
Abstract Factory注意的几点:
如果不存在”多系列对象创建“的需求变化,则没必要应用Abstract Factory模式,静态工厂方法足矣。
"系列对象"指的是这些对象之间有相互依赖、或作用的关系。例如游戏开发场景中的"道路"与"房屋"依赖,“道路”与“地道”的依赖。
Abstract Factory模式主要在于应对"新系列"的需求变动。其缺点在于难以应对”新对象“的需求变动。
Abstract Factory模式经常和Factory Method模式共同组合来应对“对象创建”的需求变化。
2、单件模式(Singleto Pattern)
动机(Motivation):
在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性、以及良好的效率。
如何绕过常规的构造器,提供一种机制来保证一个类只创建一个实例?
这应该是类设计者的责任,而不是类使用者的责任。
结构图:
意图:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
----摘自《设计模式》
生活的例子:
适用性:
(1)当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
(2)当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
代码实现:
(1)单线程Singleton实现
class SingleThread_Singleton { private static SingleThread_Singleton instance = null; private SingleThread_Singleton(){} public static SingleThread_Singleton Instance { get { if (instance == null) { instance = new SingleThread_Singleton(); } return instance; } } }
以上代码在单线程情况下不会出现任何问题。但是在多线程的情况下却不是安全的。
如两个线程同时运行到 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();创建唯一的一个实例)。(Double Check)
请注意一个关键字volatile,如果去掉这个关键字,还是有可能发生线程不是安全的。
volatile 保证严格意义的多线程编译器在代码编译时对指令不进行微调。
(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的原则。
优点: 简洁,易懂
缺点: 不可以实现带参数实例的创建。
(注:以上代码及信息借鉴于李建忠老师的MSDN和TerryLee的文章。)
3、工厂方法模式(Factory Method)
耦合关系:
耦合关系直接决定着软件面对变化时的行为
--模块与模块之间的紧耦合使得软件面对变化时,相关的模块都要随之更改;
--模块与模块之间的松耦合使得软件面对变化时,一些模块更容易被替换或者更改,但其他模块保持不变。
动机(Motivation):
在软件系统中,由于需求的变化,"这个对象的具体实现"经常面临着剧烈的变化,但它却有比较稳定的接口。
如何应对这种变化呢?提供一种封装机制来隔离出"这个易变对象"的变化,从而保持系统中"其它依赖的对象"不随需求的变化而变化。
意图(Intent):
定义一个用户创建对象的接口,让子类决定实例哪一个类。Factory Method使一个类的实例化延迟到子类。
----摘自《设计模式》
结构图(Struct):
生活实例:
适用性:
1.当一个类不知道它所必须创建的对象类的时候。
2.当一个类希望由它子类来指定它所创建对象的时候。
3.当类将创建对象的职责委托给多个帮助子类中的某个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
实例代码:
//CarFactory类: public abstract class CarFactory { public abstract Car CarCreate(); } //Car类: public abstract class Car { public abstract void StartUp(); public abstract void Run(); public abstract void Stop(); } //HongQiCarFactory类: public class HongQiCarFactory : CarFactory { public override Car CarCreate() { return new HongQiCar(); } } //BMWCarFactory类: public class BMWCarFactory : CarFactory { public override Car CarCreate() { return new BMWCar(); } } //HongQiCar类: public class HongQiCar : Car { public override void StartUp() { Console.WriteLine("Test HongQiCar start-up speed!"); } public override void Run() { Console.WriteLine("The HongQiCar run is very quickly!"); } public override void Stop() { Console.WriteLine("The slow stop time is 3 second "); } } //BMWCar类: public class BMWCar : Car { public override void StartUp() { Console.WriteLine("The BMWCar start-up speed is very quickly"); } public override void Run() { Console.WriteLine("The BMWCar run is quitely fast and safe!!!"); } public override void Stop() { Console.WriteLine("The slow stop time is 2 second"); } } /* app.config <?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="No1" value="HongQiCarFactory"/> <add key="No2" value="BMWCarFactory"/> </appSettings> </configuration> Program类: */ class Program { static void Main(string[] args) { Console.WriteLine("Please Enter Factory Method No:"); Console.WriteLine("******************************"); Console.WriteLine("no Factory Method"); Console.WriteLine("1 HongQiCarFactory"); Console.WriteLine("2 BMWCarFactory"); Console.WriteLine("******************************"); int no = Int32.Parse(Console.ReadLine().ToString()); string factoryType = ConfigurationManager.AppSettings["No" + no]; //CarFactory factory = new HongQiCarFactory(); CarFactory factory = (CarFactory)Assembly.Load("FactoryMehtod").CreateInstance("FactoryMehtod." + factoryType); ; Car car = factory.CarCreate(); car.StartUp(); car.Run(); car.Stop(); } }
Factory
Method 模式的几个要点:
Factory Method模式主要用于隔离类对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体类型,紧耦合关系会导致软件的脆弱。
Factory Method模式通过面向对象的手法,将所要创建的具体对象工作延迟到子类,从而实现一种扩展(而非更改)的策略,较好地解决了这种紧耦合关系。
Factory
Mehtod模式解决"单个对象"的需求变化,AbstractFactory模式解决"系列对象"的需求变化,Builder模式解决"对象部分"的需求变化。
4、建造者模式(Builder)
假设创建游戏中的一个房屋House设施,该房屋的构建由几部分组成,且各个部分富于变化。
如果使用最直观的设计方法,每一个房屋部分的变化,都将导致房屋构建的重新修正.....
动机(Motivation):
在软件系统中,有时候面临一个"复杂对象"的创建工作,其通常由各个部分的子对象用一定算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合到一起的算法却相对稳定。
如何应对种变化呢?如何提供一种"封装机制"来隔离出"复杂对象的各个部分"的变化,从而保持系统中的"稳定构建算法"不随需求的改变而改变?
意图(Intent):
将一个复杂对象的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
----摘自《设计模式》
结构图(Struct):
协作(Collaborations):
生活中的例子:
适用性:
1.当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
2.当构造过程必须允许被构造的对象有不同的表示时。
实例代码:
//Builder类: public abstract class Builder { public abstract void BuildDoor(); public abstract void BuildWall(); public abstract void BuildWindows(); public abstract void BuildFloor(); public abstract void BuildHouseCeiling(); public abstract House GetHouse(); } //Director类:这一部分是 组合到一起的算法(相对稳定)。 public class Director { public void Construct(Builder builder) { builder.BuildWall(); builder.BuildHouseCeiling(); builder.BuildDoor(); builder.BuildWindows(); builder.BuildFloor(); } } //ChineseBuilder类 public class ChineseBuilder : Builder { private House ChineseHouse = new House(); public override void BuildDoor() { Console.WriteLine("this Door 's style of Chinese"); } public override void BuildWall() { Console.WriteLine("this Wall 's style of Chinese"); } public override void BuildWindows() { Console.WriteLine("this Windows 's style of Chinese"); } public override void BuildFloor() { Console.WriteLine("this Floor 's style of Chinese"); } public override void BuildHouseCeiling() { Console.WriteLine("this Ceiling 's style of Chinese"); } public override House GetHouse() { return ChineseHouse; } } //RomanBuilder类: class RomanBuilder : Builder { private House RomanHouse = new House(); public override void BuildDoor() { Console.WriteLine("this Door 's style of Roman"); } public override void BuildWall() { Console.WriteLine("this Wall 's style of Roman"); } public override void BuildWindows() { Console.WriteLine("this Windows 's style of Roman"); } public override void BuildFloor() { Console.WriteLine("this Floor 's style of Roman"); } public override void BuildHouseCeiling() { Console.WriteLine("this Ceiling 's style of Roman"); } public override House GetHouse() { return RomanHouse; } }
ChineseBuilder和RomanBuilder
public class Client { public static void Main(string[] args) { Director director = new Director(); Builder instance; Console.WriteLine("Please Enter House No:"); string No = Console.ReadLine(); string houseType = ConfigurationSettings.AppSettings["No" + No]; instance = (Builder)Assembly.Load("House").CreateInstance("House." + houseType); director.Construct(instance); House house = instance.GetHouse(); house.Show(); Console.ReadLine(); } }
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="No1" value="RomanBuilder"></add>
<add key="No2" value="ChineseBuilder"></add>
</appSettings>
</configuration>
Builder模式的几个要点:
Builder模式 主要用于“分步骤构建一个复杂的对象”。在这其中“分步骤”是一个稳定的乘法,而复杂对象的各个部分则经常变化。
Builder模式主要在于应对“复杂对象各个部分”的频繁需求变动。其缺点在于难以应对“分步骤构建算法”的需求变动。
Abstract Factory模式解决“系列对象”的需求变化,Builder模式解决“对象部分”的需求变化。Builder械通常和Composite模式组合使用。
5、原型模式(Prototype)
依赖关系倒置:
抽象不应该依赖于 实现细节,实现细节应该依赖于抽象。
动机(Motivate):
在软件系统中,经常面临着“某些结构复杂的对象”的创建工作;由于需求的变化,这些对象经常面临着
剧烈的变化,但是它们却拥有比较稳定一致的接口。
如何应对这种变化?如何向“客户程序(使用这些对象的程序)"隔离出“这些易变对象”,从而使得“依赖这些易变对象的客户程序”不随着需求改变而改变?
意图(Intent):
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
----摘自《设计模式》
结构图(Struct):
生活例子:
适用性:
1.当一个系统应该独立于它的产品创建,构成和表示时;
2.当要实例化的类是在运行时刻指定时,例如,通过动态装载;
3.为了避免创建一个与产品类层次平行的工厂类层次时;
4.当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
示意性代码例子:
public abstract class NormalActor { public abstract NormalActor clone(); } public class NormalActorA : NormalActor { public override NormalActor clone() { Console.WriteLine("NormalActorA is call"); return (NormalActor)this.MemberwiseClone(); } } public class NormalActorB : NormalActor { public override NormalActor clone() { Console.WriteLine("NormalActorB was called"); return (NormalActor)this.MemberwiseClone(); } } public class GameSystem { public void Run(NormalActor normalActor) { NormalActor normalActor1 = normalActor.clone(); NormalActor normalActor2 = normalActor.clone(); NormalActor normalActor3 = normalActor.clone(); NormalActor normalActor4 = normalActor.clone(); NormalActor normalActor5 = normalActor.clone(); } } class Program { static void Main(string[] args) { GameSystem gameSystem = new GameSystem(); gameSystem.Run(new NormalActorA()); } }
如果又需要创建新的对象(flyActor),只需创建此抽象类,然后具体类进行克隆。
public abstract class FlyActor { public abstract FlyActor clone(); } public class FlyActorB : FlyActor { /// <summary> /// 浅拷贝,如果用深拷贝,可使用序列化 /// </summary> /// <returns></returns> public override FlyActor clone() { return (FlyActor)this.MemberwiseClone(); } }
此时,调用的Main()函数只需如下:
class Program { static void Main(string[] args) { GameSystem gameSystem = new GameSystem(); gameSystem.Run(new NormalActorA(), new FlyActorB()); } }
Prototype的几个要点:
Prototype模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这
些“易变类”拥有“稳定的接口”。
Prototype模式对于“如何创建易变类的实体对象“采用“原型克隆”的方法来做,它使得我们可以
非常灵活地动态创建“拥有某些稳定接口中”的新对象----所需工作仅仅是注册的地方不断地Clone.
Prototype模式中的Clone方法可以利用.net中的object类的memberwiseClone()方法或者序列化来实现深拷贝。
有关创建型模式的讨论:
Singleton模式解决的是实体对象个数的问题。除了Singleton之外,其他创建型模式解决的是都是new 所带来的耦合关系。
Factory Method ,Abstract Factory,Builder都需要一个额外的工厂类来负责实例化“易变对象”,而Prototype则是通过原型(一个特殊的工厂类)来克隆“易变对象”。
如果遇到“易变类”,起初的设计通常从Factory Mehtod开始,当遇到更多的复杂变化时,再考虑重重构为其他三种工厂模式(Abstract
Factory,Builder,Prototype).