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


    模式概述
    提供一个创建一系列相关或相互依赖对象的接口,而无需制定它们具体的类,抽象工厂(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();
            }

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

  • 相关阅读:
    (12)springboot打war包-copy
    基于Python在MacOS上安装robotframework-ride
    mac终端输入python默认打开python3
    win10 切换网卡的bat
    PyCharm2018激活码
    我告诉你 ,一个 window免费系统下载的网站!
    oracle 子查询的几个种类
    trunc()
    case when then else end 与 decode 的区别
    触发器 of oracle
  • 原文地址:https://www.cnblogs.com/kdalan/p/2554153.html
Copyright © 2020-2023  润新知