views:

246

answers:

4

If two classes both need to know about each other to do their job, i.e. class A must maintain a reference to class B and class B must maintain a reference to class A, is this generally a sign that inheritance would be a better idea than composition, provided you aren't violating the Liskov Substitution principle? My thinking is that this is the case because, if A and B both need to know about each other, they're already pretty tightly coupled and the coupling may be due to essential complexity of the problem you're trying to solve. Inheritance is the most straightforward, convenient way to express such coupling.

Also, I've started to notice that two classes mutually maintaining references to each other and knowing about each other seems to always result in messiness very quickly. Is this a well-accepted smell? If so, what are some ways of refactoring it besides just using inheritance instead of composition?

Edit: A good example of when this comes into play is template method vs. strategy. If your strategy classes require intimate knowledge of the base class, then you could use inheritance and template method instead, or you could simply have a strategy class that knows about the base class.

+3  A: 

Your instincts are correct. It is a bad smell. You should analyze it, worry about it a little, and maybe even put up a question on Stackoverflow (ah, already taken care of). But it can be good design to have classes maintain references to each other behind the scenes without sharing a formal relationship.

Inheritance is only appropriate if class B is truly what class A is and more. Or, if they share a common core of likeness (and reason to change), than they should perhaps both inherit from a common base. And if is only a contract with the outside world that they share, then implement a common interface. etc. But maybe none of these are appropriate, and that's okay. You might need to tweak the referencing of course. Maybe they should both reference a common obejct that references both of them, etc.

Use the cohesion/coupling ratio; as long as that is high, you're doing good. Look at it from different angles. Are they SOLID? Are the classes polite to each other? Does each really represent a describable thing? Do they cohesively hide complexity from the consumers of these objects. Are using the object intuitive and discoverable? Remember, sometimes complexity and bad smell in one place can serve to decrease total complexity for a situation.

But you're right to be suspicious of high coupling.

Patrick Karcher
+8  A: 

It's not necessarily a sign that inheritance is the best idea; do they have the appropriate relationship?

Is A a kind of B? If not, inheritance is just moving the problem elsewhere.

Misko writes that if A needs B and B needs A, then there's a hidden, shared object C that needs to be split into its own class.

I realise this is slightly abstract, but so is the problem.

Mark Simpson
The "Is a" test is not always a very good criteria. See Uncle Bob's example of a square being a rectangle, but a square class inheriting from a rectangle class would be a disaster.
Patrick Karcher
Definitely a fair point. There are always going to be exceptions, but I find the "is A a B?" question works very well for most things. Like you said, we should always think about it :)
Mark Simpson
Great Link, too!
xtofl
Just curious, how (if at all) does this apply to parent/child entity relationships?
gWiz
+1: excellent link
D.Shawley
Good answer. I now see what my C object would be, and I'll keep this in mind for the general case. However, in the case that made me ask this I think I'm going to leave it as inheritance because I know that the types of change that using inheritance makes easy are likely and the types of change that doing it "properly" makes easy are not likely in this specific project.
dsimcha
+2  A: 

Without giving the concrete examples, I can just suggest using the Observer pattern in order to get rid of the mutual dependency.

Also, you can check this recent dzone article. It is fairly abstract, and regards modules rather than individual classes, but you can transfer the knowledge.

Bozho
A: 

You are right that if A and B both need to know about each other, they're already pretty tightly coupled. But inheritance is definately not a good idea in this scenario.

1. A depends on B
2. B depends on A

.

Both classes are coupled together and there is a cyclic dependency between both classes.

I would propose to break this cyclic dependency by having two interfaces InfAForUseByB and InfBForUseByA such that

1. A implements InfAForUseByB
2. B implements InfBForUseByA
3. A depends on InfBForUseByA
4. B depends on InfAForUseByB
Gladwin Burboz
btw: your dependency-structure does not change the initial problem: where do we start? ... do we start with A, or with B. it's still a cycle (interfacing does basically not change anything!)
Andreas Niedermair
Also, what if you only have one class that implements InfAForUseByB or InfBForUseByA and don't anticipate ever having more? Then you're just faking the reduced coupling and adding another layer of indirection and complexity to do it.
dsimcha
Andreas (dittodhole):If you draw class diagram for original problem and solution I suggested, you will see that there is no cyclic dependencies. Issue you have is that you attempting to compose this objects into each other by constructor injection but if you would do setter injection there won't be any issues. This is one of the reason why setter injection is prefered over constructor injection.
Gladwin Burboz
dsimcha: Orignal Question -There is cyclic dependency between A and B. what are some ways of refactoring it besides just using inheritance instead of composition? ... As pointed by Mark, one of the solution is shared object C. Although most of time this is the solution, but this is not always possible as due to problem complexity C may start depending back on A and B. In such a scenario, you could potentially use the solution above.
Gladwin Burboz
dsimcha:Yes, there is another layer of indirection due to which there will be added complexity but if you note, it is not faking the reduced coupling. It is actually removing the coupling. Further more, tomorrow if there is another class A1 which implements InfAForUseByB, even that can be used by class B as class B does not depends on class A but depends on interface InfAForUseByB.
Gladwin Burboz