C# 继承与类型标志
我已经做了一些搜索(也许我没有很好地描述我的问题),但还没有找到这个问题的答案 假设您有以下POCO:C# 继承与类型标志,c#,oop,inheritance,types,C#,Oop,Inheritance,Types,我已经做了一些搜索(也许我没有很好地描述我的问题),但还没有找到这个问题的答案 假设您有以下POCO: public class StandardObject { public string A {get;set;} public string B {get;set;} } 在程序的其他地方有一些处理标准对象的逻辑。 有时,标准对象可能需要以不同的方式处理。当我们创建StandardObject时,我们知道这一点,但是当前的属性都不能用于确定链的下一步。一种方法是在Standard
public class StandardObject
{
public string A {get;set;}
public string B {get;set;}
}
在程序的其他地方有一些处理标准对象的逻辑。
有时,标准对象可能需要以不同的方式处理。当我们创建StandardObject时,我们知道这一点,但是当前的属性都不能用于确定链的下一步。一种方法是在StandardObject上添加并设置一个标志或类型enum,并在处理对象时对此进行检查。例如
if(standardObject.IsSpecial){...}
if(standardObject.ObjectType == StandardObjectTypeEnum.Special){...}
但这似乎不是最好的方法
另一个选项是创建派生类:
public class SpecialObject : StandardObject { }
因此,现在我们可以检查类型,而不是检查属性。例如
if(standardObject.GetType() == typeof(SpecialObject)){...}
(根据我们正在做的事情,类型检查的实现可能会有所不同)
请注意,SpecialObject不会以任何方式添加或更改StandardObject。它本质上是同一个对象。这种方法的优点是更灵活(例如,我们可以向SpecialObject添加一些附加属性),但实际上它不会改变。它将永远是相同的
对我来说,继承似乎是更好的方法。类型标志看起来像代码气味,而向前继承更像是正确的OOP方法。我不确定的是,鉴于StandardObject和SpecialObject是相同的,这样做是不是不好的做法?或者有什么理由应该避免这种情况 这里有一个类似的问题: 然而,大多数讨论似乎集中在国际象棋问题上,而不是什么是好的设计 编辑: 封装似乎是流行的解决方案。我避免封装的原因最好在下面的示例中描述:
- StandardObject本质上是一个DTO
- 有一个StandardObject的列表
- 列表得到处理,StandardObject得到序列化
- 序列化数据通过任意数量的不同协议发送到某处
- 其中一个协议要求在StandardObject为“特殊”时设置某些参数
考虑到“特殊”情况下的附加逻辑仅由处理StandardObject的多个不同机制中的一个所需,因此该逻辑似乎不属于StandardObject附近的任何地方,它们都是代码气味。如果您有两种相关类型之间必须以不同方式完成的特殊处理,请让它们都实现一些虚拟方法或接口操作,以允许它们处理特殊情况(如果没有必要,也可以不处理) 如果您需要根据对象的属性以不同的方式处理对象,那么最好将操作对象的方法放在类内部。对象的行为可以由某个私有状态变量或派生对象的类型确定,但调用代码不需要知道这一点 为了勾勒出这一点,假设我们有一个元素,它需要根据某些属性以特定的方式绘制 如果类中仅包含处理,您只需执行以下操作:
MyElement.Draw();
而不是:
if(MyElement.Flag)
{
DrawBlue(MyElement);
}
else
{
DrawRed(MyElement);
}
对于调用代码,不需要公开必须确定特定行为的方式。您还可以使用策略模式封装您的“处理”逻辑,方法是创建一个抽象基类,您还可以在其中放置共享实现。或者也可以调用派生实现和继承实现
public abstract class Vehicle
{
private MovementStrategy movementStrategy;
protected Vehicle(MovementStrategy strategy)
{
this.movementStrategy = strategy;
}
public void Move()
{
movementStrategy.Move();
}
public virtual void CommonAlert()
{
Console.WriteLine("Base generic vehicle alert");
}
}
public class Car : Vehicle
{
public Car(MovementStrategy movementStrategy)
: base(movementStrategy)
{
}
public override void CommonAlert()
{
Console.WriteLine("Car says 'Honk!'");
}
}
public class Elevator : Vehicle
{
public Elevator(MovementStrategy movementStrategy)
: base(movementStrategy)
{
}
public override void CommonAlert()
{
Console.WriteLine("Elevator says 'Ding!'");
base.CommonAlert();
}
}
public abstract class MovementStrategy
{
public abstract void Move();
}
public class CarMovementStrategy : MovementStrategy
{
public override void Move()
{
Console.WriteLine("Car moved");
}
}
public class ElevatorMovementStrategy : MovementStrategy
{
public override void Move()
{
Console.WriteLine("Elevator moved");
}
}
然后,对于主程序,使用以下示例:
class Program
{
static void Main(string[] args)
{
Vehicle elevator = new Elevator(new ElevatorMovementStrategy());
Vehicle car = new Car(new CarMovementStrategy());
elevator.Move();
car.Move();
elevator.CommonAlert();
car.CommonAlert();
Console.Read();
}
}
为什么不能在StandardObject中添加一个特殊的布尔值呢?你说“似乎不是最好的方法”-为什么不是?@wohanley:我可以,但问题的关键是确定更好的方法。类型标志对我来说似乎是代码的味道,但对于这个例子来说,它可能是最好的方法。这就是我想要了解的,标准对象和特殊对象显然不一样;一个是标准的,另一个是特殊的。关于你的编辑:是什么让一个特殊的对象变得特殊?协议能否通过检查对象属性或字段的值来确定对象的特殊性?特殊性是由对象的创建者确定的任意属性吗?这里“特殊”的真正含义是什么?StandardObject包含一些原始二进制数据。这些数据可以来自不同的来源。其中一个协议要求,如果数据来自特定的源,则必须设置某些特定的参数。无法从任何当前属性确定此值。抱歉,如果它看起来很模糊,但我试图让问题尽可能一般化,因为我更感兴趣的是最佳实践,而不是具体的解决方案。我做了一个编辑,解释了为什么我要避免封装。我不确定你对此是否有任何想法OK,在这种情况下,问题是:处理发生在哪里?如果DTO只是数据,而其他一些控制器正在对数据进行操作,则选择类型标志,而不是特殊继承和类型检查。如果处理发生在对象本身上,请正确使用继承,而不是执行您列出的任何一种类型检查。有许多派生类继承自“输出”基类。如果其中一个派生类是“特殊”对象,那么它需要一些额外的逻辑,但是逻辑在哪里执行?它是包含在特殊对象中(类本身或基类中),还是包含在对特殊对象中包含的数据进行操作的控制器对象中?如果是第一个,就按照我原来的建议去做。如果是后者,则使用对象中的数据类型字段。应该避免检查对象的类型和基于该类型的分支,除非您使用的是无法重构的遗留代码。