设计模式-创建型模式-工厂模式

创建型模式是为了隔离客户程序与具体类型实例化的依赖关系,通过将实例化职责委托他方法对象的方法,保证客户程序(或外部系统)获得期望具体类型实例的同时不必发生直接的引用。

概念:

工厂方法是整个创建型模式中最为典型的、也是最具启发效果的,它告诉我们使用一个变化频率比较高的类不比忙着new(),而是要依赖一个抽象的类型(抽象类或者接口)。Delegate也是一个抽象,与抽象类型不同,它是对一类方法的抽象,而不像前两者是对一组方法的抽象。哪一种更好呢。

尺有所长,寸有所短。

如果需要的仅仅是某个特定的操作,那么大可不必按照抽象类型来加工,反馈一个Delegate实例就可以了。如果需要的是抽象业务实体,或者是具有一组“操作+属性”的抽象类型,那么就循规蹈矩好了。

使用工厂方法的主要动机来自于“变化”,应用的哪些部分会快速变化呢?不一定,多数项目都会一个相对稳定的核心。无论是被叫做Framework,还是现在更时髦的、感觉更上层的Foundation、Core等等。这个部分相对比较稳定,言外之意其他部分会“相对”变化的比较频繁。

我们可能期待架构师设计一个很灵活的架构,这样开发的时候就可以“填空”,而且是Plug&&Play方法的:我们也可以期待需求分析人员把需求分析得特别透彻,这样我们代码真的Write Once Run Always了。当然,几人是Team工作,就有上下家,周围还有共同努力的同事们,我们希望上家的变化尽可能的少,同时处于人际工程学的需要,我们也尽量不为下家找麻烦。

很难。

基于几口的开发虽然不能解决上述那么多问题,但起码可以从很多方面减轻这些工作负担。如果想贯彻这种思想,那么首先要从类型结构上解决问题。否则后面的都是空谈。(当然,可以通过正交的方法,或者一般成为“拦截”的方式在具体方法执行上提供灵活性。)

最简单的工厂:

public class Simple
    {
        public interface IProduct { }

        public class ConcreteProductA : IProduct { }
        public class ConcreteProductB : IProduct { }

        public class Factory
        {
            //由工厂决定到底实例话哪个子类。
            public IProduct Create()
            {
                return new ConcreteProductA();
            }
        }
    }

  单元测试:

[TestMethod]
        public void TestMethod1()
        {
            var factory = new Simple.Factory();
            var product = factory.Create();
            Assert.AreEqual(product.GetType(), typeof(Simple.ConcreteProductA));
        }

  这个工厂模式与直接New()有什么不同?加入把它放到工程中应用有是不同的地方?

1.通过IProduct隔离了客户程序与具体ConcreteProdcutX的依赖关系,在客户程序视野内根本就没有ConcreteProductX.

2.即使ConcreteProductX增加、删除方法或者属性,也没有关系。只要按照要求实现了Iproduct就可以 ,Client无须关心ConcreteProductX的变化 (确切的说它也关系不到。。。)

3.想对直接写个ConcreteProductX而言,要平白的多写一个工厂出来,尤其当需要Iproduct频繁变化的时候,客户程序也闲不下来。

4.好的需求分析师在可以实施之前分析清楚85%的需求,好的架构师在把这些需求转换为实际技术框架的时候大概能做到90%的忠于需求,作为开发人员,设计的时候能够详细刻画95%的内容就很不错了,这样100%-85%*90%*95%=27.3%,也就是说,即使在一个精英团队,也可能四分之一的内容到了编码的时候还无法得到精确分析,那么对应某个局部领域而言,很难有效抽象这个Iproduct;但Factory提示在第一遍Draft的时候,我们就可以直接New(),但在复查或迭代的过程中一定要尽量找到Iproduct,然后套个Factory,在公共代码部分,更是如此。

此外,还有个效率的问题,如果构造一个Factory实例,并用它获取一个抽象类型实例后就不再使用,资源上有些浪费,尤其在这个工程非常频繁的时候。工程中可以通过如下集中方式解决:

