CSC/ECE 517 Fall 2007/wiki3 8 42

From Expertiza_Wiki
Jump to navigation Jump to search

Introduction

The objective of this document is to introduce a series of design patterns developed by Robert C. Martin (Uncle Bob) that relate to packages.


Robert C. Miller defines the domain of design patterns to be the architecture of systems modules (packages, classes and components) and their interconnections. He sites the need for design patterns being to address the four major reasons that designs rot: rigidity, fragility, immobility and viscosity.


  • The RIGIDITY phenomenon occurs when software managers become reluctant to address minor changes because fixing them inevitably causes other defects due to design flaws, which may end up being more numerous and severe than the original defect addressed. The result is that the software becomes rigid – it does not change as change is needed.
  • The FRAGILITY phenomenon is similar to rigidity in that the software system is so poorly designed one small defect fix or enhancement leads to numerous other defects. In fact, software rigidity is a direct result of software fragility.
  • Software IMMOBILITY occurs when software is either coupled so tightly or encapsulation is so improperly managed that the system, component or module can't be used in either other systems or the current system in other places.
  • VISCOSITY (of design) is a quantitative or estimated measure of how easy it is when making a change to follow good design principles vs. implementing a hack. If it is always easier to implement a hack, viscosity is high.


The following design principles are designed to avoid the pitfalls associated with the above four reasons of unsuccessful software engineering. The first three are concerned with the cohesion of packages – how they are stuck together or constructed. The last three are concerned with the coupling of packages, or the degree to which they rely on each other.


Reuse/Release Equivalence Principle

Principle: The granule of reuse is the granule of release. Only components that are released through a tracking system can be efficiently reused.


Said another way, the degree to which a component is reusable is directly proportional to the degree to which that components various versions are tracked. The idea behind this principle is based on client needs. It should not be expected of a user to upgrade the system whenever the designer implements a change to an external component. Because upgrading is sometimes a slow process with a high potential for difficulties that need to be managed, it is usually a process that is implemented with a great deal of care – and sometimes avoidance. Because of this, authors that don't implement a release system and ensure that older versions of a system are maintained for a period of time don't support reuse in that the demand for their components will decrease greatly.


To illustrate this principle, consider the third-party image processing libraries provided by Leadtools, Inc. These libraries and modules are release controlled and the company provides release support for various versions up to a specified time limit in the past. The reason for this is clear – without a release tracking mechanism users of their libraries would be forced to constantly upgrade as both major and minor changes were implemented. As upgrading can be a painful process, this type of solution would be less than ideal and users of Leadtools libraries would get their support elsewhere because the software would effectively be non-reusable.


Common Closure Principle

Principle: Classes that change together belong together.

This principle is concerned with the design of groupings of classes, or how packages should be organized. Experience has taught that the amount of testing required for a new release of a system is proportional to the number of packages that were changed in that system. If a package is changed, all functionality using that package must be tested regardless of whether individual classes within that package were utilized (this is due to the fact that classes can send messages to each other).


The common closure principle requires a bit for foresight and experience as it suggests that packages should encompass classes that are likely to change together. Doing so minimizes the number of packages that are affected by a change and therefore minimizes and adverse effect on a system as a whole as well as decreases the scope of required testing. In doing so, the effect of changes is minimized.


This type of thinking requires both experience and a good understanding of the environment in which the target system will be employed. When environment knowledge is limited, end users may be a good source as to the most likely changes that will occur to a system in the future (which is to say, if you don't know – ask).


Common Reuse Principle

Principle: Classes that aren't reused together should not be grouped together.

This is another principle concerned with the grouping of classes into packages. The idea behind this principle is that classes which provide the same 'reuse' functionality will more than likely be used under the same set of circumstances. For example, consider an application that maintains information about patients in a Patient object. Certainly other objects will interact with Patient objects – Insurance objects, Employer objects, etc. Because these objects are likely to be used in concert with each other, they should be packaged together.


Consider what would happen if the class PatientCollection – which maintains a list of Patient objects as well as performs various operations on those Patient objects) were placed into Package A, but the Patient, Insurance, and Employer classes were placed in Package B. If this functionality set changes, both Package A and Package B change. As previously stated, when packages are changed, all code which uses those packages must be re-validated. If Package A also contains functionality used elsewhere in the using system, the validation required has been greatly increased.


Acyclic Dependencies Principle

Principle: The dependencies between packages should not form cycles.

When classes are arranged into packages, they should be done so in such a way that if Package A depends on Package B, it should not be the case that Package B depends on Package A (either directly or indirectly). In some cases, current IDEs such as Microsoft's Visual Studio prevent this from occurring at compile time by terminating the compilation process and reporting an error. Other IDEs allow it.


The problem associated with cycles presents itself when packages are modified. When modified, the package itself must be re-validated (obviously), but each package that uses it must be re-validated and so on. Its easy to see that with the introduction of cycles this leads to headaches. It gets complicated further by considering the following situation:


Package A depends on Package B
Package B depends on Package C
Package C depends on Package D
Package D depends on Package A


Not only does the aforementioned cycle present hardship when Package A is modified, but it does so when B, C and D are modified. The illustration above is fairly simple – imaging the complication added when several other packages are added with various dependencies on A, B, C and D. If there were no cycles, the prorogation effects of changing one package would terminate.


Staple Dependencies Principle

Principle: Depend in the direction of stability.


In some sense, stability of a package increases with the number of packages that depend on it. A package of this nature is said to be responsible – that is, other packages depend on it (likewise, a package that depends on nothing is said to be independent). This follows the definition of stability being 'difficulty to change,' and a package that is responsible to a great number of packages is difficult to change because all dependent packages in the scenario need to be re-validated.


Martin presents metrics used for calculating stability. Consider Ca to be the incoming dependencies of a package (referred to as afferent coupling) and Ce to be the number of all outgoing dependencies (referred to as efferent coupling). Then instability (I) is a measure of the efferent divided by the sum of the afferent and the efferent, or:

I = Ce / (Ca + Ce)

...which results in a number between zero and one. If there are no outgoing dependencies and at least one incoming dependency then I is zero and the package is stable. If there are no incoming dependencies and one outgoing then I is one and therefore the package is instable.



According to this design principle, “Depend upon packages whose I metric is lower than yours.” Martin.


Stable Abstraction Principle

Principle:Stable packages should be abstract packages.


The stable abstraction principle is a natural consequence of the Stable Dependencies Principle (above). By definition abstract classes are maximally stable because they can't change; conversely, concrete classes are maximally instable because they can change. It follows that stability in a package is directly proportional to the level of abstraction in the design.


Martin defines metrics for calculating abstractness as the number of abstract classes in a package divided by the total number of classes in a package. Just like instability (I), the result is between zero and one, with zero being a package with no abstract classes and one being a package with only abstract classes. According to the SAP, ranges closer to one are desired.


Two Packages, Each with Seven Classes

In the figure above, Package A has seven classes, two of which are abstract. This yields an abstraction value of 0.286. Conversely, Package B has seven classes, none of which are abstract -- yielding a value of 0.0. In accordance with the matrix for determining abstractness, Package A is more abstract and is therefore more stable.


Does Stability Produce Inflexibility?

No. Remember that instable packages are easy to change whereas stable packages are difficult to change. Designing a stable package does not make it difficult to modify at all. Beyond the scope of this article is the Open Closed Principle which states that modules should be designed to be extended. A stable package is difficult to change but not difficult to extend. Stability does not therefore contradict our maintainability and extensibility requirements of elegant programming – we can still maintain a flexible system.


References

Note that the PDFs provide the best sources of information for these topics. As web pages seem to restate or refer to topics covered by Martin, these are the most thorough sources on the web.