patterncsharpMinor
Automatically update one-to-many bi-directional relationship
Viewed 0 times
updateautomaticallydirectionalonemanyrelationship
Problem
In an example game engine server, the game world could be represented by a World object. The game might have multiple game worlds and a player who is in a world only needs to receive position data of units in that world.
However a piece of code also needs to be able to look up what world a unit is in easily, so the unit object itself keeps track of a world reference.
This works for lookup, but quickly becomes problematic when changing data when the programmer isn't aware of the relationship between objects going on, so I changed
This works, but it feels like there's a better way to do this. Especially as I add more stuff that needs to be linked the same way (a
class World
{
public List Units { get; set; }
}However a piece of code also needs to be able to look up what world a unit is in easily, so the unit object itself keeps track of a world reference.
class Unit
{
public World World { get; set; }
}This works for lookup, but quickly becomes problematic when changing data when the programmer isn't aware of the relationship between objects going on, so I changed
Units in World to be readonly and have the following code in Unit.public virtual World World
{
get { return _world; }
set
{
// If already this, don't do anything
if (value == _world) return;
var oldWorld = _world;
_world = value;
if(oldWorld != null) oldWorld.UpdateUnitEntry(this);
_world.UpdateUnitEntry(this);
}
}This works, but it feels like there's a better way to do this. Especially as I add more stuff that needs to be linked the same way (a
World also has Structures and Players, and Structures maintain a list of Units as well), a lot of repeated functionality comes in. As well when I remove a unit from the game, it continues to be kept alive by this, so I have to remember to set the unit's structure to null every time (which I already forgot multiple times, resulting in weird bugs). Is there a better way to achieve this one-to-many relationship without manually updating both sides?Solution
The best way to accomplish this is to have each
Your unit would then have this as a property, and a
The
If you wanted to get more fancy, you could have a
This will get the movement logic out of the
I might be missing something, but my main point is to use an interface for each object that a
World, Structure, and anywhere else a unit can move to inherit an interface IEnterable, which looks likepublic interface IEnterable
{
void Enter(Unit unit);
void Leave(Unit unit);
}Your unit would then have this as a property, and a
MoveTo(IEnterable location) methodpublic class Unit
{
// ...
public IEnterable CurrentLocation { get; set; }
// ...
public void MoveTo(IEnterable location)
{
if (CurrentLocation != null)
{
CurrentLocation.Leave(this);
}
location.Enter(this);
}
}The
Enter and Leave would handle all the logic for the Unit movement.If you wanted to get more fancy, you could have a
Movement class that could describe the movement a little more accurately.public class Movement
{
private readonly IEnterable _destination;
public Movement(IEnterable destination)
{
if (destination== null) throw new ArgumentNullException("destination");
_destination = destination;
}
public MoveResult MoveUnit(Unit unit)
{
if (unit == null) throw new ArgumentNullException("unit");
var currentLocation = unit.CurrentLocation;
if (currentLocation == _destination) return MoveResult.SameLocation;
if (!IsValidMove(currentLocation, _destination) return MoveResult.InvalidMovment;
currentLocation.Leave(unit);
_destination.Enter(unit);
}
}This will get the movement logic out of the
Unit class because a unit should not care about how it has to move, just that it has.I might be missing something, but my main point is to use an interface for each object that a
Unit can enter, and the ease of moving a Unit becomes much better.Code Snippets
public interface IEnterable
{
void Enter(Unit unit);
void Leave(Unit unit);
}public class Unit
{
// ...
public IEnterable CurrentLocation { get; set; }
// ...
public void MoveTo(IEnterable location)
{
if (CurrentLocation != null)
{
CurrentLocation.Leave(this);
}
location.Enter(this);
}
}public class Movement
{
private readonly IEnterable _destination;
public Movement(IEnterable destination)
{
if (destination== null) throw new ArgumentNullException("destination");
_destination = destination;
}
public MoveResult MoveUnit(Unit unit)
{
if (unit == null) throw new ArgumentNullException("unit");
var currentLocation = unit.CurrentLocation;
if (currentLocation == _destination) return MoveResult.SameLocation;
if (!IsValidMove(currentLocation, _destination) return MoveResult.InvalidMovment;
currentLocation.Leave(unit);
_destination.Enter(unit);
}
}Context
StackExchange Code Review Q#43644, answer score: 7
Revisions (0)
No revisions yet.