工厂创建相同界面的不同对象
Factory to create different objects of same interface
我有 1 个接口:
public interface ISummary
{
int EventId {get; set;}
}
还有许多实现此接口的具体 类:
public class EmployeeSummary : ISummary
{
public int EventId {get; set;},
public int TotalUniqueCount {get; set;}
public int Location {get; set;}
}
public class CarSummary : ISummary
{
public int EventId {get; set;}
public int TotalMiles {get; set;}
public int TotalHours {get; set;}
}
等....
唯一共享的 属性 是 EventId
。有没有办法让 1 个 工厂方法 创建所有这些摘要对象?我想要 1 个入口点来决定要创建的对象。
所以像这样:
public ISummary CreateSummary(ConcreteObjectType with properties)
{
if EmployeeSummary
--Call this method to create and return EmployeeSummary
if CarSummary
--Call this method create and return CarSummary
}
我希望其他 类 中的所有调用都调用此方法,而不是自己创建对象。
我苦苦挣扎的部分是如何将分配给对象的属性传递给此 CreateSummary
方法,因为对象的所有属性都将不同?
如果有更好的设计模式我应该在这里使用,我愿意改变此时的对象。
嗯,这正是 Factory Method 模式存在的原因:
public class SummaryFactory
{
// new instance with values assigned by action delegate or default
public T Create<T>(Action<T> action = null) where T : ISummary, new()
{
var result = new T();
action?.Invoke(result);
return result;
}
// with object to assign value from (map)
public T Create<T>(object map) where T : ISummary, new()
{
var result = new T();
PropertyInfo[] props = map.GetType().GetProperties();
PropertyInfo[] tProps = typeof(T).GetProperties();
foreach (var prop in props)
{
var upperPropName = prop.Name.ToUpper();
var foundProperty = tProps.FirstOrDefault(p => p.Name.ToUpper() == upperPropName);
foundProperty?.SetValue(result, prop.GetValue(map));
}
return result;
}
// new instance without generic parameters
public object Create(Type type)
{
var instance = Activator.CreateInstance(type);
// add some other logic that changes instance
return instance;
}
}
现在您可以使用这个工厂了:
var factory = new SummaryFactory();
var carSummary = factory.Create<CarSummary>();
var carSummary2 = factory.Create<CarSummary>(car => { car.TotalMiles = 50; });
var carSummary3 = factory.Create<CarSummary>(new { TotalMiles = 50 });
var employeeSummary = factory.Create(typeof(EmployeeSummary));
根据对象的构造函数,您可以这样做:
public interface ISummary
{ }
public class Bar : ISummary
{ }
public class SummaryFactory
{
public static TSummary CreateSummary<TSummary>()
where TSummary : new()
{
return new TSummary();
}
}
public class Foo
{
public void AMethod()
{
var bar = SummaryFactory.CreateSummary< Bar >();
}
}
但除非构造函数为空,否则我不知道这会给你带来什么。
如果你的对象确实有构造函数参数,你可以使用策略模式而不是工厂模式来创建它们,就像这样:
public interface ISummary
{ }
public class CarSummary : ISummary
{
public CarSummary(int someParam)
{
}
}
public interface ISummaryStrategy
{
/// <summary>
///
/// </summary>
/// <returns></returns>
ISummary Create();
}
public class CarSummaryStrategy
: ISummaryStrategy
{
public ISummary Create()
{
return new CarSummary(42); //The Answer to the Ultimate Question of Life, the Universe, and Everything
}
}
public class Foo
{
private Dictionary< Type, ISummaryStrategy > _summaryStrategies;
public Foo()
{
this._summaryStrategies = new Dictionary< Type, ISummaryStrategy >
{
{typeof( CarSummary ), new CarSummaryStrategy()}
};
}
public void UseSummaries(Type summary)
{
var summaryImpl = this._summaryStrategies[summary].Create();
// use the summary
}
}
具有 new()
约束的泛型只会产生更紧密的耦合。如果您已经需要知道将哪种类型作为泛型参数传递 T
,并且您知道该方法将 return,那么使用它有什么意义呢?
// This is a *helper method* which will create a car summary.
// No abstraction, no polymorphism, just a template helper method.
var summary = CreateSummary<CarSummary>(carData);
如果你需要一个抽象工厂,那就意味着你的调用方法只知道return接口,而且是工厂[=28] =] 这应该决定实际的实施。
在您的情况下,(我相信)您有多种类型的数据 类,并且您希望该方法为每种类型创建适当的摘要。 IE。类似于:
class EmployeesData : IEmployeesData
{ ... }
class CarsData : ICarsData
{ ... }
// at this point we don't know what `data` actually is
IData data = GetDataFromSomewhere();
// so we cannot pass the concrete generic parameter here
ISummary summary = GetSummary(data);
然后你需要的是某种策略,它会在运行时选择正确的实现。不同输入类型的策略应该在程序启动时注册(在组合根内的某个地方,或者通过 DI 注入),这样你就有了类似:
static readonly Dictionary<Type, Func<object, ISummary>> _strategies =
new Dictionary<Type, Func<object, ISummary>>();
public static void Register<T>(Func<T, ISummary> strategy)
{
_strategies[typeof(T)] = input => strategy((T)input);
}
public ISummary CreateSummary(object input)
{
var type = input.GetType();
Func<object, ISummary> strategy;
if (!_strategies.TryGetValue(type, out strategy))
throw new ArgumentException($"No strategy registered for type {type}");
return strategy(input);
}
所以,在你的组合根目录的某个地方,你会有具体的方法:
ISummary CreateEmployeeSummary(EmployeesData data)
{ ... }
ISummary CreateCarSummary(CarsData data)
{ ... }
// and finally register all concrete methods:
Register<IEmployeesData>(d => CreateEmployeeSummary(d));
Register<ICarsData>(d => CreateCarsSummary(d));
我有 1 个接口:
public interface ISummary
{
int EventId {get; set;}
}
还有许多实现此接口的具体 类:
public class EmployeeSummary : ISummary
{
public int EventId {get; set;},
public int TotalUniqueCount {get; set;}
public int Location {get; set;}
}
public class CarSummary : ISummary
{
public int EventId {get; set;}
public int TotalMiles {get; set;}
public int TotalHours {get; set;}
}
等....
唯一共享的 属性 是 EventId
。有没有办法让 1 个 工厂方法 创建所有这些摘要对象?我想要 1 个入口点来决定要创建的对象。
所以像这样:
public ISummary CreateSummary(ConcreteObjectType with properties)
{
if EmployeeSummary
--Call this method to create and return EmployeeSummary
if CarSummary
--Call this method create and return CarSummary
}
我希望其他 类 中的所有调用都调用此方法,而不是自己创建对象。
我苦苦挣扎的部分是如何将分配给对象的属性传递给此 CreateSummary
方法,因为对象的所有属性都将不同?
如果有更好的设计模式我应该在这里使用,我愿意改变此时的对象。
嗯,这正是 Factory Method 模式存在的原因:
public class SummaryFactory
{
// new instance with values assigned by action delegate or default
public T Create<T>(Action<T> action = null) where T : ISummary, new()
{
var result = new T();
action?.Invoke(result);
return result;
}
// with object to assign value from (map)
public T Create<T>(object map) where T : ISummary, new()
{
var result = new T();
PropertyInfo[] props = map.GetType().GetProperties();
PropertyInfo[] tProps = typeof(T).GetProperties();
foreach (var prop in props)
{
var upperPropName = prop.Name.ToUpper();
var foundProperty = tProps.FirstOrDefault(p => p.Name.ToUpper() == upperPropName);
foundProperty?.SetValue(result, prop.GetValue(map));
}
return result;
}
// new instance without generic parameters
public object Create(Type type)
{
var instance = Activator.CreateInstance(type);
// add some other logic that changes instance
return instance;
}
}
现在您可以使用这个工厂了:
var factory = new SummaryFactory();
var carSummary = factory.Create<CarSummary>();
var carSummary2 = factory.Create<CarSummary>(car => { car.TotalMiles = 50; });
var carSummary3 = factory.Create<CarSummary>(new { TotalMiles = 50 });
var employeeSummary = factory.Create(typeof(EmployeeSummary));
根据对象的构造函数,您可以这样做:
public interface ISummary
{ }
public class Bar : ISummary
{ }
public class SummaryFactory
{
public static TSummary CreateSummary<TSummary>()
where TSummary : new()
{
return new TSummary();
}
}
public class Foo
{
public void AMethod()
{
var bar = SummaryFactory.CreateSummary< Bar >();
}
}
但除非构造函数为空,否则我不知道这会给你带来什么。
如果你的对象确实有构造函数参数,你可以使用策略模式而不是工厂模式来创建它们,就像这样:
public interface ISummary
{ }
public class CarSummary : ISummary
{
public CarSummary(int someParam)
{
}
}
public interface ISummaryStrategy
{
/// <summary>
///
/// </summary>
/// <returns></returns>
ISummary Create();
}
public class CarSummaryStrategy
: ISummaryStrategy
{
public ISummary Create()
{
return new CarSummary(42); //The Answer to the Ultimate Question of Life, the Universe, and Everything
}
}
public class Foo
{
private Dictionary< Type, ISummaryStrategy > _summaryStrategies;
public Foo()
{
this._summaryStrategies = new Dictionary< Type, ISummaryStrategy >
{
{typeof( CarSummary ), new CarSummaryStrategy()}
};
}
public void UseSummaries(Type summary)
{
var summaryImpl = this._summaryStrategies[summary].Create();
// use the summary
}
}
具有 new()
约束的泛型只会产生更紧密的耦合。如果您已经需要知道将哪种类型作为泛型参数传递 T
,并且您知道该方法将 return,那么使用它有什么意义呢?
// This is a *helper method* which will create a car summary.
// No abstraction, no polymorphism, just a template helper method.
var summary = CreateSummary<CarSummary>(carData);
如果你需要一个抽象工厂,那就意味着你的调用方法只知道return接口,而且是工厂[=28] =] 这应该决定实际的实施。
在您的情况下,(我相信)您有多种类型的数据 类,并且您希望该方法为每种类型创建适当的摘要。 IE。类似于:
class EmployeesData : IEmployeesData
{ ... }
class CarsData : ICarsData
{ ... }
// at this point we don't know what `data` actually is
IData data = GetDataFromSomewhere();
// so we cannot pass the concrete generic parameter here
ISummary summary = GetSummary(data);
然后你需要的是某种策略,它会在运行时选择正确的实现。不同输入类型的策略应该在程序启动时注册(在组合根内的某个地方,或者通过 DI 注入),这样你就有了类似:
static readonly Dictionary<Type, Func<object, ISummary>> _strategies =
new Dictionary<Type, Func<object, ISummary>>();
public static void Register<T>(Func<T, ISummary> strategy)
{
_strategies[typeof(T)] = input => strategy((T)input);
}
public ISummary CreateSummary(object input)
{
var type = input.GetType();
Func<object, ISummary> strategy;
if (!_strategies.TryGetValue(type, out strategy))
throw new ArgumentException($"No strategy registered for type {type}");
return strategy(input);
}
所以,在你的组合根目录的某个地方,你会有具体的方法:
ISummary CreateEmployeeSummary(EmployeesData data)
{ ... }
ISummary CreateCarSummary(CarsData data)
{ ... }
// and finally register all concrete methods:
Register<IEmployeesData>(d => CreateEmployeeSummary(d));
Register<ICarsData>(d => CreateCarsSummary(d));