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

开发场景

      实际项目中经常需要加工一批对象,这时候如果按部就班的一个一个来生成,效率相对上比较低,最好专门设计独立的批量工厂。很难想象调用“啤酒Factory”的Create()方法的时候,要经过工厂的一些列处理,最后才产生一瓶啤酒,如果有人要搞一件啤酒的时候,就要等24次处理,这就不行了。所以有了批量工厂。

 

实际项目中的开发过程也与此类似,但需要新增两个步骤。

1.准备生产:之前的实现中,实际加工对象的步骤其实就一个new(),但之前必须编写很多访问配置文件,寻找抽象产品类型需要实例化的产品类型,项目中经常还需要增加很多其他比如记录日志、权限检查之类的操作,这些步骤都被划分到“准备生产”的过程里。

2.配货:这是个可选步骤,在此以实际生产过程做类比,比如我们产5000台手机,但是不一定全是一个颜色的,因此后续实际生产并不是for(int i=0;i<5000;i++)的单纯new(),还需要增加一个配货的过程。

依据单一职责的原则,需要增加三个对象。

1.指定生产的抽象类型(Director):它告诉客户程序在保持IFactory的前提下,生产某类产品的数量,同时由于具体某类产品是由Concrete Factory决定的,因此客户程序实际获得IFactory还需由有Director告诉Assembler并进行替换。例如上面那个手机的情景,就需要Direcotr在用完当前BlackMobileFactory生存3000部黑色手机后,将Clietn的IFactory换成RedMoblieFactory,继续生产2000台红色手机。

2.Director的每一个步骤成为一个决策(Decision):它包括两个信息,例如当决策生产2000台红色手机的时候,它包含数量(2000)和实际加工对象(红色手机实例)两项。

3.为批量的IProduct增加一个集合容器类型,项目中也可以直接使用.net提供的既有集合类型,同时修改Concrete Factory的加工方式,变返回某个单独的IProdcut为返回IProdcut集合。

另外,由于项目中批量的加工任务不一定只有一类产品,因此把Director这个"军师"放到客户程序中的任务,还是交给Assembly完成。

1.装载IProdcut的容器类型

 public class ProductCollection
    {
        private IList<IProduct> _data = new List<IProduct>();

        /// <summary>
        /// 对外的集合操作方法
        /// </summary>
        /// <param name="item"></param>
        public void Insert(IProduct item) { _data.Add(item); }

        public void Insert(IProduct[] items)
        {
            if (items == null || items.Length == 0) return;
            foreach (var item in items) { _data.Add(item); }
        }
        public void Remove(IProduct item) { _data.Remove(item); }
        public void Clear() { _data.Clear(); }

        /// <summary>
        /// 获取所有IProdcut内容的属性
        /// </summary>
        public IProduct[] Data
        {
            get
            {
                if (_data == null || _data.Count == 0) return null;
                var result = new IProduct[_data.Count];
                _data.CopyTo(result, 0);
                return result;
            }
        }
        /// <summary>
        /// 获取当前集合内的元素数量
        /// </summary>
        public int Count => _data.Count;

        /// <summary>
        /// 为了便于操作,重载的运算符
        /// </summary>
        /// <param name="collection"></param>
        /// <param name="items"></param>
        /// <returns></returns>
        public static ProductCollection operator +(ProductCollection collection, IProduct[] items)
        {
            var result = new ProductCollection();
            if (!(collection == null || collection.Count == 0)) result.Insert(collection.Data);
            if (!(items == null || items.Length == 0)) result.Insert(items);
            return result;
        }
        public static ProductCollection operator +(ProductCollection source, ProductCollection target)
        {
            var result = new ProductCollection();
            if (!(source == null || source.Count == 0)) result.Insert(source.Data);
            if (!(target == null || target.Count == 0)) result.Insert(target.Data);
            return result;
        }


    }
View Code

2.定义批量工厂和产品类型容器

 public interface IBatchFactory
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="quantity">待加工的产品数量</param>
        /// <returns></returns>
        ProductCollection Create(int quantity);
    }

    /// <summary>
    /// 为了方便提供的抽象基类
    /// </summary>
    /// <typeparam name="T">Concrete Product基类</typeparam>
    public class BatchProductFactoryBase<T> : IBatchFactory where T : IProduct, new()
    {
        public virtual ProductCollection Create(int quantity)
        {
            if (quantity <= 0) throw new ArgumentException();
            var collection = new ProductCollection();
            for (var i = 0; i < quantity; i++) collection.Insert(new T());
            return collection;
        }
    }



    /// <summary>
    /// 两个实体批量生产工厂类型
    /// </summary>
    public class BatchProductAFactory : BatchProductFactoryBase<ProductA> { }

    public class BatchProductBFactory : BatchProductFactoryBase<ProductB> { }
View Code

3.

public abstract class DecisionBase
    {
        private readonly IBatchFactory _factory;
        private readonly int _quantity;
        protected DecisionBase(IBatchFactory factory, int quantity)
        {
            _factory = factory; _quantity = quantity;
        }

        public virtual IBatchFactory Factory => _factory;
        public virtual int Quantity => _quantity;
    }

    public abstract class DirectorBase
    {
        private IList<DecisionBase> _decisions = new List<DecisionBase>();

        /// <summary>
        /// 实际项目中,最好将每个Derector需要添加的Decision也定义在配置文件中
        /// 这样增加新的Decison项都在后台完成,而不需要Assembler显示调用该方法补充
        /// </summary>
        /// <param name="decision"></param>
        protected virtual void Inset(DecisionBase decision)
        {
            if (decision == null || decision.Factory == null || decision.Quantity < 0) throw new ArgumentException();
            _decisions.Add(decision);
        }
        /// <summary>
        /// 便于客户程序使用增加的迭代器
        /// </summary>
        public virtual IEnumerable<DecisionBase> Decisions => _decisions;
    }
增加生产指导"顾问"
4.
internal class ProductADecision : DecisionBase
    {
        public ProductADecision() : base(new BatchProductAFactory(), 2) { }
    }

    internal class ProductBDecision : DecisionBase
    {
        public ProductBDecision() : base(new BatchProductBFactory(), 3) { }
    }

    public class ProductDirector : DirectorBase
    {
        public ProductDirector()
        {
            base.Inset(new ProductADecision());
            base.Inset(new ProductBDecision());
        }
    }
由Director指导的客户程序

 

public class ClientBatch//实现批量工厂的客户代码
    {
        /// <summary>
        /// 实际项目中,可以通过Assembler从外部把Director注入
        /// </summary>
        private DirectorBase _director = new ProductDirector();

        public IProduct[] Produce()
        {
            var collection = new ProductCollection();
            foreach (var decision in _director.Decisions)
            {
                collection += decision.Factory.Create(decision.Quantity);
            }
            return collection.Data;
        }
    }
"客户"程序
[TestMethod]
        public void BatchTest()
        {
            var client = new ClientBatch();
            var products = client.Produce();
            Assert.AreEqual(2 + 3, products.Length);
            for (var i = 0; i < 2; i++) { Assert.AreEqual("A", products[i].Name); }
            for (var i = 2; i < 5; i++) { Assert.AreEqual("B", products[i].Name); }
        }
单元测试

 

以上。

 

posted @ 2017-04-01 14:36  九元五分  阅读(147)  评论(0编辑  收藏  举报