工厂创建相同界面的不同对象

时间:2016-09-21 15:31:57

标签: c# design-patterns factory-pattern

我有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 工厂方法创建所有这些摘要对象?我想要一个入口点来决定要创建哪些对象。

类似于:

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方法,因为对象上的所有属性都不同?

如果我应该在这里使用更好的设计模式,我愿意在这一点上更改对象。

3 个答案:

答案 0 :(得分:8)

嗯,这正是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 result = Activator.CreateInstance(type);

        // add all logic that you need

        return result;
    }
}

现在你可以使用这个工厂了:

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));

答案 1 :(得分:2)

具有new()约束的泛型只会使耦合更紧密。如果您已经需要知道要传递哪种类型作为通用参数T,并且您知道该方法将返回什么,那么使用它的重点是什么?

// 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);

如果您需要一个抽象工厂,那么这意味着您的调用方法只知道返回接口,它是 factory 应该决定的实际的实施。

在您的情况下,(我相信)您有几种类型的数据类,并且您希望该方法为每种类型创建适当的摘要。即类似的东西:

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));

答案 2 :(得分:1)

根据对象的构造函数,您可以这样做:

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
    }
}