1.把工厂实例作为参数注入到操作中,而不是在每个方法内部自行创建,类似:

 private string _connectionString;
            private DbProviderFactory _dbProviderFactory;
            protected DataBase(string connectionString, DbProviderFactory dbProviderFactory)
            {
                _connectionString = connectionString;
                _dbProviderFactory = dbProviderFactory;
            }

            public virtual DbConnection CreateConnection()
            {
                DbConnection newConnection = _dbProviderFactory.CreateConnection();
                newConnection.ConnectionString = _connectionString;
                return newConnection;
            }

        2.把工厂设计为单例模式,因为工厂的职责相对单一,所有客户端需要的加工过程使用的都是一个唯一的共享实例。

3.使用静态类,不过注意:静态类不能被继承,它只能从object继承,没有其他可能。所以看起来静态类有些不想“面向对象”的味道。

public enum Category
    {
        A, B
    }

    public static class ProductFactory
    {
        public static Simple.IProduct Create(Category category)
        {
            switch (category)
            {
                case Category.A: return new Simple.ConcreteProductA();
                case Category.B: return new Simple.ConcreteProductB();
                default: throw new NotSupportedException();
            }
        }
    }

  参数化工厂

上面的那个简单工厂,只能是个Example,充其量是个模型。因为我们有两个实现类,如果没有任何机制告诉Factory该构造谁,这个工厂只能采用上面那个A或者随机产生A/B之中某一个实例的方法,这是不可以的,如果想让工厂在运行的时候有效选择符合抽象类型要求的某个实例,最简单的就是传递一个参数。它可以是一个字符串,一个枚举值等等。

简单工厂的局限性

上面的简单工厂比较优雅的解决了外部new()的问题,它把目标实例的创建工作交给一个外部的工厂完成,但是如果应用中需要工厂的类型只有一个,而且工厂的职责又非常单纯——就是一个new()的替代品,类似我们面向对象中最普遍的思路,这时候就需要进一步抽象了,于是出现了新的发展:工厂方法模式和抽象工厂模式。

工厂方法模式

作为简单工厂的一个扩展,工厂方法的意图也非常明确,它把类的实例化过程延迟到子类,将new()的工作交给工厂完成。同时,增加一个抽象的工厂定义,解决一系列具有统一通用工厂方法的实体工厂问题。它适合以下场景。

1.客户程序需要隔离它与需要创建的具体类型间的耦合关系。

2.很多情况下,客户程序在开发过程中还无法预知生产环境中实际要提供给客户程序创建的具体类型。

3.将创建型工作隔离在客户程序之外,客户程序仅需要执行自己的业务逻辑,把这部分职责交给外部对象完成。

4.如果目标对象虽然可以统一抽象为某个抽象类型,但他们的继承关系太过复杂,层次性比较复杂。

经典工厂方法主要有四个角色。

1.抽象产品类型(Product):工厂要加工的对象所具有的抽象特征实体。

2。具体产品类型(Concrete Product) :实现客户程序所需要的抽象特质的类型,它就是工厂需要延迟实例的备选对象。

3.声明的抽象工厂类型(Creator):定义一个工厂方法的默认实现,它返回抽象产品类型Product.

4.重新定义创建过程的具体工厂类型(Concrete Creator),返回具体产品类型Concrete Product的具体工厂。

代码如下:

/// <summary>
    /// 抽象产品类型
    /// </summary>
    public interface IProduct
    {
        string Name { get; }//约定的抽象产品所必须具有的特征
    }
    //具体产品类型
    public class ProductA : IProduct
    {
        public string Name => "A";
    }

    public class ProductB : IProduct
    {
        public string Name => "B";

    }

    public interface IFactory//抽象工厂
    {
        IProduct Create();//每个工厂所需要具有的工厂方法--创建产品
    }

    //抽象工厂的实例
    public class FactoryA : IFactory
    {
        public IProduct Create()
        {
            return new ProductA();
        }
    }

    public class FactoryB : IFactory
    {
        public IProduct Create()
        {
            return new ProductB();
        }
    }

   通过这个例子可以看到,当客户程序作为Iproduct消费者的时候,抽象类型在虚拟构造的时候到底用哪个实体类型由Factory决定。客户程序需要使用Iproduct的时候通过访问Ifactory所定义的Create()方法就可以获得一个产品实例。直观上客户程序可以把频繁变化的某个Product隔离在自己的视线之外,自身不会收到它的影响。

 解耦Concrete Factory与客户程序

虽然经典工厂方法模式告诉了我们最后的结果,通过Prodcut、Concrete Product、Factory和Concrete Factory四个角色解决了客户程序对Product的获取问题,但没有把客户程序和Factory连起来,也就是说,没有介绍如何把Factory放到客户程序里。示例代码如下:

