减少工厂的参数列表

本文关键字:参数 列表 工厂 | 更新日期: 2023-09-27 18:31:52

我有一个场景,工厂可以制造便宜或昂贵的自行车。

  public class BikeFactory : IBikeFactory
{
    public T GetInstance<T>(Guid userId, string bikeName, string bikeDescription, decimal value, DateTime shippingDate) where T : EntityBase
    {
        if (typeof(EntityBase).IsAssignableFrom(typeof(T)) && typeof(T) == typeof(ExpensiveBike))
        {
            Information bikeInfo = new Information(bikeName,bikeDescription, value, shippingDate);
            return (T)Activator.CreateInstance(typeof(T), userId, newIfo);
        }
        else if (typeof(EntityBase).IsAssignableFrom(typeof(T)) && typeof(T) == typeof(CheapBike))
        {
            Information bikeInfo = new Information(bikeName,bikeDescription, value, shippingDate);
            return (T)Activator.CreateInstance(typeof(T), userId, newIfo);
        }
        else return null; //for now
    }
}

Bouth 实体 CheapBike 和 ExpensiveBike 持有描述它们的 ValueObject 信息(保存有关它们的基本信息)。如您所见,参数列表很丑陋,但它就在那里,因此参数被提供给信息构建器

  Information(bikeName, bikeDescription, vlaue, shippingDate)

以及 CheapBike 和 ExpensiveBike 构造函数(持有自行车所有者的 ID)

  CheapBike(Guid owner, Information bikeInformation) // same for Expensive

我正在寻找一种减少参数数量的方法,因此工厂方法 GetIstance() 看起来像这样:

    public T GetInstance<T>(Guid userId, Inforamtion bikeInfo) where T : EntityBase

问题

  1. 我是否应该允许客户端通过 ValueObject 工厂构造信息 ValueObject 并将其传递给 Bikefactory?

     VOfactory.GetInstance<Inforamtion>(infoDto.name, infoDto.description, infoDto.value, infoDto.shippingDate)
     Efactory.GetInstance<CheapBike>(userId, bikeInfo)
    
  2. 或者只允许客户端构造信息值对象?

     Information bikeinfo = new Information(infoDto.name, infoDto.description, infoDto.value, infoDto.shippingDate);
    
  3. 这甚至是一种有效的方法吗?

减少工厂的参数列表

我是否应该允许客户端通过 ValueObject 工厂构造信息 ValueObject 并将其传递给 Bikefactory?

这是很多工厂,我可能会尝试通过提供重载来简化它。同时接受Information对象或所有参数的对象。

public T GetInstance<T>(Guid userId, Inforamtion bikeInfo) 
    where T : EntityBase
public T GetInstance<T>(Guid userId, 
                        string name, 
                        string description, 
                        decimal value, DateTime shippingDate) 
    where T : EntityBase

然后,我将允许他们在不使用工厂的情况下实例化Information对象。实际上,在 ValueObject 的情况下,工厂的唯一好处是实例化Information对象,这没有用 - 这就是构造函数的用途。

或者只允许客户端构造信息值对象?

是的,请参阅上面的解释。

这甚至是一种有效的方法吗?

当然,只要工厂使用正确并且对消费者有利。

用法示例:

var bikeInfo = new Information 
{ 
    Name = infoDto.name, 
    Description = infoDto.description, 
    Value = infoDto.value, 
    ShippingDate = infoDto.shippingDate 
}
Efactory.GetInstance<CheapBike>(userId, bikeInfo);

Efactory.GetInstance<CheapBike>(userId, 
                                infoDto.name,
                                infoDto.description, 
                                infoDto.value,
                                infoDto.shippingDate);

在这种情况下,我只是将 parms 包装到您的 dto 对象中,并让用户在创建时分配它们。话虽如此,我会再次查看您的设计,看看工厂在这种情况下是否真的有用。如果从工厂返回的两个对象都有完全相同的参数,那么为什么有多个对象呢?

void Main()
{
    var bike = Factory.GetInstance<CheapBike>(() => new Information()
    {
        Name = "BMX",
        Description = "..."
    });
    //or
    var bike = Factory.GetInstance<CheapBike>(GetInfo);
}
private Information GetInfo()
{
     //do some logic to populate the info object to be passed
     //into the constructor during factory initialization
     return yourPopulatedInfoObject;
}
public static class Factory 
{
    public static T GetInstance<T>(Func<Information> func) where T : EntityBase
    {
        return (T)Activator.CreateInstance(typeof(T), func());
    }
}

通过将工厂模式应用于此处的设计,您尝试解决哪些问题?

以下是使用工厂的两个常见动机:

  1. 减轻客户端代码选择具体实现的负担
  2. 抽象出复杂的创建过程

据我所知,您工厂的客户正在将他们需要的混凝土类型传递给工厂(ExpensiveBikeCheapBike),因此空白#1。

此外,您的工厂仅将客户端代码传递的任何内容委托给客户端选择的具体实现的构造函数。换句话说,您的工厂不会抽象出任何额外的复杂性,因此 #2 无效。

基本上,你可能根本不应该在这里使用任何工厂,除非我错过了什么。直接调用构造函数实际上会降低设计的复杂性。

"我正在寻找一种减少参数数量的方法"

一长串参数通常表明错过了一些有意义的整体概念,但除非我们了解业务,否则我们无法为您识别这些概念。

就像在良好的面向对象实践中一样,寻找高度内聚的数据成员,并尝试提出一个有意义的统一概念。您选择的概念必须为您的设计增加价值。如果概念太抽象或武断,最好不要对信息进行分组,直到你弄清楚它。

识别这些概念的一个好方法是分析命令并查看哪些数据一起更改。例如,自行车的名称和描述一起更改可能是很常见的,因此它可能形成一个BikeDescriptor概念。

例如

var descriptor = new BikeDescriptor(name, description);
var value = new Money(299.0, Currency.CAD);
var bike = new CheapBike(id, descriptor, value, shippingDate);