550 likes | 942 Vues
Hayden Melton Computer Science hayden@cs.auckland.ac.nz May 2006. Software Design and Metrics. Overview. Buzzwords for designing software: Loosely-coupled Cohesive Encapsulating Manageable size “Fuzzy terms” – what do they actually mean?
E N D
Hayden Melton Computer Science hayden@cs.auckland.ac.nz May 2006 Software Design and Metrics
Overview • Buzzwords for designing software: • Loosely-coupled • Cohesive • Encapsulating • Manageable size • “Fuzzy terms” – what do they actually mean? • Today I’ll try to be rigorous about one form of coupling
Coupling • Two things are coupled if to changing one also requires changing another [Fowler, Stevens] • “Strength of association of established by a connection from one module to another” [Stevens, Myers, Constantine] • Many forms of coupling – dependency at runtime, compile time dependency, change dependency (CVS data).
Java • Java is a strongly typed language, which means the compiler checks the types of expressions etc before allowing compilation • A Java program can be thought of as a collection of types (i.e. classes, interfaces, enums, primitives) • Can view a (reference) type as a composition of other types
Meaning of Compilation Dependency public class Foo { private A a = new A(); public P m1() {X x = new X(a);K k = x.m();P p = k.m(); return p; } public P2 m2(P3 p3) {P2 p2 = Z.run(a, p3); return p2; }}
A P X K Foo P2 P3 Z Meaning of Compilation Dependency public class Foo { private A a = new A(); public P m1() {X x = new X(a);K k = x.m();P p = k.m(); return p; } public P2 m2(P3 p3) {P2 p2 = Z.run(a, p3); return p2; }}
KEY Source-declared type Externally-declared type Structure of a typical Java Program
KEY Source-declared type Externally-declared type Compilation dependency Structure of a typical Java Program
KEY Source-declared type Externally-declared type Compilation dependency Structure of a typical Java Program
KEY Source-declared type Externally-declared type Compilation dependency Structure of a typical Java Program
Observations on Structure • Externally-declared types can’t depend on source-declared types • The graph is connected • Classes must communicate with each other to be part of the same system • The source-declared subgraph is usually connected • Externally declared types can’t instantiate source declared ones, and they’ve got to be instantiated somewhere
Design Principles • Depend entirely on externally-declared types (if possible) • Proven, well-understood, stable, widely-available • Dependency on externally-declared types less detrimental coupling than dependency on source-declared types • Source types change, unique to application – so not well understood
(a) (b) Two Systems: a comparison • Which is most “loosely-coupled”? • Think in terms of reuse, understanding, testing, …
(a) “Layered” or “Hierarchical” Structure • Reuse • Deploying a type in the context of a new system and having it compile • Testing • Unit testing, Integration testing • Understanding • No good place to start [Lakos], go round the cycle many times [Fowler].
Reuse • Why “verbatim reuse”? • Even the smallest changes to reused code can introduce errors [Stark]. • Version proliferation • Divergence from original, hard to integrate updates [Martin]. • For reuse to be effective the class we’re reusing can’t be tied to a large block of unnecessary code • Increases the number of types to be considered in the new system, and potentially more code that could contain bugs • Really a problem – Azureus (logger, internationalization, torrent parser, concurrency). Eclipse (Internal compiler depends on a few thousand classes c.f. Sun’s Java compiler).
Reuse • Dependency is transitive • If A depends on B, and B depends on C, then A transitively depends on C. • Deploy a source file from one program in the context of another also need all the files it needs to compile. But we also need all the files the others need. We need the transitive closure of its dependency. • Transitively a recurring theme…
(a) (b) Testing • Unit testing – about testing a class “in isolation” • More dependencies it has, potentially more interactions it has harder to test in isolation
(a) (b) Testing • Integration testing – about testing system “in order” • Test one level, find it’s correct. Test the next level find it’s correct, …
(a) (b) Testing • Integration testing – about testing system “in order” • Test one level, find it’s correct. Test the next level find it’s correct, …
(a) (b) Testing • Integration testing – about testing system “in order” • Test one level, find it’s correct. Test the next level find it’s correct, … Where to start? Need to test it all in one go!
Understanding • Similar argument to reuse – in order to understand a class should only (at most) have to understand the classes on which it transitively depends • Worst case for understanding class indicated in red: • A starting point for understanding a system the first time you look at it (as per integration test order)
Understanding • What about polymorphism? To understand Foo, we also have to understand BarImpl yet Foo doesn’t transitively depend on BarImpl! Baz Foo BarImpl IBar
Understanding • public class Baz { void run() { IBar bar = new BarImpl(); Foo foo = new Foo(bar); //... }}public class Foo { private IBar bar; public Foo(IBar bar) { this.bar = bar; } //...makes extensive use of bar in body} Constructor Dependency Injection
Understanding • Argument still holds if we have good unit tests • Should be able to glean all the pre- and post- conditions from the unit tests of Foo. • The key is the type MockBarImpl • public class TestFoo extends TestCase { public void testFoo() { IBar bar = new MockBarImpl(); Foo foo = new Foo(bar); //... }}
Comparing Hierarchical Structures • Hierarchical/layered structures better than their cyclic analogues • What “shape” of hierarchical structure is best? (c) (b) (a)
Comparing Hierarchical Structures • Number of classes coupled to “nothing” (or “standalone”)? • (a) has 1 • (b) has 4 • (c) has 7 • Recall: Reuse, understanding, testing. (c) (b) (a)
Ideal Hierarchical Structure • So we want a “flatter” hierarchical structure rather than a taller one • Classes in “flat” structures tend to have lower transitive dependencies than those in “tall” structures • We also want the “flat” structure to have small transitive dependencies, not like:
a d b c e f g h i Metric - CRSS • Class Reachability Set Size (CRSS) captures the transitive dependencies of each class
Bunch of classes all involved in one, big strongly connected component. Why the big gap in the Histogram? Freq CRSS
Bunch of classes all involved in one, big strongly connected component. Why the big gap in the Histogram? Freq CRSS
Bunch of classes all involved in one, big strongly connected component. Why the big gap in the Histogram? Freq CRSS
Bunch of classes all involved in one, big strongly connected component. Why the big gap in the Histogram? Freq CRSS
Using the Metric to Manage a Project • Empirical evidence I have gathered shows if uncontrolled, dependencies seem to get worse over time! • CRSS values grow • Cycles grow and become intertwined v1
Using the Metric to Manage a Project • Empirical evidence I have gathered shows if uncontrolled, dependencies seem to get worse over time! • CRSS values grow • Cycles grow and become intertwined v1 v2