public class Client
    {
        public void SomeMethod()
        {
            IFactory factory = new FactoryA();//获取了抽象的Factory的同时与FactoryA产生了依赖。

            IProduct product = factory.Create();//后续操作仅依赖抽象的IFactory和Iprodcut。
        }
    }

  如果让客户程序来直接new()某个Concrete Factory,由于Concrete Factor依赖与Concrete Product,因此还是形成了间接的实体对象依赖关系,背离了这个模式的初始意图。怎么办?

依赖注入,通过依赖注入将客户程序需要的某个IFactory传递给它。

实际工程中很多时候不得不做这种工作——将Concrete Product与客户程序分离,其原因是我们不想让上游开发人员的修改迫使我们必须重新Check Out代码,编译、测试再Check in,这种工作在项目开发的中后期常常是令人非常厌恶的,所以,我们把这些烂摊子交给依赖注入。

以下是实现依赖注入的Assembler的代码。

 public class Assembler //一个手动实现依赖注入的中间部分
    {
        /// <summary>
        /// 保存"抽象类型/实体类型"对应关系的字典
        /// </summary>
        private static readonly Dictionary<Type, Type> Dictionary = new Dictionary<Type, Type>();

        static Assembler()
        {
            //注册抽象类型需要使用的实体类型
            //实际的配置信息可以从外层机制获取,例如通过配置获取
            //Dictionary.Add(typeof(ITimerProvider), typeof(SystemTimeprovider));
            Dictionary.Add(typeof (IFactory), typeof (FactoryA));//增加一个注册项
        }

        /// <summary>
        /// 根据客户程序需要的抽象类型选择相应的实体类型,并返回类型实例
        /// </summary>
        /// <param name="type">实体类型实例</param>
        /// <returns></returns>
        public object Create(Type type)//主要用于非泛型方式调用
        {
            if ((type == null) || !Dictionary.ContainsKey(type)) throw new NullReferenceException();
            var targetType = Dictionary[type];
            return Activator.CreateInstance(targetType);
        }

        /// <summary>
        /// 抽象类型(抽象类/接口/或者某种基类)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T Create<T>()//主要用于泛型方式调用
        {
            return (T)Create(typeof(T));
        }
    }

  相应的客户程序就可以修改为依赖Assember的方式

  

public class ClientA
    {
        private readonly IFactory _factory;
        public ClientA(IFactory factory)//通过构造器方式注入
        {
            if (factory == null) throw new ArgumentNullException();
            _factory = factory;
        }
        public IProduct GetProduct() => _factory.Create();
    }

  单元测试代码:

 public void Test()
        {
            var factory = (new Assembler()).Create<IFactory>();
            var clienta = new ClientA(factory);//注入IFactory
            var product = clienta.GetProduct();//通过IFactory获取IProduct
            Assert.AreEqual("A", product.Name);
        }

  这样的话,即使是在系统上线之后,如果还需要修改IFactory的具体类型,一样可以通过增加新的程序集,在生产环境中更新相关IFactory需要使用的具体工厂类型。在这种方式下,客户程序成为一个可以动态加载的框架,外部机制很容易通过配置将新完成的程序集部署到运行环境中,而在经典的设计模式实现中,如果需要把模式工程话,很多时候需要借助外部的配置。

学习内容,记录下来。

时间: 2024-08-11 21:09:45

设计模式-创建型模式-工厂模式的相关文章

创建型-抽象工厂模式学习

1.抽象工厂模式的意图: 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类. 2.抽象工厂模式的适用性: 一个系统要独立于它的产品的创建.组合和表示时. 一个系统要由多个产品系列中的一个来配置时. 当你要强调一系列相关的产品对象的设计以便进行联合使用时. 当你提供一个产品类库,而只想显示它们的接口而不是实现时. 3.场景描述: 考虑一个生产多种不同风格的家具的工厂(FurnitureFactory),不同风格的家具系列可以提供不同的门.窗.地板等的组合,为同一所住房可以提供不同

设计模式之创建型抽象工厂模式

