设计模式学习-抽象工厂模式

模式概述
提供一个创建一系列相关或相互依赖对象的接口,而无需制定它们具体的类,抽象工厂(Abstract Factory)模式又称为Kit模式,属于对象创建型模式。

抽象工厂模式与工厂方法模式最大的区别在于:工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则针对的是多个产品等级结构。所以在抽象工厂模式中经常会用到产品族这一概念,它指的是位于不同的产品等级结构中,并且功能互相关联的产品系列。


UML类图

 

其中类与对象的关系为:

  • AbstractFactory:抽象工厂
    声明生成抽象产品的方法

  • ConcreteFactory:具体工厂
    执行生成抽象产品的方法,生成一个具体的产品

  • AbstractProduct:抽象产品
    为一种产品声明接口

  • Product:具体产品
    定义具体工厂生产的具体产品的对象,实现产品接口

  • Client:客户
    应用程序,使用抽象产品和抽象工厂生成对象。

为了了解对象的创建和产品的生成过程,我们看一下抽象工厂典型应用的顺序图:

优势劣势:

抽象工厂模式主要优点是隔离了具体类的生成,使得客户不需要知道什么被创建了。由于这种隔离,使得更换一个具体的工厂变得相对容易,该模式符合GRASP纯虚构的模式,实现了高内聚。主要缺点是,在添加新的产品对象时,难以扩展抽象工厂,因为抽象工厂接口规定了所有可能被创建的产品集合,如果要支持新种类的产品,需要对工厂接口进行扩展,这就涉及到修改既有代码,违反了开闭原则。
 

应用场景:

这个模式想必大家都十分熟悉,在开发中也会经常用到,比如说多数据库支持,另外在下面的情景,可以考虑使用抽象工厂模式:

  1. 系统需要屏蔽有关对象如何创建、如何组织和如何表示
  2. 系统需要由关联的多个对象来构成
  3. 有关联的多个对象需要一起应用并且它们的约束是强迫的,不可分离的
  4. 提供一组对象而不显示它们的实现过程,只显示它们的接口 


应用示例:

下面实现一个有信息保存和日志保存的小模块,要求该模块可以方便的将保存的位置在数据库和XML之间进行切换。

由于保存的位置不定,所以信息保存和日志保存会针对不同的位置,有不同的处理形式,而针对一种保存方式而言,信息和日志的保存又形成一族,针对这些特点,我们采用抽象工厂模式来实现该模块。

首先,我们先将信息保存和日志保存抽象出来:

    public abstract class MessageDal
    {
        public abstract void SaveMessage();
    }

    public abstract class LogDal
    {
        public abstract void SaveLog();
    }

然后,我们根据不同的保存形式,分别实现抽象类:

    public class SqlMessageDal : MessageDal 
    {
        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 class AbstractFactory 
    {
        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();
        }
    }
客户端调用如下:
    class Client
    {
        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保存,我们还是修改了客户端代码,作为一个有追求的码农,这是我们不想看到的。所以,我们采用反射修改一下我们的客户端调用代码:

        static void Main() {
            AbstractFactory factory = (AbstractFactory)Assembly.Load("Gof").CreateInstance("Gof.AbstractFactory.SqlFactory");
            MessageDal msg = factory.CreateMessageDal();
            LogDal log = factory.CreateLogDal();
            msg.SaveMessage();
            log.SaveLog();
            Console.ReadLine();
        }

由于创建实际工厂采用了反射形式,所以现在我们可以通过配置文件决定我们的保存位置了,客户端代码也就无需修改了。这里不在赘述...

posted @ 2012-06-19 23:32  kdalan  阅读(978)  评论(1编辑  收藏  举报