Search code examples
oopinheritanceinterfaceabstract-classcomposition

How to attach one of many sets of implementations to an object?


Suppose I'm modeling a collection of Agents. These agents might be controlled by a Player or by a Bot. Additionally, each Agent has a Role - such as Explorer or Engineer. All Roles share the same common actions, but each one might implement the actions a little differently. Ideally, you would be able to invoke agentInstance.move(), and the action would play out depending on what Role they have.

I've been wrestling with how to model this for a while, and looked through the Strategy and Template patterns specifically - but I'm not sure they're what I need.

One idea is to make Player and Bot subclasses of abstract Agent, and to make Explorer and Engineer subclasses of abstract Role. Then each Agent would have a Role. However, that would seem to imply that I would have to pass-through my commands like this: agentInstance.getRole().move(), or create helper functions like agentInstance.move(){ this.role.move(); } - which seems a bit inelegant.

Example Model - Note that Agent and Role would have to share methods by coincidence.

I'm also aware that my designs can be a bit interface-deficient, so I considered modeling the Roles as interfaces and sub-interfaces with default implementations that are then inherited by Agent, but that feels like a solution looking for a problem.

So - what's the best way to go about modeling this? What considerations are crucial to the decision, especially with how similar Java 8 interfaces and abstract classes are?


Solution

  • I would do it like this:

    • I will make Role an interface since it has no attribute. Explorer and Engineer will be class not interface b'coz that is how Strategy works.
    • Player and Bot will be associated with Agent so that they can issue orders to Agent.

    For me, there are 2 crucial considerations when doing OOP Analysis and Design:

    • Does it solve my problem? If it does then dont fix it! Future problems are for future.
    • It has to be as easy to refactor as possible. As new requirements come in you will have to do a lot of refactoring.

    enter image description here