如何避免具有继承的不可变数据模型中的代码重复?

时间:2011-04-16 23:01:41

标签: c# inheritance immutability

给定一个类层次结构,例如:

Entity { id, name, position }
Combatant : Entity { health, strength }
Avatar : Combatant { connection }

哪些都是不可变的。

要在实体上实现“移动”,我可以返回具有不同位置的新实体。

Entity Move(p) { return new Entity(id, name, p); }

但是,如果我在“阿凡达”中调用“移动”,我将获得一个实体,而不是“阿凡达”。所以我必须在所有不可变类上实现'move'。有没有办法避免这种情况或更好的解决方案?

3 个答案:

答案 0 :(得分:3)

你可以用泛型来解决这个问题,为了简单起见,我假设所有属性都有受保护的setter:

Entity<InheritingType>
  where InheritingType : Entity<InheritingType>
{

  public T Move(Position newPosition)
  {
      T result = this.Clone();
      result.Position = newPosition;
      return result;
  }

  private T Clone() 
  {
     //create a new instance of ourselves using reflection
    //i.e. reflect all the protected properties in the type (or fields if you don't want     even protected properties) , and set them
    //you could also have the Clone method be abstract and force it's implementation in all inheriting types

  }
}

要允许当前类型保持原样,您可以为每种具体类型执行通用基础的简单继承:

Entity : Entity<Entity>{}
Combatant<InheritingType> : Entity<InheritingType>{}
Combatant : Combatant<Combatant>{}
Avatar : Combatant<Avatar>{}

对于深度克隆的示例,您可以follow this link,但我应该指出,如果性能很重要,最好要求每个继承类重写此方法并将其自己的属性添加到克隆过程中。

答案 1 :(得分:0)

您需要将移动逻辑与模型分离。 (始终坚持SOLID原则)。其余的类似于NightDweller的帖子

您的代码可能如下所示:

pubilc interface IMovementLogic<T> where T:Entity
{
     T Apply(Position p); 
    //You can name the method anything else you like such as "Move" or "execute
}

public class EntityMovement : IMovementLogic<Entity> {...}
public class CombatantMovement : IMovementLogic<Combatant> {...}
public class AvatarMovement : IMovementLogic<Avatar> {...}

public class EntityMovement<T> : IMovementLogic<T> where T:Entity {...}
public class CombatantMovement : EntityMovement<Combatant> {...}
public class AvatarMovement : EntityMovement<Avatar> {...}

然后为你的类的ech实现这个接口。

取决于移动算法,您也可以考虑使用装饰器模式。

答案 2 :(得分:0)

试图在一些业余爱好项目中广泛使用不可变类型,我得出的结论是,在C#中,除了以下特殊情况外,它们还有很多功夫:类型是继承自{的结构或密封类{1}},并且没有集合的字段。

在所有其他情况下,我认为不可变类型比C#中的价值要大得多,不幸的是,尽管我更愿意使用它们。

您确定要让这个类层次结构不可变吗?

这已经很棘手了,当你添加一个属性/字段作为一个集合时,你的困难将在屋顶上射击。例如,除非您非常小心地执行此操作,否则.Move必须创建集合的深层副本。但即使你对.Move足够小心,替换集合中单个元素的单个属性肯定会要求复制整个集合。等...