Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: Enforcing doubly linked objects

I am designing a game engine in Java.

At the core of this engine exist the two classes Asset and Attribute, where an Asset has a list of Attributes. Most Attributes need no link back up to their Attribute, meaning that Attributes can and often do appear in the lists of more than one Asset. However, there is an extention of Attribute called UniqueAttribute, which is an implementation for those that are specific to their Asset, and utilise a link back.

  1. Ideally, my Asset's addAttribute method would look something like this if I cut out the other code:

    public void addAttribute(Attribute attribute){
      if(attribute instanceof UniqueAttribute)
        ((UniqueAttribute)attribute).setAsset(this);
      attributeList.add(attribute);
    }
    

    Unfortunately, since they live in different packages, UniqueAttribute.setAsset() must be public. This leaves the method open to outside users of the engine to mess with, and while I could just handwave it off by saying using this method directly is a bug - it seems rather sloppy.

  2. The second option is to provide the UniqueAttribute with the Asset on construction, meaning that the code at the point of creation would look something like this:

    asset.addAttribute(new UniqueAttribute(asset));
    

    While I can add a check-and-throwable or assert to confirm the correct asset is passed in, I am basically relying on the user to connect the two, which I also would prefer not to do.

  3. The third option is to bite the bullet and put 50 java files all into the same package so that I can just use the standard visiblity.

Is there some kind of pattern or something that will help link these two together without exposing the wires, or forcing me to put everything into one massive package?

Irrelevant rant: I have always disliked that the concept of subpackages in java has not really been expanded in any meaningful way. A subpackage, as far as java is concerned is simply a different package, and there have been many occasions I could do with more visibility modifiers directly related to this.

like image 304
Numeron Avatar asked Sep 20 '11 08:09

Numeron


3 Answers

My suggestion would be that Asset, Attribute and UniqueAttribute should all be in the same package (possibly along with a few other core "engine" classes). Then you can use standard package visibility for UniqueAttribute.setAsset.

You don't need to put all other classes in the same package - your Asset.addAttribute method should be public and accessible from other packages so the rest of your application can just use that directly.

So the solution could be called "3-" in your categorisation.

As some more general points, also consider:

  • Whether you really need the complexity of both Attributes and UniqueAttributes - I'm not sure you really do, having previously implemented a reasonably complex game object model without needing anything that looked like a UniqueAttribute. If the UniqueAttribute "needs a link back" then perhaps it is trying to be too clever / do too much?
  • Even if you do need both, do you really want to write code that treats them the same way / as part of the same object heirarchy? they seem quite conceptually different, and you will end up writing a lot of conditional code if you conflate the two.....
  • There are various other advantages of attributes being consistently shared and immutable - it's better for memory usage, concurrency and testability among other things. And as they are presumably quite small, the cost of copy-on-write semantics is trivial in the cases where you need it.
like image 82
mikera Avatar answered Nov 20 '22 10:11

mikera


I would add a callback method in Attribute which is called when an instance of Attribute is added to an Asset:

class Attribute {
    protected void addedToAsset(Asset asset) {
        // do nothing
    }
}

This method would be called in the addAttribute method

class Asset {
    public void addAttribute(Attribute attribute) {
       attributeList.add(attribute);
       attribute.addedToAsset(this);
    }

}

And the method would be overridden in UniqueAttribute in order to control the link with Asset:

class UniqueAttribute extends Attribute {
    Asset asset;
    protected void addedToAsset(Asset asset) {
        // manage the previous link if needed
        if (this.asset != null) { ... }
        this.asset = asset;
    }
} 

Wit this solution, Asset and Attribute should be placed in the same package. But UniqueAttribute could be in whatever package you want.

like image 2
Laurent Legrand Avatar answered Nov 20 '22 12:11

Laurent Legrand


Modify your scond option

asset.addAttribute(new UniqueAttribute(asset)); 

like this:

class UniqueAttribute {
Asset asset;
     public UniqueAttribute(Asset asset) { this.asset = asset; asset.addAttribute(this); }
}

Do a similar approach for the non unique Attribute. That means instead of using addAttribute() from the outside, only use it inside of the constructors.

An other option is to add two factory methods to Asset: createAttribute() and createUniqueAttribute();

like image 1
Angel O'Sphere Avatar answered Nov 20 '22 10:11

Angel O'Sphere