我有一个关于使用create方法创建工厂接口的问题,该方法可以根据实现方式接受不同的参数类型。
为了给你更多背景知识,我在项目中使用了依赖注入,并且需要在运行时生成有状态对象 - 因此我注入工厂(而不是对象本身)来创建这些有状态对象。我遇到的问题是,对于某些接口,具体实现根本不能具有相同的构造函数参数类型,因此创建这些接口实例的工厂需要将几乎“动态”参数传递给create方法。
我已经讨论了几天了,以下是我能想出的最佳解决方案(即将对象传递给工厂创建方法并将其投射到工厂的具体实现中)。我真的在寻找之前遇到过这种情况的人的反馈,听听他们提出的问题,以及我提出的解决方案是否可以接受。
如果缺少任何信息,请道歉,并提前多多感谢!
//
// Types...
//
interface IDataStore
{
List<string> GetItems();
}
public class XmlDataStore : IDataStore
{
public XmlDataStore(XmlDocument xmlDoc)
{
// Initialise from XML Document...
}
public List<string> GetItems()
{
// Get Items from XML Doc...
}
}
public class SQLDataStore : IDataStore
{
public SQLDataStore(SqlConnection conn)
{
// Initialise from SqlConnection...
}
public List<string> GetItems()
{
// Get Items from Database Doc...
}
}
//
// Factories...
//
interface IDataStoreFactory
{
IDataStore Create(object obj);
}
class XmlDataStoreFactory : IDataStore
{
IDataStore Create(object obj)
{
// Cast to XmlDocument
return new XmlDataStore((XmlDocument)obj);
}
}
class SQLDataStoreFactory : IDataStore
{
IDataStore Create(object obj)
{
// Cast to SqlConnection
return new SQLDataStore((SqlConnection)obj);
}
}
答案 0 :(得分:1)
我不确定我是否正确理解了您的问题,但对我来说,在您调用它们时使用工厂实例创建有状态对象听起来有点奇怪。
直接回答您的问题:泛型是您的解决方案。你的rinterface成为一个开放的通用抽象:
interface IDataStore<TStoreType>
{
List<string> GetItems();
}
interface IDataStoreFactory<TStoreType>
{
IDataStore<TStoreType> Create(TStoreType obj);
}
您的工厂类将如下所示:
class XmlDataStoreFactory : IDataStoreFactory<XmlDocument>
{
IDataStore<XmlDocument> Create(XmlDocument document)
{
return new XmlDataStore(document);
}
}
class SQLDataStoreFactory : IDataStoreFactory<SqlConnection>
{
IDataStore<SqlConnection> Create(SqlConnection connection)
{
return new SQLDataStore(connection);
}
}
这样可行,但从您给出的示例中我得到的印象是您在整个代码库中使用工厂。也许我在这一点上错了,但看看你的设计并减少工厂的数量。需要工厂意味着将数据与行为混合在一起,这最终会让你陷入困境。
例如,假设您有某种服务,在登录时将当前用户添加到审计日志中。此服务需要当前用户,这是运行时数据(或上下文数据)的典型示例。但不是:
public class AuditLogService
{
public void AddApplicationSignIn(User user)
{
//... add user to some log
}
}
我知道这不是一个很好的例子,因为你实际上不需要这个类的工厂,但是下一个代码示例你会明白这一点:
public class AuditLogService
{
private readonly IUserContext userContext;
public AuditLogService(IUserContext userContext)
{
this.userContext = userContext;
}
public void AddApplicationSignIn()
{
var user = this.userContext.GetCurrentUser();
//... add user to some log
}
}
因此,通过从行为中分割数据,您可以排除对工厂的需求。并承认有些工厂是最好的解决方案。我认为IDataStore不是你需要工厂的东西。
有关分割数据和行为的好博客,请阅读here
答案 1 :(得分:1)
基于this注释,您需要一个工厂,它可以生成多种类型的IDataStore。您可以通过在单例工厂实例中创建一个开放的通用工厂方法来完成。
interface IDataStore<TStoreType>
{
void SetBaseType(TStoreType obj);
List<string> GetItems();
}
interface IDataStoreFactory
{
IDataStore<TStoreType> Create<TStoreType>(TStoreType obj)
}
class DataStoreFactory : IDataStoreFactory
{
public IDataStore<TStoreType> Create<TStoreType>(TStoreType obj)
{
if (obj.GetType() == typeof(SqlConnection))
{
var store = new SQLDataStore((SqlConnection)(Object)obj);
return (IDataStore<TStoreType>)store;
}
if (obj.GetType() == typeof(XmlDocument))
{ //... and so on }
}
}
class SQLDataStore : IDataStore<SqlConnection>
{
private readonly SqlConnection connection;
public SQLDataStore(SqlConnection connection)
{
this.connection = connection;
}
public List<string> GetItems() { return new List<string>(); }
}
你可以像这样使用这个工厂:
var factory = new DataStoreFactory();
var sqlDatastore = factory.Create(new SqlConnection());
var xmlDatastore = factory.Create(new XmlDocument());
如果您使用DI容器,您的数据存储工厂将变得复杂得多。您可以在工厂中注入容器并直接从容器中检索实例,这通常会从下到上构建您的实例,包括自己的依赖项,生命周期管理等。但是要非常小心这种方法,这是使用服务定位器模式的第一步,即anti pattern