例如,考虑一个实用工具类SerializableList
:
public class SerializableList : List<ISerializable>
{
public T Add<T>(T item) where T : ISerializable
{
base.Add(item);
return item;
}
public T Add<T>(Func<T> factory) where T : ISerializable
{
var item = factory();
base.Add(item);
return item;
}
}
通常我会这样使用它:
var serializableList = new SerializableList();
var item1 = serializableList.Add(new Class1());
var item2 = serializableList.Add(new Class2());
我也可以通过分解来使用它,如下所示:
var serializableList = new SerializableList();
var item1 = serializableList.Add(() => new Class1());
var item2 = serializableList.Add(() => new Class2());
第二种方法似乎是一种首选的使用模式,因为我最近注意到了SO。是真的如此(为什么,如果是的话)还是仅仅是品味问题?
答案 0 :(得分:3)
举个例子,工厂方法很愚蠢。除非被调用者需要能够控制实例化点,实例化多个实例或延迟评估,否则它只是无用的开销。
编译器无法优化代理创建。
引用使用您在问题评论中提供的工厂语法的示例。这两个例子都试图(虽然很差)提供有保证的实例清理。
如果您考虑使用声明:
using (var x = new Something()) { }
天真的实施将是:
var x = new Something();
try
{
}
finally
{
if ((x != null) && (x is IDisposable))
((IDisposable)x).Dispose();
}
此代码的问题在于,在x
分配之后,但在输入try
块之前,可能会发生异常。如果发生这种情况,x
将无法正确处理,因为finally
块将不会执行。为了解决这个问题,using
语句的代码实际上更像是:
Something x = null;
try
{
x = new Something();
}
finally
{
if ((x != null) && (x is IDisposable))
((IDisposable)x).Dispose();
}
使用工厂参数引用的两个示例都试图处理同样的问题。传递工厂允许实例在受保护的块中实例化。直接传递实例可以避免在此过程中出现问题而不会调用Dispose()
。
在这些情况下,传递工厂参数是有道理的。
答案 1 :(得分:2)
<强>缓存强>
在你提供的例子中,没有其他人指出的那样有意义。相反,我会给你另一个例子,
public class MyClass{
public MyClass(string file){
// load a huge file
// do lots of computing...
// then store results...
}
}
private ConcurrentDictionary<string,MyClass> Cache = new ....
public MyClass GetCachedItem(string key){
return Cache.GetOrAdd(key, k => new MyClass(key));
}
在上面的例子中,假设我们正在加载一个大文件,我们正在计算一些东西,我们对该计算的最终结果感兴趣。为了加快访问速度,当我尝试通过Cache加载文件时,如果有缓存,Cache将返回缓存条目,只有当缓存找不到该项时,它才会调用Factory方法,并创建MyClass的新实例。
所以你要多次读取文件,但是你只创建只保存一次数据的类的实例。此模式仅用于缓存目的。
但是如果你没有缓存,并且每次迭代都需要调用new运算符,那么根本不使用工厂模式。
备用错误对象或错误记录
出于某种原因,如果创建失败,List可以创建错误对象,例如
T defaultObject = ....
public T Add<T>(Func<T> factory) where T : ISerializable
{
T item;
try{
item = factory();
}catch(ex){
Log(ex);
item = defaultObject;
}
base.Add(item);
return item;
}
在此示例中,您可以监视工厂是否在创建新对象时生成异常,并且当发生这种情况时,您记录错误,并返回其他内容并在列表中保留一些默认值。我不知道这将是什么实际用途,但错误记录在这里听起来更好。
答案 2 :(得分:1)
不,没有普遍偏好传递工厂而不是价值。但是,在非常特殊的情况下,将更喜欢传递工厂方法而不是值。
想一想:
将参数作为值传递有什么区别,或者 将其作为工厂方法传递(例如使用
Func<T>
)?
答案很简单:执行顺序。
为什么要推迟创建/计算/获取的价值?想到明显的事情:
Func<T, T>
而不是Func<T>
。答案 3 :(得分:1)
该问题将方法与不同目的进行比较。第二个应命名为 CreateAnd 添加<T>(Func<T> factory)
。
因此,根据所需的功能,应使用一种或另一种方法。