设计模式-创建型模式-工厂模式-批量工厂
开发场景
实际项目中经常需要加工一批对象,这时候如果按部就班的一个一个来生成,效率相对上比较低,最好专门设计独立的批量工厂。很难想象调用“啤酒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的容器类型
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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; } }
2.定义批量工厂和产品类型容器
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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> { }
3.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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()); } }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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; } }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
[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); } }
以上。