views:

101

answers:

2

I'm developing a game that maintains its information in a class called WorldState. Each GameObject in the world (trees, zombies, the player, health packs, etc) is composed of three objects: GameObjectController, GameObjectModel, and GameObjectView. (These classes may be extended if the GameObject requires additional functionality.)

WorldState has a reference to an implementation of the interface IPersistentState, which is used to load and save GameObjects. Right now, the implementation of IPersistentState is ReadWriteXML, which uses XML. I fear that the IPersistentState interface is too coupled to using XML - if I were to switch to using a database, or csv, or whatever, it would probably be necessary to change it. I don't think I'm creating enough of an abstraction.

The IPersistentState interface:

/// <summary>
/// Used to read and write from persistent memory.
/// 
/// When dictionaries are returned, they are key-value pairs:
///     dict["key"] == value
///     
/// Reading is done from whatever the current URI is.
/// </summary>
// This is too hard coded to XML.
public interface IPersistentState
{
    /// <summary>
    /// Gets the KV for every object of element `elementName`. 
    /// Really this is just a convenience/wrapper method for `GetAllOfTypeIn()`
    /// where the ancestor is the root.
    /// </summary>
    /// <param name="elementName">the element of object to get, like "zombie" or "point"</param>
    /// <returns>(See interface summary)
    ///             For Zombie, it could look like this:
    /// 
    ///             dict["positionX"] == "23"
    ///             dict["positionY"] == "4"
    ///             
    ///             etc
    /// </returns>
    ICollection<IDictionary<string, string>> GetAllOfType(string elementName);

    /// <summary>
    /// Creates or overwrites the file `fileName`, filling it with the data in `dicts`.
    /// The root of the data is `rootName`.
    /// </summary>
    /// <param name="dicts"></param>
    /// <param name="rootName"></param>
    /// <param name="fileName"></param>
    void Write(ICollection<IDictionary<string, string>> dicts, string rootName, string fileName);

    /// <summary>
    /// Appends the information in `dict` to the end of the current file, under the element `elementName`.
    /// </summary>
    /// <param name="dict">The key-value pairs to append to the file identified by the current URI</param>
    /// <param name="elementName">The root of the key-value pairs to append</param>
    void Append(IDictionary<string, string> dict, string elementName);

    /// <summary>
    /// Sets the current URI from which to read.
    /// </summary>
    /// <param name="uri">Presumed to be in the Data/ directory.</param>
    // not generic?
    void SetURI(string uri);

    /// <summary>
    /// Gets the KV for every object of element `elementName`. 
    /// The search is constrainted to within `ancestor`.
    /// </summary>
    /// <param name="elementName">The element to look for</param>
    /// <param name="ancestor">The element in which to search</param>
    /// <returns>(See GetAllOfType)</returns>
    ICollection<IDictionary<string, string>> GetAllTypeIn(string elementName, string ancestor);
}

Here is example usage from a WorldState loading method:

        persistentState.SetURI(configURI);
        ICollection<IDictionary<string, string>> levelVariantURIDicts = persistentState.GetAllOfType("levelVariant");

        // OH YEAH LINQ!!
        levelVariantURIs = levelVariantURIDicts.Select(dict => dict["filePath"]).Distinct().ToList();

configURI points here:

<config>
  <levelVariant>
    <filePath>SampleVariant</filePath>
  </levelVariant>
  <levelVariant>
    <filePath>LegendaryMode</filePath>
  </levelVariant>
  <levelVariant>
    <filePath>AmazingMode</filePath>
  </levelVariant>
</config>

What do you think about this? Perhaps the abstraction won't work for a database, but it would for different file types? Maybe I should just hope that the project always uses XML? How can I change this to make it more generic?

+1  A: 

From the wording of your question, it sounds like you already understand this and just aren't sure which way to go, but I'll answer in the form of a "how I think you should do it".

If you want a truly abstract data access layer interface, you need to define your model and then build your interface to your model, not to any particular implementation. For example, you have rootName, fileName, elementName as parameters in your interface methods, which sounds like you were thinking about XML when you wrote it.

Instead of thinking about an implementation, think about how each method affects the model, and then let the implementation(s) of the interface worry about how to actually store and change the underlying data.

For instance, if your model has a hierarchical nature, then define the levels of the hierarchy in terms specific to the domain (your game), and use those terms to define the generic interface.

So maybe your model has a structure like:

  Player
    Player Attribute
    Player Possessions
    Player Score
  City
    Building
      Objects
        Immutable Object
          Object property
        Mutable Object
          Object Property
          Object Action

etc.

Then your interface methods should use these concepts.

So you might define a method to save some set of player attributes, like:

void SavePlayerAttributes(string playerName, ICollection<IDictionary<string, string>> attributeNamesAndValues);

Or a method to create a new object at a certain location:

void CreateImmutableObject(string cityName, string buildingName, ILocationCoordinates coordinates, IObjectPropertySet objectProperties);

Very rough, but hopefully you get the idea.

Charles
A: 

I'm inclined to think your current implementation is fine for now because:

  1. It's hard to anticipate the changes you'll make in the future and you probably have more critical things to focus on right now than premature abstractions.
  2. You already have the IPersistentState abstraction, so if you do need to change the implementation later, it shouldn't be too hard to do so at that time.
  3. XML isn't necessarily a bad format for other storage mediums. For example, it could be a perfectly reasonable format to store in a database if you're only using it for serialization/deserialization and don't need structured queries inside the data.
C. Dragon 76