工厂方法
简单工厂的优缺点
优点:
将子类的创建与具体的应用场景解藕,使得修改和新增产品类型时只需要维护工厂,而不用考虑实际应用场景
缺点:
1.在新增和修改产品创建时,依然要修改工厂,违反了开闭原则,随着类型的增多将不断增加该类的逻辑
2.工厂类集中了所有产品的创建逻辑,一旦发生问题将影响整个系统
工厂方法
工厂方法通过对工厂进行一次抽象,将具体产品的创建交给具体工厂去完成,从而避免了对核心工厂抽象的修改,并且分离了创建逻辑,使得各个产品能够独立完成创建。
以手机工厂举例,产品和工厂接口
namespace DesignModel.FactoryMethod
{
public enum PhoneType
{
Inviled = -1,
iPhoneXs = 1,
VivoPhone = 2
}
public interface IProduct
{
void Show();
void Check();
PhoneType GetPhoneType();
}
public interface IFactory
{
IProduct CreateProduct();
}
}
iphone和vivo手机产品
namespace DesignModel.FactoryMethod
{
public class iPhoneXs : IProduct
{
public PhoneType GetPhoneType()
{
return PhoneType.iPhoneXs;
}
public void Show()
{
Debug.Log("Show iPhone Xs");
}
public void Check()
{
Debug.Log("Check iPhone Xs");
}
}
public class VivoPhone : IProduct
{
public PhoneType GetPhoneType()
{
return PhoneType.VivoPhone;
}
public void Show()
{
Debug.Log("Show vivo Phone");
}
public void Check()
{
Debug.Log("Check vivo Phone");
}
}
}
对应的工厂
namespace DesignModel.FactoryMethod
{
public class iPhoneFactory : IFactory
{
public IProduct CreateProduct()
{
return new iPhoneXs();
}
}
public class VivoFactory : IFactory
{
public IProduct CreateProduct()
{
return new VivoPhone();
}
}
}
简单的客户端测试
public class FactoryMethodClient : MonoBehaviour
{
void Start ()
{
IProduct phone1 = new iPhoneFactory().CreateProduct();
phone1.Show();
IProduct phone2 = new VivoFactory().CreateProduct();
phone2.Show();
}
}
拓展
改进对象池,使得它使用工厂方法
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace DesignModel.FactoryMethod
{
public class PhonePoolManager
{
//单例
private static PhonePoolManager instance = null;
public static PhonePoolManager Instance
{
get
{
if (instance == null) instance = new PhonePoolManager();
return instance;
}
}
//对象池
private Dictionary<PhoneType,List<IProduct>> pool;
//私有化初始化,并填充对象池类型
private PhonePoolManager()
{
pool = new Dictionary<PhoneType, List<IProduct>>();
pool.Add(PhoneType.iPhoneXs, new List<IProduct>());
pool.Add(PhoneType.VivoPhone, new List<IProduct>());
}
/// <summary>
/// 获取Phone对象
/// </summary>
/// <param name="type">要获取的对象类型</param>
public IProduct GetPhone(PhoneType type)
{
IProduct phone = null;
if (pool[type].Count <= 0)
phone = CreatePhone(type);
else
{
phone = pool[type][0];
pool[type].RemoveAt(0);
}
return phone;
}
private IProduct CreatePhone(PhoneType type)
{
switch(type)
{
case PhoneType.VivoPhone:
return new VivoFactory().CreateProduct();
case PhoneType.iPhoneXs:
return new iPhoneFactory().CreateProduct();
default:
return null;
}
}
/// <summary>
/// 回收phone对象
/// </summary>
/// <param name="item">要回收的对象</param>
public void RecoverPhone(IProduct item)
{
switch(item.GetPhoneType())
{
case PhoneType.VivoPhone:
pool[PhoneType.VivoPhone].Add(item);
break;
case PhoneType.iPhoneXs:
pool[PhoneType.iPhoneXs].Add(item);
break;
}
}
}
}
工厂方法的局限
可以看到在对象池中,具体的创建逻辑是这一部分
private IProduct CreatePhone(PhoneType type)
{
switch(type)
{
case PhoneType.VivoPhone:
return new VivoFactory().CreateProduct();
case PhoneType.iPhoneXs:
return new iPhoneFactory().CreateProduct();
default:
return null;
}
}
我们可以看到,虽然产品的创建和具体的产品分离,但我们依然需要知道对应工厂的类型,并且创建对应工厂的实例
总结工厂方法的局限如下:
1.每个产品都要有一个对应的工厂,类的数量和复杂性加倍了,会给程序带来额外开销
2.虽然工厂本身对修改封闭了,但对于使用工厂方法的类,例如上面的PhonePoolManager,一旦产品发生变化,依然要修改对应部分的代码CreatePhone
应用
在实际应用中,我们可以将工厂名配置在配置文件中,通过反射去获取类型,避免对代码的修改.
例如在我们的CreatePhone函数中,我们可以将PhoneType保存为类名字符串,从而通过工厂类名反射获取对应类型,创建对应产品。
用反射重写PhonePoolManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Reflection;
namespace DesignModel.FactoryMethod
{
public class PhonePoolManager
{
private const string NAME_SPACE = "DesignModel.FactoryMethod";
//单例
private static PhonePoolManager instance = null;
public static PhonePoolManager Instance
{
get
{
if (instance == null) instance = new PhonePoolManager();
return instance;
}
}
//对象池
private Dictionary<string,List<IProduct>> pool;
//私有化初始化,并填充对象池类型
private PhonePoolManager()
{
pool = new Dictionary<string, List<IProduct>>();
}
/// <summary>
/// 获取Phone对象
/// </summary>
/// <typeparam name="T">对象工厂类型</typeparam>
public IProduct GetPhone<T>() where T : IFactory, new()
{
return GetPhone(new T().GetType().Name);
}
/// <summary>
/// 获取Phone对象
/// </summary>
/// <param name="type">要获取的对象工厂类型</param>
public IProduct GetPhone(string type)
{
IProduct phone = null;
if (pool.ContainsKey(type) && pool[type].Count > 0)
{
phone = pool[type][0];
pool[type].RemoveAt(0);
}
else
{
phone = CreatePhone(type);
}
return phone;
}
private IProduct CreatePhone(string type)
{
string fullName = NAME_SPACE + "." + type;
Assembly assembly = Assembly.GetExecutingAssembly();
IFactory factory = (IFactory)assembly.CreateInstance(fullName);
if (factory != null)
return factory.CreateProduct();
return null;
}
/// <summary>
/// 回收phone对象
/// </summary>
/// <param name="item">要回收的对象</param>
public void RecoverPhone(IProduct item)
{
if (!pool.ContainsKey(item.GetType().Name))
{
pool.Add(item.GetType().Name, new List<IProduct>());
}
if (!pool[item.GetType().Name].Contains(item))
pool[item.GetType().Name].Add(item);
}
}
}
简单的测试
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace DesignModel.FactoryMethod
{
public class FactoryMethodClient : MonoBehaviour
{
void Start ()
{
IProduct phone3 = PhonePoolManager.Instance.GetPhone<iPhoneFactory>();
phone3.Show();
PhonePoolManager.Instance.RecoverPhone(phone3);
IProduct phone4 = PhonePoolManager.Instance.GetPhone("VivoFactory");
phone4.Show();
PhonePoolManager.Instance.RecoverPhone(phone4);
IProduct phone5 = PhonePoolManager.Instance.GetPhone<iPhoneFactory>();
phone5.Show();
PhonePoolManager.Instance.RecoverPhone(phone5);
}
}
}
测试结果