2

Given a class hierarchy such as:

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

Which are all immutable.

To implement 'move' on an entity I can return a new entity with a different position.

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

However if I call 'move' on an Avatar, I will get an Entity, not an Avatar. So I have to implement 'move' on all immutable classes. Is there a way to avoid this, or a better solution?

Timothy Pratley
  • 10,586
  • 3
  • 34
  • 63

3 Answers3

3

you can resolve this with generics, i am assuming for the sake of simplicity that there are protected setters for all the properties:

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

  }
}

To allow the current types to remain as they are you can do a simple inheritance of the generic base for each concrete type:

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

For a examples for deep cloning you can follow this link although i should point out that if performance is important, it would be better to require each inheriting class to override this method and add their own properties to the cloning process.

Community
  • 1
  • 1
NightDweller
  • 913
  • 5
  • 8
  • Essentially you are advising me to clone and set, which works just fine without any generics... am I missing something important about the generics part? – Timothy Pratley Apr 17 '11 at 03:16
  • 1
    @Timothy - You can do it without generics, but then the return type would be Entity and not the Concrete child type. You'd have to do an unsafe cast which can be avoided by using the above method – NightDweller Apr 17 '11 at 09:08
  • Oh I see. I'm not too concerned about the return type yet, so long as the object is preserved, but useful to keep in mind - thanks! :) Looking back I think my question is a bit misguided in that what was really tripping me up was the object being returned, not the function signature. – Timothy Pratley Apr 19 '11 at 00:24
0

You need to decouple the logic of movement from your model. (always stick to SOLID principles). The rest is similiar to NightDweller's post

your code may look like this:

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> {...}

or

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

then implement this interface for ech of your classes.

depending on the algorithm of movement, you may also consider using decorator pattern.

gido
  • 31
  • 3
0

Having tried to use immutable types extensively in some hobby projects, I came to the conclusion that in C#, they are quite a bit of effort except in the following special case: the type is a struct or a sealed class inheriting from object, and has no fields that are collections.

In all other cases I think immutable types are far more trouble than they are worth in C#, unfortunately, even though I'd rather like to use them more.

Are you sure you want this hierarchy of classes to be immutable?

It's already tricky, and the moment you add a property/field that is a collection your difficulties will shoot through the roof. For example, unless you do it very carefully, .Move will have to create deep copies of collections. But even if you are careful enough with .Move, replacing a single property of a single element in a collection will definitely require the whole collection to be copied. Etc...

enverpex
  • 1,080
  • 2
  • 8
  • 13
  • Thanks for your comments. The reason I am using immutable classes is to support time-travel. Using persistent maps I keep a history of previous states. Dealing with collections has been the easy part, thanks to FSharpMap (from Microsoft.FSharp.Core) and other useful immutable data structures. It is working very well for my purposes so far using simple entities... until I ran into the complexity of class hierarchical state changes. – Timothy Pratley Apr 17 '11 at 01:23
  • @Timothy I'll check out FSharpMap next time I'm feeling like writing some bullet-proof code for hobby - haven't come across it before. – enverpex Apr 17 '11 at 01:31