在一个方法中接受多个相似的实体——优雅的解决方案
本文关键字:实体 相似 解决方案 一个 方法 | 更新日期: 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
作为直接替代将是错误的表示
我可以考虑以下选项,而不是创建函数的重载:
在函数内部使用对象类型作为输入和类型转换
我认为我们可以类似地使用动态类型,但解决方案将类似于上面,它不会是一个干净的解决方案,在任何情况下,随着
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; }
}
因此,一旦我们有了EntityName
和EntityOrder
两个实体,我们就可以为它们添加接口,通常使用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>();
注:看看我对这个问题的回答,你可能也会发现它很有用:
将字符串映射到实体以用于泛型方法
您可能想要实现一个interface
或abstract
类。
From MSDN
如果您希望创建组件的多个版本,请创建抽象类。抽象类提供了一种简单易行的方法对组件进行版本化。通过更新基类,所有继承类会随着更改自动更新。接口,在另一方面,一旦创建就不能更改。如果一个新版本的接口是必需的,你必须创建一个全新的接口。
如果您正在创建的功能将在广泛的范围内有用不同的对象,使用一个接口。应该使用抽象类主要用于密切相关的对象,而接口是最适合为不相关的类提供公共功能。
如果你正在设计小而简洁的功能,使用接口。如果您正在设计大型功能单元,请使用抽象类。
如果你想提供通用的,实现的在组件的所有实现中,使用一个抽象类。抽象类允许您部分地实现类,而接口不包含任何成员的实现。
抽象类示例
Cat
和Dog
都可以继承abstract
类Animal
,并且这个抽象基类将实现一个方法void Breathe()
,因此所有动物都将以完全相同的方式执行该方法。(您可以将此方法设置为virtual
,以便您可以对某些动物override
,如Fish
,它不像大多数动物那样呼吸)。
界面示例
所有动物都可以喂食,所以你将创建一个名为IFeedable
的interface
,并让Animal
实现它。只有Dog
和Horse
可以很好地实现ILikeable
-你不会在基类上实现这一点,因为这不适用于Cat
。