为实现抽象的 类 创建一个 DTO 是一种不好的做法吗?
Is it a bad practice to create one DTO for many classes that implements an abstract one?
例如:
我有:
- 一个摘要 class 动物 属性 名字
- Dog 实现了 Animal 并拥有 属性 个国家/地区
- Cat 实现了 Animal 并且具有 属性 大小
我可以按如下方式使用 DTO 吗?
AnimalDto {
public string Name { get; set;}
public string Country { get; set;}
public string Size { get; set;}
public AnimalType Type { get; set}
}
这个怎么样:
public abstract AnimalDTO
{
public static AnimalDTO Create(Animal animal)
{
var dog = animal as Dog;
var cat = animal as Cat;
if (dog != null)
{
return Dog(dog.Country);
}
else
{
return Cat(cat.Size);
}
}
public static AnimalDTO Dog(string country) =>
new Animals.Dog(country);
public static AnimalDTO Cat(string size) =>
new Animals.Cat(size);
public abstract void Switch(Action<string> isDog, Action<string> isCat);
private static class Animals
{
public class Dog : AnimalDTO
{
readonly string country;
public Dog(string country)
{
this.country = country;
}
public override void Switch(Action<string> isDog, Action<string> isCat) => isDog(country);
}
public class Cat : AnimalDTO
{
readonly string size;
public Dog(string size)
{
this.size = size;
}
public override void Switch(Action<string> isDog, Action<string> isCat) => isCat(size);
}
}
例如:
public void CallFactory(Animal animal)
{
var a = AnimalDTO.Create(animal);
Factory(a);
}
public void Factory(AnimalDTO animal)
{
animal.Switch(
isDog: (country) => {
// here you can be sure that the animal is dog
// you can use country variable
Console.Writeline($"It is a dog! from country {country}");
},
isCat: (size) => {
// it is a cat
Console.Writeline($"it is a cat of size {size}");
});
}
我觉得这是一个 XY 问题。为什么要使用摘要 class?
总是一种避免创建摘要的方法class - 除了必须从您所在的图书馆中提取class摘要消耗但无法控制。如何?使用接口,当您需要通用功能时,使用静态函数或与另一个组合使用 class.
我知道这不是您问题的直接答案,但它可能会解决您的问题。
public interface IAnimal
{
string Name { get; set; }
}
public class Dog : IAnimal
{
public string Name { get; set; }
public string Country { get; set; }
}
public class Cat : IAnimal
{
public string Name { get; set; }
public string Size { get; set; }
}
由此,您最终得到两个 classes,它们都可以用作 DTO,它们都没有不必要的属性。
在运行时你可以输入检查。
IAnimal myAnimal = ... ;
Helpers.DoSomethingWithAnimal(myAnimal);
switch (myAnimal)
{
case (Cat cat):
Helpers.DoSomethingWithCat(cat);
break;
case (Dog dog):
Helpers.DoSomethingWithDog(dog);
break;
}
序列化时,您可以插入一个额外的 属性 来跟踪您需要将数据反序列化回的动物类型。
{
name: "My Dog",
country: "Romania",
"__animal-type": "dog"
}
要回答这个问题,您可以查看 SOLID 原则,尤其是 the Single Responsibility Principle,它指出 class 必须只有 一个 改变的原因。
如果您的 DTO 重新组合属于动物王国的所有 classes 的所有属性,则每次需要更改这些 classes 时,它都必须更改。如果 Cat 更改,它将更改。如果狗改变,它也会改变。等等。我会避免这种情况。
如其他答案所述,如果可能的话,可能值得重新考虑设计。
例如:
我有:
- 一个摘要 class 动物 属性 名字
- Dog 实现了 Animal 并拥有 属性 个国家/地区
- Cat 实现了 Animal 并且具有 属性 大小
我可以按如下方式使用 DTO 吗?
AnimalDto {
public string Name { get; set;}
public string Country { get; set;}
public string Size { get; set;}
public AnimalType Type { get; set}
}
这个怎么样:
public abstract AnimalDTO
{
public static AnimalDTO Create(Animal animal)
{
var dog = animal as Dog;
var cat = animal as Cat;
if (dog != null)
{
return Dog(dog.Country);
}
else
{
return Cat(cat.Size);
}
}
public static AnimalDTO Dog(string country) =>
new Animals.Dog(country);
public static AnimalDTO Cat(string size) =>
new Animals.Cat(size);
public abstract void Switch(Action<string> isDog, Action<string> isCat);
private static class Animals
{
public class Dog : AnimalDTO
{
readonly string country;
public Dog(string country)
{
this.country = country;
}
public override void Switch(Action<string> isDog, Action<string> isCat) => isDog(country);
}
public class Cat : AnimalDTO
{
readonly string size;
public Dog(string size)
{
this.size = size;
}
public override void Switch(Action<string> isDog, Action<string> isCat) => isCat(size);
}
}
例如:
public void CallFactory(Animal animal)
{
var a = AnimalDTO.Create(animal);
Factory(a);
}
public void Factory(AnimalDTO animal)
{
animal.Switch(
isDog: (country) => {
// here you can be sure that the animal is dog
// you can use country variable
Console.Writeline($"It is a dog! from country {country}");
},
isCat: (size) => {
// it is a cat
Console.Writeline($"it is a cat of size {size}");
});
}
我觉得这是一个 XY 问题。为什么要使用摘要 class?
总是一种避免创建摘要的方法class - 除了必须从您所在的图书馆中提取class摘要消耗但无法控制。如何?使用接口,当您需要通用功能时,使用静态函数或与另一个组合使用 class.
我知道这不是您问题的直接答案,但它可能会解决您的问题。
public interface IAnimal
{
string Name { get; set; }
}
public class Dog : IAnimal
{
public string Name { get; set; }
public string Country { get; set; }
}
public class Cat : IAnimal
{
public string Name { get; set; }
public string Size { get; set; }
}
由此,您最终得到两个 classes,它们都可以用作 DTO,它们都没有不必要的属性。
在运行时你可以输入检查。
IAnimal myAnimal = ... ;
Helpers.DoSomethingWithAnimal(myAnimal);
switch (myAnimal)
{
case (Cat cat):
Helpers.DoSomethingWithCat(cat);
break;
case (Dog dog):
Helpers.DoSomethingWithDog(dog);
break;
}
序列化时,您可以插入一个额外的 属性 来跟踪您需要将数据反序列化回的动物类型。
{
name: "My Dog",
country: "Romania",
"__animal-type": "dog"
}
要回答这个问题,您可以查看 SOLID 原则,尤其是 the Single Responsibility Principle,它指出 class 必须只有 一个 改变的原因。
如果您的 DTO 重新组合属于动物王国的所有 classes 的所有属性,则每次需要更改这些 classes 时,它都必须更改。如果 Cat 更改,它将更改。如果狗改变,它也会改变。等等。我会避免这种情况。
如其他答案所述,如果可能的话,可能值得重新考虑设计。