'''Context''': * Class Diagrams, RefactoringBrowser(s) and the interface dependencies that result from their use. '''Forces''': * Classes, to be useful, must have a well-defined responsibility for solving a problem identified in the requirements. * Classes whose dependencies form a cycle pose a maintenance-time risk because refactoring one can result in an unpredictable amount of further refactoring, including multiple passes around their cycle. * Any two Classes that depend mutually on each other don't pose very much risk of this, and anyway can't be factored out without splitting their responsibilities across interface lines, which leads to RavioliCode, which is worse. * Many languages, especially C++, don't support much cyclic dependency anyway. * Most libraries and components shouldn't even permit 2-cycles between each others' components (ie. inter, not intra), because it makes it impossible to link/deploy one library/component without also linking/deploying the other. * A cyclic dependency between >2 classes often (possibly always, though that hasn't been proved) hides a useful class. '''Therefore:''' If the cycle has length greater than 2 '''or''' if it links two libraries/components: * Abstract a base class that represents and eliminates the cyclic dependency. * Shift responsibility to eliminate the cyclic dependency. * '''or''' Create an AssociationClass to eliminate the cyclic dependency. The last two are usually useful only when the cycle wasn't actually needed in the problem domain, but just happened in the solution. '''Example:''' ---- '''// With Cycle:''' class Hornhair { // ... private: set dronesM; }; class Drone { // ... private: set tasksM; }; class Task { // ... private: Hornhair * supervisorM; }; ---- '''// Abstract A Base Class:''' class Hornhair: public Manager { // ... private: set dronesM; }; class Drone { // ... private: set tasksM; }; class Task { // ... private: Manager * supervisorM; }; ---- '''// Shift Responsibility:''' class Hornhair { // ... private: set dronesM; }; class Drone { // ... private: set tasksM; private: Hornhair * supervisorM; }; class Task { // ... }; ---- '''// Use AssociationClass:''' class Hornhair { // ... private: Team teamM; }; class Team: public multimap { // ... }; class Drone { // ... }; class Task { // ... }; ---- '''See Also''': * AclassIsNothingButaCyclicDependency, MultipleInheritanceIsNotEvil, ClassUnfolding, LawOfDemeter, AcyclicDependenciesPrinciple. ---- It is [sort of] possible to tell what the prescription of this pattern is. It is not possible for this reader to see '''why''' this pattern is. To exhibit this it would be necessary to show two programs of identical functionality, one the first way, one the second, and to show that the second is more maintainable or more understandable or better on some dimension. -- RonJeffries ---- I wonder when would the second or third tactic be needed if you have the first one (extract-superclass)? I think I'm missing something about the cyclical refactoring that you mentioned. -- MichaelFeathers ---- ''Okay folks, keep your hats on, there's more code coming here. I'm not certain that showing changes communicating around this 3-cycle will actually make my point, but it's certainly a worthwhile exercise if nothing else.'' -- PeterMerel ---- This stuff seems related to what Robert C. Martin calls the DependencyInversionPrinciple. He says that stable things should not depend on unstable things. He makes the unstable more stable by making a more abstract version of it: a new base class. Then both the original dependant and the concrete unstable thing itself depend on the abstraction. The "dependency arrow" from the unstable thing reverses direction. His solution is thus similar to your "Abstract a base class". What about the problems they solve? What I'm struggling with here is why you say it is cycles, specifically, which are bad. I think it's a special case. If the cycle contains a mixture of stable and unstable things, then necessarily at least one of the stable things must be depending on an unstable thing. Otherwise it just seems like an example of the general principle that dependencies are bad and abstractions good. -- DaveHarris ---- CategoryAbstraction