5

我已经进行了一些搜索(也许我没有很好地描述我的问题)但无法找到答案

假设您有以下 POCO:

public class StandardObject
{
   public string A {get;set;}
   public string B {get;set;}
}

在程序的其他地方有一些逻辑来处理标准对象。有时可能需要以不同方式处理 StandardObject 的情况。我们在创建 StandardObject 时就知道这一点,但是当前的任何属性都不能用于进一步确定这一点。一种方法是在 StandardObject 上添加和设置标志或类型枚举,并在处理对象时检查它。例如

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 附近的任何地方

4

3 回答 3

3

它们都是代码气味。如果您有必须在两种相关类型之间以不同方式完成的特殊处理,请让它们都实现一些允许它们处理特殊情况的虚拟方法或接口操作(如果没有必要,也可以不处理)。

于 2012-09-07T02:48:06.583 回答
1

您还可以使用策略模式封装您的“处理”逻辑,方法是拥有一个抽象基类,您还可以在其中放置共享实现。或者您也可以调用派生实现和继承实现。

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();

    }
}
于 2012-09-07T03:03:32.717 回答
0

如果您需要根据对象的属性以不同的方式处理对象,则最好将方法用于在类内部操作对象。对象的行为可以由一些私有状态变量或派生对象的类型决定,但调用代码不需要知道这一点。

概括地说,我们有一个元素需要根据某些属性以特定方式绘制。

如果该类只是在其中进行处理,则您只需要做:

MyElement.Draw();

代替:

if(MyElement.Flag)
{
     DrawBlue(MyElement);
}
else
{
    DrawRed(MyElement);
}

对于调用代码,无需公开必须确定特定行为的方式。

于 2012-09-07T02:57:41.180 回答