概念:
抽象工厂模式(Abstract Factory),提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。
上面这段话,我到现在还是不怎么明白。分析:首先抽象工厂是提供一个接口,什么样的接口?创建对象的接口。创建什么对象?一系列相关或相互依赖的对象。而且创建了这个接口之后,不需要指定这个接口的具体的类。还是不明白?
百度百科是这么说的:抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。根据LSP原则,任何接受父类型的地方,都应当能够接受子类型。因此,实际上系统所需要的,仅仅是类型与这些抽象产品角色相同的一些实例,而不是这些抽象产品的实例。换言之,也就是这些抽象产品的具体子类的实例。工厂类负责创建抽象产品的具体子类的实例。
UML类图:
从左到右,从上到下,依次是:客户端,客户端关联抽象工厂,抽象产品A,抽象产品B。然后抽象产品A,有两个具体子类:具体产品1类和具体产品2类(他们的实例化对象就是我们要创建的产品对象)。同理,抽象产品B。最后就是,抽象工厂,以及他的2个具体工厂。他们都有两个方法,用来创建具体的产品。
代码:
分析:还是汽车的例子。由于生意越做越大,所以要扩展行业。决定,工厂不只生产汽车,还生产汽车运动服装。(这只是为了说明汽车类和服装类不是同一种产品,真实的工厂是不会一边生产汽车,还生产衣服的)。
运动服装(SportsWear),两种牌子的宝马牌(BMWwear)和奔驰牌子(Benzwear)。其实衣服的牌子也可以完全和汽车没有关系。本来我打算用的是耐克(NIKE)和阿迪达斯(Adidas),但是后来写代码的时候,是在奔驰工厂或宝马工厂中的创建服饰,我就觉得在奔驰工厂中生产耐克,额~,觉得有些问题,就把耐克改成了奔驰服装,这样理解也方法。奔驰工厂生产的东西,带上标识,汽车就是奔驰汽车,衣服就是奔驰衣服,手机就是奔驰手机。
不过现在想想耐克其实也是可以的吧,大不了我说明下奔驰工厂生产的服装就是叫耐克服装也没什么,不过我代码已完成就不该了。
UML类图:
为了简化问题,所有的产品类都只有一个方法,Show。然后工厂类增加一个方法CreateSportsWear,返回值是ISportsWear类型。
代码:
Namespace CarAbstractFactory { class Program { static void Main(string[] args) { //最后,客户端修改下,首先要实例化具体工厂. //然后具体工厂创建具体的车, //具体的车创建完成了,自己展示下. Factory factory; ICar car; //客户端是通过他们的抽象接口(ICar)操纵实例 //比较部分,开始 factory = new BenzFactory(); car=factory.CreateCar();//产品的具体类(Benz)名也被具体工厂(BenzFactory)的实现(factory)分离,(Benz)没有出现在客户端中 car.Show(); //比较部分,结束 //然后是具体工厂创建具体的服饰,然后具体服饰显示下,表明创建是成功的 ISportsWear benzsports =factory.CreateSportsWear(); benzsports.Show(); //为了体现抽象工厂的易于交换产品系统的优点。我把代码修改了一下,可以比较客户端两段代码, //两段代码只有new的对象不一样,其他都一样,所以要修改是非常方便的。 factory = new BMWFactory(); car = factory.CreateCar(); car.Show(); //同理 ISportsWear bmwsports =factory.CreateSportsWear(); bmwsports.Show(); Console.Read(); } } //首先还是抽象产品角色,汽车接口(产品角色都没变,不管抽象产品还是具体产品) interface ICar { void Show(); } //然后是两个具体产品角色,宝马和奔驰,他们都要实现ICar接口 class BMW:ICar { public void Show() { //内容很简单,只是输出一句话 Console.WriteLine("一辆宝马诞生了!!!"); } } //Benz类 class Benz:ICar { public void Show() { Console.WriteLine("我是一辆奔驰!!!"); } } //增加一个抽象产品系列,运动服装类 interface ISportsWear { void Show();//只有这个方法,显示 } //接着具体的运动服装产品类 class BMWwear : ISportsWear { //实现接口的方法 public void Show() { Console.WriteLine("宝马服饰"); } } //奔驰服饰 class Benzwear : ISportsWear { public void Show() { //这个效果和"Benzwear服饰"是一样的. Console.WriteLine("{0}服饰 ",this.GetType().Name); } } //然后要增加工厂类的方法,增加CreateSportsWear方法, //抽象工厂的名字从CarFactory改为Factory,因为现在不只是生产车子。 abstract class Factory { //是个抽象方法,修改了下这个方法,原来是CarCreate,改成了CreateCar(我类图是这么写的) public abstract ICar CreateCar(); //增加抽象方法,子类要实现 public abstract ISportsWearCreateSportsWear(); } //新开的两个工厂 //宝马工厂 class BMWFactory : Factory { //实现父类的抽象方法 public override ICar CreateCar() { //CreateCar方法,它就是实例化个宝马类,也就是子类决定实例化哪一个类.类的实例延迟到子类. return new BMW(); } public override ISportsWearCreateSportsWear() { //创建一种衣服,宝马服装 return new BMWwear(); } } //奔驰工厂,同宝马工厂 class BenzFactory :Factory { public override ICar CreateCar() { return new Benz(); } //和上面的相比,方法名一样,参数一样的,但是因为对象不一样,所以返回的结果不一样,是多态. public override ISportsWear CreateSportsWear() { //创建一种衣服,宝马服装 return new Benzwear(); } } }
运行结果:
抽象工厂的优点是,易于交换产品系列,如我客户端中表示的,变换工厂只需要修改一个地方就可以。第二个优点是,让具体的创建实例过程和客户端分离,客户端是通过他们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户端中。
但是他的缺点,如果我要在生产别的东西。比如我要增加一个生产家具的类,那么我就要增加一个家具的抽象类,两个具体的家具类,一个奔驰家具类和一个宝马家具类。然后还要增加方法CreateFurniture,就要更改Factory,BenzFactory和BMWFactory。要改三个类,太糟糕了。
其实就上面的修改三个类,我觉得已经违背了开发-封闭原则,但是好像没有谁说这是修改,而是说这是扩展。我只好往这上面凑,觉得他们是增加了一个方法,而没有修改其他的地方,所以算是扩展。
可以用简单工厂来改进抽象工厂,简单工厂就是把3个工厂类又简化成一个工厂类,然后具体要实例化那个产品类,肯定要有switch…case或if语句来选择。而使用switch…case或if语句不好,因为分支判断会耦合性高,然后使用反射来去除switch或if,解除分支判断带来的耦合。
反射代码我就不写了。