0

For a game I'm making I have two objects; Gun and ReloadSystem. At the moment, these two object reference each other. Gun tells ReloadSystem to perform a reload when the gun gets clicked, and ReloadSystem changes the ammunition and tells Gun when it can shoot again. My idea is that these objects are a composition where Gun is the parent and ReloadSystem is the child like so: enter image description here

I've been wondering how to solve the circular depedency between the two items. So far I have thought of two solutions:

  1. I could move the logic of onClick() to another class, let's say GunEventHandler. But then the composition goes the opposite direction, as in that ReloadSystem has a Gun instead of the other way around.
  2. I could make another interface called Reloadable, make Gun implement it and ReloadSystem depend on it. However in the implementation classes the circular reference would still be there, but through higher abstractions.

What do you think is the best approach here? I appreciate your help very much.

3 Answers 3

4

1 and 2 are the same option as far as dependency knowledge goes. Without them the ReloadSystem has to know what a Gun is. Narrow what the ReloadSystem knows down to Reloadable and it doesn't care what's implementing it.

Doing this follows ISP. But comes at a cost.

That doesn’t break the cycle.

If you really want to solve the circular dependency try this:

          .-[Gun]-.
       <>’         ‘<>
   [Chamber]<-[ReloadSystem]
       ^
   +---+---+
[Round] [Empty]

No more cycles. The gun fires whatever’s in the chamber and whatever’s there does whatever it’s going to do when fired.

Now sure. Chamber is “reloadable”. It’s also “fireable”. But that’s it. You’ll have to decide if you really need to follow ISP to the extreme here.

The big change is the gun doesn’t know what will happen if fired. So it doesn’t need to be told what will happen. So the reload system doesn’t need to know about the gun at all. No cycle.

6
  • +1 I think there are a number of other ways you could model this, but the introduction of new objects is a general solution for relationships where it's not clear who's 'in charge'.
    – JimmyJames
    Commented Feb 12 at 15:16
  • @JimmyJames you’re right. I’m mostly reacting to the idea of the gun needing to “know” if it can fire. I don’t like that knowledge residing in two places. This is simply an example design that resolves that (as well as the cycle). There are others. Commented Feb 12 at 16:50
  • I honestly didn't ponder your solution very deeply, but it seemed to obviate the need for me to create my own answer. I prefer this kind of refactoring over visitor-style trapeze acts.
    – JimmyJames
    Commented Feb 12 at 16:57
  • @JimmyJames thank you. I’d love to see what you come up with. Your answers are always worth a read. Commented Feb 12 at 17:13
  • Thanks for the compliment. I guess I'll take a crack at it. How'd you know I was so susceptible to peer pressure?
    – JimmyJames
    Commented Feb 12 at 22:08
2

One solution to your problem is a callback system, where the Gun "has a" ReloadSystem. The Reload method takes a reference to the Gun as a parameter. So at the end of Reload it can call back to its parent. The circular dependency is only temporary.

1

I am not sure which of the circular dependencies you are concerned with, there are at least three:

  1. Compile time circular dependency.
  2. Memory/Reference circular dependency (each object holds a reference to the other).
  3. Construction circular dependency (you cannot immutable create both objects using only the constructors).

Taking each in turn:

1. Compile Time

There is a pattern (some would say anti-pattern) of creating one or more interfaces for every single object. Given this, it is possible for two interfaces to have a circular dependency (each interface could have a method which takes the other interface as a parameter), however all concrete objects only reference interfaces, so there is no compile time dependency between the concrete classes.

In a real world program I would not recommend going this far, however the thought experiment of what this does to your code is useful, for example (using the I prefix to indicate an interface) means that:

  • Gun only depends on IReloadSystem
  • ReloadSystem only depends upon IGun.

You can create multiple classes that implement IGun and multiple classes that implement IReloadSystem - hence no Gun is tied to any particular ReloadSystem

2. Memory/Reference

In Java you typically don't have to worry about reference counting / freeing memory (the garbage collection typically manages that for you). Hence references to other objects are typically just a short hand for control flow:

If I call a method on object (A) and (A) has a reference (direct or indirect) to object (B) there is a possibility that a method on (B) gets invoked. Additionally if both (A) and (B) have references to each other there is a possibility of an infinite loop / stack overflow - if each object calls the other one.

In your case a Gun and a ReloadSystem are tightly intertwined - if I was reading your code I would not be surprised if they invoked each other - in short I don't see it as a problem that they call each other.

That said a Gun is probably a collection of functionalities, so it is likely as you refactor it that Gun simply becomes a (top level) collection and it is other subsystems (of the gun) that have the circular reference dependencies between them.

3. Object Construction

If I only define constructors as follows, for two Java objects:

class A {
    A(B b) {}
}

class B {
    B(A a) {}
}

It becomes (almost **) impossible to construct them since to construct the first one and I need to construct the other one first, hence there is a catch 22.

** - Some inversion of control frameworks can create a dynamic proxy to resolve this constraint, but it is bad practice, since you still have the same problem when you want to test your classes.

Hence the better solution is simply to add a setB(B b) method on class A, so that you can construct (A) first, construct (B) then call setB() on (A) to finish the construction - if you go this path I would suggest using a factory to construct both objects.

Summary

I would probably introduce interfaces, so that you don't tie reload systems to particular guns. Then I would add setReloadSystem() methods to each of the guns - as it makes a little more sense to me to construct the Gun first before constructing the ReloadSystem, but your milage may vary on that point.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.