看了一下抽象工厂模式,有一点点体会,还是先写下来,免得拖久了就忘了。
下面这个图画的挺好的,所以就把它复制过来。
为了方便引进抽象工厂模式,引进一个新概念:产品族(Product Family)。所谓产品族,是指位于不同产品等级结构,功能相关联的产品组成的家族。如图:
图中一共有四个产品族,分布于三个不同的产品等级结构中。只要指明一个产品所处的产品族以及它所属的等级结构,就可以唯一的确定这个产品。
引进抽象工厂模式
所谓的抽象工厂是指一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象。如果用图来描述的话,如下图:
抽象工厂模式由两个部分组成:工厂和产品。所以就应该有两个或者以上的虚父类(也可以用接口),分别是工厂的虚父类和产品的虚父类(有几个产品就应该有几个虚父类)。工厂和产品的关系是:一个工厂可以生产多个产品,不仅仅只有一个,所以在抽象工厂模式中一个具体工厂可以产生不同的产品对象。
抽象工厂的类扮演的角色是工厂模式的核心,但它与应用系统商业模式无关。在这个虚基类中定义的是:工厂中制造的所有产品,例如下面的例子中的Cpu1和Cpu2,代表Cpu的不同型号,如果还有其他型号或者产品则在这里面加上它。整个是一个抽象的东西:一个工厂里面制造这些产品。在这个类里面的抽象方法是返回每一个产品的对象,因为这个虚类被具体的工厂继承之后会产品具体的产品,而每一个产品类都会继承这个抽象工厂类中的一个抽象产品类,从而只有在客户端调用时才会产生具体的类。抽象工厂的一个缺点是一旦你在抽象工厂类中确定了所有产品,那么久不能够再添加任何的产品了,否则要改的话就会动全部的子类,这违背了“开放-关闭”原则。
具体工厂:直接在客户端的调用下创建产品的实例,这个角色含有选择合适产品对象的逻辑。
抽象产品角色:担任这个角色的类是工厂方法模式所创建的对象的父类,或它们共同拥有的接口。
具体产品角色:这个角色代表具体的产品。
上面四种角色的关系是:抽象工厂对应的是抽象产品角色(抽象工厂中有抽象产品),具体工厂对应具体的产品(具体的工厂生产具体的产品),而具体工厂和具体产品分别继承于抽象工厂和抽象产品。客户端调用时会先去产生一个具体工厂的对象赋给父类抽象工厂类,然后再用这个对象去制造这个具体的工厂中的具体的产品赋值给具体的产品,然后这个产品对象去调用它相应的方法。
下面的抽象工厂模式结构图还是挺清楚的:
假设一个抽象工厂下面有三个具体的工厂:Mac,IBM,PC,这三个工厂都可以做Cpu1和CPU2。
namespace AbstractFactory
{
abstract class AbsComputerFactory
{
abstract public Cpu createCpu1();
abstract public Cpu createCpu2();
}
class IBMComputerFactory:AbsComputerFactory
{
public override Cpu createCpu1()
{
return new IBMCpu1();
}
public override Cpu createCpu2()
{
return new IBMCpu2();
}
}
class MacComputerFactory:AbsComputerFactory
{
public override Cpu createCpu1()
{
return new MacCpu1();
}
public override Cpu createCpu2()
{
return new MacCpu2();
}
}
class PCComputerFactory:AbsComputerFactory
{
public override Cpu createCpu1()
{
return new PCCpu1();
}
public override Cpu createCpu2()
{
return new PCCpu2();
}
}
abstract class Cpu
{
abstract public void makeCpu();
}
class IBMCpu1:Cpu
{
public override void makeCpu()
{
Console.WriteLine("This is IBM PC," + this.GetType());
}
}
class IBMCpu2:Cpu
{
public override void makeCpu()
{
Console.WriteLine("This is IBM Cpu," + this.GetType());
}
}
class MacCpu1:Cpu
{
public override void makeCpu()
{
Console.WriteLine("This is Mac cpu1," + this.GetType().ToString());
}
}
class MacCpu2:Cpu
{
public override void makeCpu()
{
Console.WriteLine("This is Mac cpu2," + this.GetType().ToString());
}
}
class PCCpu1:Cpu
{
public override void makeCpu()
{
Console.WriteLine("This is PC cpu1," + this.GetType().ToString());
}
}
class PCCpu2:Cpu
{
public override void makeCpu()
{
Console.WriteLine("This is PC cpu2,"+this.GetType().ToString());
}
}
class Program
{
static void Main(string[] args)
{
AbsComputerFactory acf = new PCComputerFactory();
Cpu PCCpu_1 = acf.createCpu1();
PCCpu_1.makeCpu();
Cpu PCCpu_2 = acf.createCpu2();
PCCpu_2.makeCpu();
AbsComputerFactory acf2 = new MacComputerFactory();
Cpu MacCpu_1 = acf2.createCpu1();
MacCpu_1.makeCpu();
Cpu MacCpu_2 = acf2.createCpu2();
MacCpu_2.makeCpu();
AbsComputerFactory acf3 = new IBMComputerFactory();
Cpu IBMCpu_1 = acf3.createCpu1();
IBMCpu_1.makeCpu();
Cpu IBMCpu_2 = acf3.createCpu2();
IBMCpu_2.makeCpu();
Console.ReadKey();
}
}
}
抽象工厂模式可以解决有层级关系的问题,但是它一旦确定好产品之后就不能再增加产品的种类,但是如果想要再增加一个工厂HP,那么这时只需要增加几个类就可以了。这体现了“对扩展开放,对修改关闭”原则。
在以下情况下应当考虑使用抽象工厂模式:(摘录)
- 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。
- 这个系统有多于一个的产品族,而系统只消费其中某一产品族。
- 同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。
- 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。
在客户端直接调用的时候是直接创建了产品的实例,这样写时则说明了编译的时候对象就已经确定了。然而用反射的时候则是为了在运行时确定对象,这样可以很灵活的去读取配置文件中的信息从而去创建一个需要的对象。举例如下:在程序中的App.config中添加下面的代码:
<appSettings>
<add key="CpuModel" value="HPCpu1"/>
</appSettings>
在客户端代码中添加using System.Reflection;using Configuration;
可以在客户端中添加如下代码:
string CpuModel = ConfigurationSettings.AppSettings["CpuModel"].ToString();
Cpu1 hpCpu1 = (Cpu1)Assembly.Load("AbstractFactory").CreateInstance("AbstractFactory."+CpuModel);
hpCpu1.makeCpu1();
Load后面的是程序集的名称,CreateInstance后面的是空间名称.产品类的名称。通过反射可以字符串以及修改它的配置文件进行动态加载。
反射就是通过程序集获得类的对象。