通过对类的工厂抽象使其业务用于对产品类簇的创建,而不负责某一类产品的实例.抽象类是一种声明但不能使用的类,当你使用的时候就会报错.JavaScript中的抽象类不能像传统面向对象语言那样轻松地创建,我们可以在类的方法中手动抛出错误来模拟抽象类.你可能会想,这样的类什么都不能做有什么用?其实它在继承上是很有用的.抽象工厂模式不能用来创建具体对象,一般用它作为父类创建一些子类. // 抽象工厂方法 var PageFactory = function(parent, child) { // 判断抽象

设计模式之创建型简单工厂模式

简单工厂模式是由一个方法来决定到底要创建哪个类的实例,而这些实例经常都拥有相同的接口.这种模式主要用在所实例化的类型在编译期并不能确定,而是在执行期决定的情况. 说的通俗点,就像公司茶水间的饮料机,要咖啡还是牛奶取决于你按哪个按钮. 由一个工厂对象决定创建某一种产品对象类的实例,主要用来创建同一类对象.用简单工厂来创建对象,可以减少全局变量创建提高代码复用率,它的使用场合限制在创建单一对象. // MyCss基类 var MyCss = function() { this.intro = '用于

设计模式(创建型)之原型模式(Prototype Pattern)

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! 概述 原型模式是一种创建型设计模式,它通过复制一个已经存在的实例来返回新的实例,而不是新建实例.被复制的实例就是我们所称的原型,这个原型是可定制的.原型模式多用于创建复杂的或者耗时的实例, 因为这种情况下,复制一个已经存在的实例可以使程序运行更高效,或者创建值相等,只是命名不一样的同类数据. 原型模式要求对象实现一个可以"克隆"自身的接口,这样就可以通过

设计模式(创建型)之建造者模式(Builder Pattern)

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! 概述 建造者模式将客户端与包含多个组成部分的复杂对象的创建过程分离,客户端压根不用知道复杂对象的内部组成部分与装配方式,只需要知道所需建造者的类型即可.它关注如何一步一步创建一个的复杂对象,不同的具体建造者定义了不同的创建过程,且具体建造者相互独立,增加新的建造者非常方便,无须修改已有代码,系统具有较好的扩展性. 问题来了... 你可能会有疑惑,建造者模式和抽象工

01.创建型————简单工厂模式

package Create.a.SimpleFactory; //业务接口 public interface Salary { void calcSalary(); } package Create.a.SimpleFactory; //业务实现类 public class HeBeiSalary implements Salary { @Override public void calcSalary() { // TODO Auto-generated method stub System.

设计模式 创建型 抽象工厂

package com.mozq.base; /** * 抽象工厂:提供一个创建一系列相关对象的接口,而不是指定它们具体的类. * 1.抽象工厂 * 2.抽象产品 * 3.具体产品 * 4.具体工厂 * 5.客户端使用某系列产品 * 使用步骤: * 1.工厂后于产品.//工厂就是生产产品的,产品都没有设计好,无法定义工厂的功能. * 2.具体后于抽象. * 必须先定义抽象产品,才能定义生产该抽象产品的抽象工厂. * 必须先定义具体产品,才能定义生产该具体产品的具体工厂. 与其他模式关系: 因为具

【C#设计模式——创建型模式】抽象工厂模式

抽象工厂模式比工厂模式具有更高层次的抽象性.当要返回一系列相关类中的某一个,而每个类都能根据需要返回不同的对象时,可以选择这种模式.直接进入示例. 示例描述:完成花园的规划,多种花园种类,每个里面多种植物 编写一个基类Garden,Garden就是抽象工厂.它定义了具体类中的方法,并返回一系列相关类中的某个类. public class Garden { protected Plant center, shade, border; protected bool showCenter, showS

JDK 源码 阅读 - 2 - 设计模式 - 创建型模式

A.创建型模式 抽象工厂(Abstract Factory) javax.xml.parsers.DocumentBuilderFactory DocumentBuilderFactory通过FactoryFinder实例化具体的Factory. 使用例子: DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = docBuilder

设计模式——创建型模式

简单的说我们可以把23种设计模式可以归为三大类,分别是创建型模式.结构型模式和行为型模式. 今天,首先看一下创建型模式.创建型设计模式包括5种:单例模式(Singleton).工厂方法模式(Factory Method).抽象工厂模式(Abstract Factory).建造者模式(Builder).原型模式(Prototype).  1.单例模式(Singleton)        1)简介 保证一个类仅有一个实例,并提供一个访问它的全局访问点. 单例模式是一种常用的软件设计模式.在它的核心结