📄 ood.txt
字号:
| Alarm | +-------+The developers that are working in the 'Transport' category choose towork with older, more stable, versions of Elevator and Conveyor, whichin turn depend upon an old version of Alarm. When Transport wants tomake a release, they simply test their category with the appopriatereleases of the dependent categories, and then release it.However, if Alarm called a function that belonged to 'Control Panel',thus creating a cyclic dependency, then Transport could not workwithout old and stable versions of Elevator and Conveyor, which couldnot work without a stable verion of Alarm, which could not workwithout a stable version of Control Panel, which needs a stableversion of Transport. Since we are trying to make changes toTransport, all of the other categories are suddenly invalidated, andthe entire cycle must be released at the same time.Thus, cycles in the dependency structure interfere with the ability torelease an application in pieces. However there is even more harmthat they can do. Consider again the diagram above. The developersworking in 'Elevators' want to run some unit tests. They must linkwith Alarm. However, if we assume that the same cycle exists, (i.e.Alarm calls a function in Control Panel) then the unit test must linkwith Control Panel, which must link with Revenue, which must link withthe Database. And the database doesn't work.The developers of 'Elevator' are really angry. They don't need thedatabase. They don't want to link with the database. The databasecode is 14 megabytes and takes 45 minutes to link in; so why do theyhave to use it? The 'Elevator' developers go find the developer in'Alarm' who created the cyclic dependency upon 'Control Panel' andtake him out back for a little lesson in software engineering.*** How can the cycle be broken. Simple. Whenever there is a cyclein the dependency graph of class categories, that cycle can be brokenby splitting one of the categories into two. In this case, we couldsplit the 'Control Panel' category into two categories as shown below: +---------------+ --------| Control Panel | / +---------------+ / / \ / +-----------+ +---------+ / | Transport | | Revenue || +-----------+ +---------+| / \ \| +----------+ +----------+ +----------+| | Elevator | | Conveyor | | Database || +----------+ +----------+ +----------+| \ /| +-------+| | Alarm | \ +-------+ \ / +---------------+ | Control Panel | | Utilities | +---------------+The new category 'Control Panel Utilities' contains the function thatAlarm wants to call.-------------------------------------------------------------------- 8. Dependencies between released categories must run in the direction of stability. The dependee must be more stable than the depender.--------------------------------------------------------------------One could view this as a axiom, rather than a principle, since itis impossible for a category to be more stable than the categoriesthat it depends upon. When a category changes it always affects thedependent categories (even if for nothing more than aretest/revalidation). However the principle is meant as a guide todesigners. Never cause a category to depend upon less stablecategories. What is stability? The probable change rate. A category that islikely to undergo frequent changes is instable. A category that willchange infrequently, if at all, is stable.There is an indirect method for measuring stability. It employs theaxiomatic nature of this principle. Stability can be measured as aratio of the couplings to classes outside the category.A category which many other categories depend upon is inherentlystable. The reason is that such a category is difficult to change.Changing it causes all the dependent categories to change. On the other hand, a category which depends on many other categoriesis instable, since it must be changed whenever any of the categoriesit depends upon change.A category which has many dependents, but no dependees is ultimatelystable since it has lots of reason not to change and no reason tochange. (This ignores the categories intrinsic need to change basedupon bugs and feature drift). A category that depends upon many categories but has no dependents isultimately instable since it has no reason not to change, and issubject to all the changes coming from the categories it depends upon.So this notion of stability is positional rather than absolute. Itmeasures stability in terms of a category's position in the dependencyhierarchy. It says nothing about the subjective reasons that acategory might need changing, and focuses only upon the objective,physical reasons that facilitate or constrain changes.To calculate the Instability of a category (I) count the number ofclasses, outside of the category, that depend upon classes within thecategory. Call this number Ca. Now count the number of classesoutside the category that classes within the category depend upon.Call this number Ce. I = Ce / (Ca + Ce). This metric ranges from 0to 1, where 0 is ultimately stable, and 1 is ultimately instable. In a dependency hierarchy, wherever a category with a low I valuedepends upon a category with a high I value, the dependent categorywill be subject to the higher rate of change of the category that itdepends upon. That is, the category with the high I metric acts as acollector for all the changes below it, and funnels those changes upto the category with the low I metric. Said another way, a low I metric indicates that there are manyrelatively many dependents. We don't want these dependents to besubject to high rates of change. Thus, if at all possible, categoriesshould be arranged such that the categories with high I metrics shoulddepend upon the categories with low I metrics.-------------------------------------------------------------------- 9. The more stable a class category is, the more it must consist of abstract classes. A completely stable category should consist of nothing but abstract classes.--------------------------------------------------------------------The stability being referred to in this principle, is again the Imetric which is based upon "positional" stability. That is, a modulethat is in a highly stable position in the dependency graph shouldalso be highly abstract. The justification for this principle is based upon the idea thatexecutable code (The implementations of methods) changes more oftenthan the interfaces between modules. Therefore interfaces have moreintrinsic stability than executable code. In C++, it is more likelythat you will change a .cc file than a .h file.In some languages, the division between implementation and interfaceis very clear cut. But in others (like C++) it is blurred. Thestability of the .h file is not as great as it could be becauseprivate functions and private variables are likely to need changing asthe implementation evolves. In such languages, the maximum stabilitycomes from pure interfaces. A class that contains pure interfaces isan abstract class.Thus, we can measure the abstraction of a category by computing theratio of abstract classes to total classes: A = (# abstract classes) / (# of classes).For example, if a class category contains 10 classes, of which 6 areabstract then A = .6. A will always range from [0,1].Principle 9 says that categories that are placed into positions ofstability ought to be abstract. The reason for this that the higherthe abstraction of the category, the higher its intrinsic stability.And since intrinsic stability is limited by positional stability,intrinsically stable modules should also have positional stability.Consider a category with very low abstraction. Such a category hasmostly concrete classes in it. If it is put in a place of stability,then many other categories will depend upon it. And thus, it will bedifficult to change its implementation. On the other hand, consider acategory with very high abstraction. Such a category consists mostlyof abstract classes. If we put this category into a position of verylow stability, then very few other categories will depend upon it.And then, of what use is the abstract interface?But there is another reason to put abstractions in positions ofstability. And that reason goes back to principle #1; the open closedprinciple. We want concrete categories to depend upon abstractcategories so that the derivatives of those abstract categories can becontrolled by the concrete categories. This is reuse. The concretecategories can be reused with different derivatives of the abstractcategories. Also, when the implementations of those derivativecategories change, the abstract category is not affected, and so theconcrete categories that depend upon it are also unaffected. Thus,the concrete categories are open to be extended, but do not need to bemodified in order to achieve that extension.The open closed principle leads us to the realization that we do notwant all of our categories to be stable. Stability is inflexibility.A stable category is difficult to change. And we do not want ourapplications to be difficult to change. But the open closed principleallows that a module that is highly stable (closed for modification)can also be open for extension. Yet this can only happen if theextension takes place in a derivative of the stable abstraction.Thus, in the ideal world our models would consist of two kinds ofcategories. Completely abstract and stable categories that aredepended upon by completely concrete and instable categories.We are now in a position to define the relationship between stability(I) and abstractness (A). We can create a graph with A on thevertical axis and I on the horizontal axis. If we plot the two "good"kinds of categories on this graph, we will find the categories thatare maximally stable and abstract at the upper left at (0,1). Thecategories that are maximally instable and concrete are at the lowerright at (1,0).But not all categories can fall into one of these two positions.Categories have degrees of abstraction and stability. For example, itis very common that one abstract class derives from another abstractclass. The derivative is an abstraction that has a dependency. Thus,though it is maximally abstract, it will not be maximally stable. Itsdependency will decrease its stability.Consider a category with A=0 and I=0. This is a highly stable andconcrete category. Such a category is not desirable because it isrigid. It cannot be extended because it is not abstract. And it isvery difficult to change because of its stability.Consider a category with A=1 and I=1. This category is alsoundesirable (perhaps impossible) because it is maximally abstract andyet has no dependents. It, too, is rigid because the abstractions areimpossible to extend.But what about a category with A=.5 and I=.5? This category ispartially extensible because it is partially abstract. Moreover, itis partially stable so that the extensions are not subject to maximalinstability. Such a category seems "balanced". Its stability is inbalance with its abstractness.Consider again the A-I graph (below). We can draw a line from (0,1)to (1,0). This line represents categories whose abstractness is"balanced" with stability. Because of its similarity to a graph usedin astronomy, I call this line the "Main Sequence". | | 1= (0,1) |\ | \ Abstractness| \ The | \ Main | \ Sequence | \ | \ | \ (1,0) +--------:-- 1 InstabilityA category that sits on the main sequence is not "too abstract" forits stability, nor is "too instable" for its abstractness. It has the"right" number of concrete and abstract classes in proportion to itspositional stability. Clearly, the most desirable positions for acategory to hold are at one of the two endpoints of the main sequence.However, in my experience only about half the categories in a projectcan have such ideal characteristics. Those other categories have thebest characteristics if they are on or close to the main sequence.This leads us to another metric. If it is desirable for categoriesto be on or close to the main sequence, we can create a metric whichmeasures how far away a category is from this ideal.D : Distance : |(A+I-1)/root2| : The perpendicular distance of a category from the main sequence. This metric ranges from [0,~0.707]. (One can normalize this metric to range between [0,1] by using the simpler form |(A+I-1)|. I call this metric D').Given this metric, a design can be analyzed for its overallconformance to the main sequence. The D metric for each category canbe calculated. Any category that has a D value that is not near zerocan be reexamined and restructured.Statistical analysis of a design is also possible. One can calculatethe mean and variance of all the D metrics within a design. One wouldexpect a conformant design to have a mean and variance which wereclose to zero. The variance can be used to establish "control limits"which can identify categories that are "exceptional" in comparison toall the others.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -