概念:
抽象工厂模式(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,解除分支判断带来的耦合。
反射代码我就不写了。