设计模式学习-抽象工厂模式
抽象工厂模式与工厂方法模式最大的区别在于:工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则针对的是多个产品等级结构。所以在抽象工厂模式中经常会用到产品族这一概念,它指的是位于不同的产品等级结构中,并且功能互相关联的产品系列。
UML类图:
其中类与对象的关系为:
- AbstractFactory:抽象工厂
声明生成抽象产品的方法
- ConcreteFactory:具体工厂
执行生成抽象产品的方法,生成一个具体的产品
- AbstractProduct:抽象产品
为一种产品声明接口
- Product:具体产品
定义具体工厂生产的具体产品的对象,实现产品接口
- Client:客户
应用程序,使用抽象产品和抽象工厂生成对象。
为了了解对象的创建和产品的生成过程,我们看一下抽象工厂典型应用的顺序图:
优势劣势:
抽象工厂模式主要优点是隔离了具体类的生成,使得客户不需要知道什么被创建了。由于这种隔离,使得更换一个具体的工厂变得相对容易,该模式符合GRASP纯虚构的模式,实现了高内聚。主要缺点是,在添加新的产品对象时,难以扩展抽象工厂,因为抽象工厂接口规定了所有可能被创建的产品集合,如果要支持新种类的产品,需要对工厂接口进行扩展,这就涉及到修改既有代码,违反了开闭原则。
应用场景:
这个模式想必大家都十分熟悉,在开发中也会经常用到,比如说多数据库支持,另外在下面的情景,可以考虑使用抽象工厂模式:
- 系统需要屏蔽有关对象如何创建、如何组织和如何表示
- 系统需要由关联的多个对象来构成
- 有关联的多个对象需要一起应用并且它们的约束是强迫的,不可分离的
- 提供一组对象而不显示它们的实现过程,只显示它们的接口
应用示例:
下面实现一个有信息保存和日志保存的小模块,要求该模块可以方便的将保存的位置在数据库和XML之间进行切换。
由于保存的位置不定,所以信息保存和日志保存会针对不同的位置,有不同的处理形式,而针对一种保存方式而言,信息和日志的保存又形成一族,针对这些特点,我们采用抽象工厂模式来实现该模块。
首先,我们先将信息保存和日志保存抽象出来:
{
public abstract void SaveMessage();
}
public abstract class LogDal
{
public abstract void SaveLog();
}
然后,我们根据不同的保存形式,分别实现抽象类:
{
public override void SaveMessage()
{
Console.WriteLine("信息保存到数据库中");
}
}
public class XmlMessageDal : MessageDal
{
public override void SaveMessage()
{
Console.WriteLine("信息保存到XML中");
}
}
public class SqlLogDal : LogDal
{
public override void SaveLog()
{
Console.WriteLine("日志保存到数据库中");
}
}
public class XmlLogDal : LogDal
{
public override void SaveLog()
{
Console.WriteLine("日志保存到XML中");
}
}
{
public abstract MessageDal CreateMessageDal();
public abstract LogDal CreateLogDal();
}
public class SqlFactory : AbstractFactory {
public override MessageDal CreateMessageDal()
{
return new SqlMessageDal();
}
public override LogDal CreateLogDal()
{
return new SqlLogDal();
}
}
public class XmlFactory : AbstractFactory {
public override MessageDal CreateMessageDal()
{
return new XmlMessageDal();
}
public override LogDal CreateLogDal()
{
return new XmlLogDal();
}
}
{
static void Main() {
AbstractFactory factory = new SqlFactory();
MessageDal msg = factory.CreateMessageDal();
LogDal log = factory.CreateLogDal();
msg.SaveMessage();
log.SaveLog();
factory = new XmlFactory();
msg = factory.CreateMessageDal();
log = factory.CreateLogDal();
msg.SaveMessage();
log.SaveLog();
Console.ReadLine();
}
}
至此我们的小模块实现完成了,保存位置修改,只需生成不同的工厂实例就可以了。
实际的生产环境,我们肯定一般只需要保存到一个位置,上面的客户端代码为了演示,将2个位置都保存了,代码中,为了切换到XML保存,我们还是修改了客户端代码,作为一个有追求的码农,这是我们不想看到的。所以,我们采用反射修改一下我们的客户端调用代码:
AbstractFactory factory = (AbstractFactory)Assembly.Load("Gof").CreateInstance("Gof.AbstractFactory.SqlFactory");
MessageDal msg = factory.CreateMessageDal();
LogDal log = factory.CreateLogDal();
msg.SaveMessage();
log.SaveLog();
Console.ReadLine();
}
由于创建实际工厂采用了反射形式,所以现在我们可以通过配置文件决定我们的保存位置了,客户端代码也就无需修改了。这里不在赘述...