originally suggested to me, and I've come to believe, that AclassIsNothingButaCyclicDependency
. Any time you spot a DependencyCycle?
of length greater than 2 in a class hierarchy, you have found a class.
[Note by MikeMowbray
: I didn't actually say that a class is "nothing more" than a cyclic dependency. In fact, pure abstract classes need not contain dependency cycles. But yes, a 'concrete' class can often be regarded as a small atomic unit of cyclic dependency. BTW, RaviSethi?
showed ages ago that retaining some cyclic dependency allows a slightly larger class of problems to be solved - he proved it with denotational semantics I recall. So tiny localized cyclic dependencies are not necessarily evil.]
Mike has a neat way of drawing class diagrams, that I also still apply; he embodies a dependency criterion in the vertical dimension of the diagram, so that any class lower in the diagram can implicitly use (pass/compose/aggregate/inherit/...) classes higher in the diagram, but not vice-versa. Not only do all the abstract classes float to the top, but there's a great deal of information available in the diagram about what can
depend on what. This dependency information isn't represented in, say, CrcCard
No, a request: please explain this idea, with an example. -- RonJeffries
Maybe I'm slow, but I read MultipleInheritanceIsNotEvil
and I still don't get it. Maybe an example with real-world object names would help. -- JeffGrigg
I'll grant that perhaps it isn't always true, but I like it a lot so let's see. I don't refer here to cycles of just two classes; I don't think those are profitably broken at all, though I do usually look at them and think about whether one of the dependencies might be profitably removed to an AssociationClass
. But I do this with three or more. I describe the technique of creating a class from the cycle in MultipleInheritanceIsNotEvil
. -- PeterMerel
I didn't understand it there, and I don't understand it here. Please explain why, say, "Person" is nothing but a cyclic dependency. Or "Dollar" or "Integer". Thanks! -- RonJeffries
Maybe a better title for this page would be AcyclicDependencyIsNothingButaClass??
I think some may be interpreting the title too literally. GradyBooch
has been known to say "When things get too complex, add more objects." This seems counter-intuitive to some but is actually on target much of the time, because the extra class is used to add an extra level of indirection and
separation that reduces coupling and dependency (if done right ;-).
So my understanding of what the creator of this page is really trying to say is that if a design has a cyclic-dependency, then it probably means that a class is just waiting to be created that will factor out some responsibility and remove the cyclic dependency. I believe KentBeck
has similar thoughts about when to split (refactor) code in a class into additional methods. -- BradAppleton
"Person" is a noun, not a class. Likewise "Dollar" and "Integer". If we associate them with responsibilities, and we understand their collaborative relationships, then we can call them classes. Now it's these collaborative relationships that we're trying to understand in terms of dependency. What uses what? Given these three nouns it's hard to build a cycle, but it's doable. Let's say we have
- Person uses Dollar to buy ledgers
- Dollar inherits from Integer
- Integer uses Person to record themself
So using arrows for dependencies, I see
Dollar -> Integer
What class is lurking in this cycle? Well, Ledger is a pretty good candidate; after all we just used the word. So if we say that Person aggregates Ledger, then the dependencies look like
Dollar -> Integer -> Ledger
[I suggest people avoid tabs in diagrams. They display inconsistently.]
So my conjecture here is that all dependency cycles hide classes. So why phrase it the other way around? Because I can always do this transformation in reverse. It just makes things less well factored to do so. In what way less well factored? Instance variables would get turned into parameters. Maintenance would find itself chasing its own tail. And the classes would take on extraneous responsibilities; in the cycle above, for example, I'd be requiring all people to be able to record Integers; Person.record(Integer) just seems like a pretty foolish contract to require all classes deriving from Person to meet.
But, more than this, it's fair to say that everything inside a (well-factored) class relies on everything else inside it. I think this is where our need for privacy emerges, so that each part of the implementation of a class can safely depend on each other part.
It still seems non-obvious to me. Would you say that whenever a class is removed from a system, a cyclic dependency is introduced? For example, if we remove Integer from your diagrams?
So long as Integer served a functional purpose, yes, I think I'd say that. At least, counter-examples don't occur to me.
I can sense you have some notion of layers of abstraction, and a belief that all well-factored systems are layered, and the presence of a cyclic dependency means that the system is not well-layered so you want to introduce a new abstraction to break the loop. But then I lose it. -- DaveHarris
You're most of the way there, but it's not layers of abstraction so much as a continuum of dependence. I'd say then that all well-factored systems form directed acyclic graphs (plus permitting 2-cycles) in this continuum.
This isn't the only constraint I apply. I use CycleAbstraction? as a design constraint to multiply classes, and the converse constraint, OnceAndOnlyOnce, to reduce them. Between these two constraints is the land of well-factored systems.
In the examples, dependency is used in two senses.
Person USES Dollar.
Dollar INHERITS from Integer.
I think this ambiguity needs to be resolved.
I just picked Ron's three nouns and imagined an arbitrary cycle. The nature of each dependency doesn't make any difference to the conjecture as I understand it. I'd be most interested in any (>2) cyclic dependency that doesn't
nicely resolve by abstracting a class; feel free to vary the dependencies and see whether you can find a beast like that. Don't feel constrained to use the classes here either - any counter-example will shed a lot of light. -- PeterMerel
Perhaps we should look to business systems (and relational database models) for counter-examples. If relationships are commonly navigated in both directions, one could expect a large number of cyclic dependencies. -- JeffGrigg
Relational decompositions frequently include long cycles. So do grammars. There are considerable disconnects between these models and OO decompositions that OO architecture must take pains to resolve. See CrossingChasms
for a PatternLanguage
on translating relations into objects, and vice versa.
I suspect your real question is what about when the problem domain naturally contains cycles? In my experience, they neatly reduce to 2-cycles and classes. 2-cycles are allowed, so no big deal. If anyone can find a particular ThreeLeafedClover?
to discuss as a counter-example here, that'd help us a lot. If not ... well, perhaps there are no ThreeLeafedClover?
What I'm looking for is a compelling 3 or 4 class example of a "natural" cycle in a widely understood problem domain, like Students/Courses/Classes or Customers/Orders/Payments. Then we could see how adding a class to remove the cycle improves the design, or argue about how adding the class does not improve anything. -- JeffGrigg
The VisitorPattern in GoF is a greater than 2 class example of a cycle. Granted, it may be design level and not analysis level, but it is an example from a well published domain: the GoF domain :-). See AcyclicVisitor for an example method of how to remove the cycles.
suggested elsewhere that I apply my apparently above-average reasoning skills to this topic. He was joking (in several ways, I suspect) but I'll play along. I just don't expect it to help much. I can fool some of the people some of the time ...)
I'm not convinced. I'm close to believing that cyclic dependencies are often - perhaps even almost always - indicators of a class lurking in the shadows. Partly as a result of this, in many situations you can turn it around and show how removing a class would result in a new cyclic dependency. But even if that were always
the case, I couldn't buy the title of this page as a true statement. I'm too much of a Platonist for that. -- GlennVanderburg
I'm far from a platonist, but I'm happy enough with Glenn's position. The title of this page is a bit of a StrawMan
; I figure that someone - maybe even the superlative Glenn, given a little more time to think - can knock it off its perch. The way they do will be very interesting to me, so I'll stand pat on wholeheartedly supporting the title until then. If only there was some way to phrase this as an ExtremeProgrammingChallenge
I'm sure it'd be polished off before lunchtime. -- PeterMerel
I think this is the opposite of TightGroupsOfClasses
. I also think the definition of class here is false and very wrong. There are clear cyclic dependencies in normal relationships in the real world. For instance, husband and wife. Husband and wife aren't the same class. Other examples include orchid and oak, river and cloud. -- SunirShah
With respect to "husband/wife", I would factor out man, woman, and wifeof/husbandof; i.e., the relationship between the entities now has a distinct identity. Generally, one can say that if an entity relates to non-owned entities, the relationship has an independent existence. Placing it inside one or other of the participants is to break some logical consistency. -- RichardHenderson
There is an unusually high level of nonsense on this page. I would not normally object in such a free forum, but this page is reachable in two steps from OoDesignPrinciples
, which means that it may be quickly reached by people trying to gain initial understanding of object orientation, which is hard enough already. My comment is not meant to suppress, only to warn people who are looking for wisdom that they are in the wrong place. There is wisdom available in this Wiki, quite a bit of it, but not on this page. Hit your <back> button.
You can detect the level of nonsense by the polite puzzlement of several responders to the original author; I will be less polite. You can also see nonsense in the absurdity of the examples; I will site only the first few; almost all are equally ridiculous:
- Person uses Dollar to buy ledgers
Person does not use
Dollars in the sense of a using
relationship. Perhaps the Ledger has an attribute which is price, which is of type Dollar (though there is probably no reason to have such a type: a simple real number would suffice). Perhaps the transaction of buying the Ledger has an attribute which is price. Perhaps the Person has zero or more Ledgers, or perhaps the Person has zero or more such transactions. But a Person has no sensible relationship to Dollar at all, not even having a price (in this context).
- Dollar inherits from Integer
Why? What does it add to Integer? Again, if anything, a Dollar just has a value, which might be of type integer; this implies no IsA
relationship. (Never mind that its value is probably not an integer).
- Integer uses Person to record themself
Huh? Simply makes no sense at all.
And it only gets worse.
The original author does not understand even the beginning of the fundamentals of object-oriented analysis. He continually makes the most ubiquitous neophyte error, namely, finding all sorts of fancy relationships when the most that is called for is having an attribute, AKA simple containment. Such wholesale ignorance is of course forgivable, and often transitory, but should not be broadcast without caveat under the apparent endorsement of knowledgeable experts.
I think cyclic dependencies should actually be thought of as
an indication that your way of modelling (that is, layering based
on allowed dependencies) is not sufficient for distinguishing between different objects/classes/entities. This means either that you only
have just one entity (this would suggest making a class), or that you need an alternative means for distinguishing and structuring the entities. For example, you could use data flows to distinguish between classes
at the same layer. Once you find a relationship between those
entities in each layer that is acyclic, you can use that
to structure your code. You will obtain a second hierarchy,
which allows you to retain all the good properties of the
system. -- EsaPulkkinen
This approach seems to be a bit counter to dynamic OO language
thinking where you may not know all uses ahead of time. If many classes had a To_Text method, then we don't want to draw a line to all using classes, for example. Besides, defining something based on how or what uses it is ripe with LifeIsaBigMessyGraph