[Moved from PrinciplesOfObjectOrientedDesign
There are several TypesOfDependency
, presented here in order of impact to users (from greatest impact to least impact):
- A change in one package causes another package to need editing. Includes all the following types of dependency as well (in other words, if you need to edit, you will also need to compile, link, test, and install that package in order to use it in production).
- A change in one package causes another package to need recompiling (possibly without any changes to source code). Includes all the following types of dependency as well. (The need to compile without source changes is frequently a symptom of the FragileBinaryInterfaceProblem.)
- A change to one package causes another package to need re-linking. Includes all the following types of dependency as well. (Does not apply to all languages.)
- A change in one package causes another package to need re-testing. May not cause re-deployment (if tests successfully pass)
- A change in a package causes another package to need to be re-shipped and/or re-installed. Almost always caused by one of the other kinds of dependency.
The design decisions you make regarding dependencies must be influenced by the kinds of dependency your project is most sensitive to. Changing one interface may activate edit dependencies throughout one or more projects -- that's why interfaces must be very stable. We all care about that. Smalltalkers don't care about compile dependencies while C++ developers on large projects do. Compiling packages into dynamically linked libraries or components is generally enough to remove both link and deployment dependencies. Test dependencies are activated by changes in implementation, but may not have a high impact if full regression testing is infrequent or exceptionally fast. Compile dependencies are physical dependencies that are activated when depended on files change. They may have a low impact if compilation is quick or physical dependencies are limited. (They may have a high
impact if the source is not available.) Finally edit dependencies are activated by non-backwards-compatible changes in interfaces.
Packages are a unit of physical decoupling as well as reuse. We may not care about REP if we are working on a single program with no code ownership. In fact REP would get in the way on a project like that. However, we still need a way to manage dependencies in such a project. JohnLakos
suggests that we should put compilation firewalls at package boundaries (I believe, I don't have his book with me, it's a good idea in any case). We can also erect link barriers at package boundaries by compiling the packages into shared libraries or DLLs. So I think we need a principal or pattern that motivates physical decoupling at package boundaries. Something like the ReuseDecouplingEquivalencePrinciple?
A corollary to that would be that packages have interfaces (the set of classes that they expose) and that those interfaces should be narrow. That is because physical decoupling strategies are work-intensive. If we don't have to do them then writing code is faster and easier. Maybe it could be called InsulatingClassesAsPackageInterfaces?
, the InterfaceClassPrinciple?
, or the InsulatingPackageInterfacePrinciple?
. [more to come] -- PhilGoodwin
Are these types of dependencies? Or are they different binding-times [See http://www.softwareproductlines.com/introduction/binding.html