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.
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.
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.
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.
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:
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.
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();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With