在一个方法中接受多个相似的实体——优雅的解决方案

本文关键字:实体 相似 解决方案 一个 方法 | 更新日期: 2023-09-27 18:02:38

我有两个数据实体,它们几乎是相似的,设计是这样的:

public Class Entity1 : Base
{
  public int layerId;
  public List<int> Groups;
}

区别在于Entity1有额外的integer Groups集合

public Class Entity2 : Base
{
  public int layerId;
}

这些实体使用Json作为UI的输入填充,我需要将它们传递给处理方法,该方法给出相同的Output实体。方法有一个逻辑来处理,如果List<int> Groups is null,我需要创建一个能够以优雅的方式处理每个输入的方法。我不能只使用Entity1,因为它们是不同业务流程的两个不同功能输入,因此使用Entity1作为直接替代将是错误的表示

我可以考虑以下选项,而不是创建函数的重载:

  1. 在函数内部使用对象类型作为输入和类型转换

  2. 我认为我们可以类似地使用动态类型,但解决方案将类似于上面,它不会是一个干净的解决方案,在任何情况下,随着switch-case混乱。

我目前正在做的处理方法是这样的:

public OuputEntity ProcessMethod(Entity 1)
{
  // Data Processing
}

我已经创建了一个Entity1的构造函数,它将Entity2作为输入。

任何创建一个优雅的解决方案的建议,可以有多个这样的实体。可能是使用泛型,我们使用Func委托创建两个或多个实体的公共类型,这几乎类似于我目前所做的。比如:

Func<T,Entity1>

因此在逻辑中使用Entity1输出进行进一步处理。

在一个方法中接受多个相似的实体——优雅的解决方案

我需要创建一个能够以优雅的方式处理每个输入的方法

创建一个Interface,或者可以说是一个契约,其中每个实体都遵循特定的设计。这样就可以以类似的方式处理公共功能。随后,每个差异都在其他接口中表示,并对该接口进行测试,并按这种方式处理差异。

可以使用泛型,

泛型类型可以针对接口进行测试,因此可以使用干净的操作方法。

例如,我们有两个实体,它们都具有Name属性作为字符串,但其中一个具有Order属性。所以我们定义了公共接口

public interface IName
{
   string Name { get; set; }
   string FullName { get;  }
}
public interface IOrder
{
   decimal Amount { get; set; }
}
因此,一旦我们有了EntityNameEntityOrder两个实体,我们就可以为它们添加接口,通常使用Partial类定义,例如EF动态创建它们时:
public partial class EntityName : IName
{
  // Nothing to do EntityName already defines public string Name { get; set; }
  public string FullName { get { return "Person: " +  Name; }}
}

public partial class EntityOrder : IName, IOrder
{
  // Nothing to do Entity Order already defines public string Name { get; set; }
  // and Amount.
  public string FullName { get { return "Order: " + Name; } }
}

然后我们可以用相同的方法把它们一起处理

public void Process(IName entity)
{
     LogOperation( entity.FullName );
   // If we have an order process it uniquely
   var order = entity as IOrder;
   if (order != null)
   {
      LogOperation( "Order: " + order.Amount.ToString() );
   }
}

泛型方法可以强制实现如下接口:

public void Process<T>(T entity) where T : IName
{
   // Same as before but we are ensured that only elements of IName 
   // are used as enforced by the compiler.
}

创建一个通用的方法来帮你完成这个工作:

List<OuputEntity> MyMethod<T>(T value) where T : Base 
// adding this constraint ensures that T is of type that is derived from Base type
{
   List<OutputEntity> result = new List<OutputEntity>();
   // some processing logic here like ...
   return result;
}
var resultForEntity1 = MyMethod<Entity1>();
var resultForEntity2 = MyMethod<Entity2>();

注:看看我对这个问题的回答,你可能也会发现它很有用:

将字符串映射到实体以用于泛型方法

您可能想要实现一个interfaceabstract类。

From MSDN

如果您希望创建组件的多个版本,请创建抽象类。抽象类提供了一种简单易行的方法对组件进行版本化。通过更新基类,所有继承类会随着更改自动更新。接口,在另一方面,一旦创建就不能更改。如果一个新版本的接口是必需的,你必须创建一个全新的接口。

如果您正在创建的功能将在广泛的范围内有用不同的对象,使用一个接口。应该使用抽象类主要用于密切相关的对象,而接口是最适合为不相关的类提供公共功能。

如果你正在设计小而简洁的功能,使用接口。如果您正在设计大型功能单元,请使用抽象类。

如果你想提供通用的,实现的在组件的所有实现中,使用一个抽象类。抽象类允许您部分地实现类,而接口不包含任何成员的实现。

抽象类示例

CatDog都可以继承abstractAnimal,并且这个抽象基类将实现一个方法void Breathe(),因此所有动物都将以完全相同的方式执行该方法。(您可以将此方法设置为virtual,以便您可以对某些动物override,如Fish,它不像大多数动物那样呼吸)。

界面示例

所有动物都可以喂食,所以你将创建一个名为IFeedableinterface,并让Animal实现它。只有DogHorse可以很好地实现ILikeable -你不会在基类上实现这一点,因为这不适用于Cat