Title: Design Smells and OO Principles
1Design Smells and OO Principles
- Midterm will be assigned this week and due next
week.
2Design Smells
- The Odors of Rotting Software
- 1. Rigidity - The design is hard to change
- 2. Fragility - The design is easy to break
- 3. Immobility - The design is hard to reuse
- 4. Viscosity - It is hard to do the right thing
- 5. Needless Complexity - Overdesign
- 6. Needless Repetition - Mouse abuse
- 7. Opacity - Disorganized expression
3Rigidity
- Changing one part of the system forces changes in
other parts. - The tendency for software to be difficult to
change, even in simple ways. - Symptom Every change causes a cascade of
subsequent changes in dependent modules. - Effect When software behaves this way, managers
fear to allow developers to fix non-critical
problems. This reluctance derives from the fact
that they dont know, with any reliability, when
the developers will be finished.
4Fragility
- Changes in one part cause bugs in unrelated
parts. - The tendency of the software to break in many
places every time it is changed. Often the
breakage occurs in areas that have no conceptual
relationship with the area that was changed. - Symptom Every fix makes it worse, introducing
more problems than are solved. - Effect Every time mangers/ team leaders
authorize a fix, they fear that the software will
break in some unexpected way.
5Immobility
- System components cannot easily be isolated for
reuse. - The inability to reuse software from other
projects or from parts of the same project. - Symptom A developer discovers that he needs a
module that is similar to one that another
developer wrote. But the module in question has
too much baggage that it depends upon. After much
work, the developer discovers that the work and
risk required to separate the desirable parts of
the software from the undesirable parts are too
great to tolerate. - Effect And so the software is simply rewritten
instead of reused.
6Viscosity
- "Doing things right is harder than doing things
wrong." - The tendency of the software/ development
environment to encourage software changes that
are hacks rather than software changes that
preserve original design intent. - Symptom It is easy to do the wrong thing, but
hard to do the right thing. - Effect The software maintainability degenerates
due to hacks, workarounds, shortcuts, temporary
fixes etc.
7Needless Complexity
- Infrastructure or abstraction without immediate,
direct benefit.
8Needless Repetition
- Repeating code that could be unified under a
single abstraction.
9Opacity
- The code is hard to read or understand.
10"Law of Demeter"
- A method of an object should use only the
following kinds of objects - itself
- its parameters
- any objects it creates/instantiates
- its direct component objects
11LoD (2)
- You can't call methods on the fields of other
objects. So anything that looks like
foo.getBar().getBaz().doSomething() is not
allowed. What you can do is add a method to foo
called 'doSomething' which delegates to a method
on its 'bar' field called 'doSomething' which
delegates to its 'baz' field. That doesn't break
the law, because it prevents foo's caller from
knowing about bar and baz, and foo from knowing
about baz. - Preserving encapsulation.
12LoD (3)
- Must use accessor functions to access inherited
data members - "The Rather Good Idea of Demeter" - Robert C.
Martin
13Benefits
- Coupling control
- Reduces data coupling
- Information hiding
- Prevents a method from directly retrieving a
subpart of an object - Information restriction
- Restricts the use of methods that provide
information - Few interfaces
- Restricts the classes that can be used in a
method - Small interfaces
- Restricts the amount of information passed in a
method - Explicit interfaces
- Explicitly states which classes can be used in a
method
14Single Responsibility
- A class has a single responsibility. It meets
that responsibility, the whole responsibility,
and nothing but that responsibility - Cohesion
15SR 2
- A seemingly simple, but actually quite subtle,
principle of class cohesion is the Single
Responsibility Principle - A class should have only one reason to change
- In practice this requires that the class be an
abstraction of only one thing (a neighbourhood of
its own?)
16SR 3
Naïve approach
Separated Responsibilities
Computational Geometry Application
Graphical Application
GUI
Rectangle
draw()
17SR 4
18SR 5
19SR 6
20Open Closed
- Software entities (classes, module, functions,
etc.) should be open for extension, but closed
for modification.
21OC 2
22OC 3
23OC 4
24OC 5
25OC 6
26OC 7
27OC 8
- The Open-Closed principle is at the heart of many
of the claims made for OOD. It is when this
principle is in effect that applications are more
maintainable, reusable and robust. The Liskov
Substitution Principle (A.K.A Design by Contract)
is an important feature of all programs that
conform to the Open-Closed principle. It is only
when derived types are completely substitutable
for their base types that functions which use
those base types can be reused with impunity, and
the derived types can be changed with impunity
28OC 9
- We should write our modules so that they can be
extended, without requiring them to be modified.
In other words, we want to be able to change what
the modules do, without changing the source code
of the modules. - How? Abstraction and Polymorphism
29The open/ closed principle (OCP) Example
30The open/ closed principle (OCP) Example
31The open/ closed principle (OCP) Discussion
- If I need to create a new shape, such as a
Triangle, I must modify the drawShape()'
function. - In a complex application the switch/case
statement above is repeated over and over again
for every kind of operation that can be performed
on a shape . - Worse, every module that contains such a
switch/case statement retains a dependency upon
every possible shape that can be drawn, thus,
whenever one of the shapes is modified in any
way, the modules all need recompilation, and
possibly modification - However, when the majority of modules in an
application conform to the open/closed principle,
then new features can be added to the application
by adding new code rather than by changing
working code. Thus, the working code is not
exposed to breakage.
32Liskov Substitution
- Subtypes must be substitutable for their base
types - In Java inheritance is the mechanism supporting
abstraction and polymorphism - What are the characteristics of best inheritance
hierachy? - What are the traps that cause bad hierachies?
33LS 2
34LS 3
35The Liskov Substitution Principle (LCP)
- A client of a base class should continue to
function properly if a derivative of that base
class is passed to it. - In other words, if some function takes an
argument ot type Policy, then it should be legal
to pass in an instance of Personal Auto Policy to
that provided Personal Auto Policy is directly/
indirectly derived from Policy.
36The Liskov Substitution Principle (LCP) Example
37The Liskov Substitution Principle (LCP) Discussion
- Is Square a Rectangle ? Mathematically yes,
Behaviorally, a Square is not a Rectangle and it
is behavior that software is really all about. - It is only when derived types are completely
substitutable for their base types that functions
which use those base types canbe reused with
impunity, and the derived types can be changed
with impunity. - Violations of LSP are latent violations of OCP.
38Dependency Inversion
- The use of interfaces can be seen as an
application of the Dependency-Inversion
Principle - a.High-level modules should not depend on
low-level modules. Both should depend on
abstractions - b.Abstractions should not depend on details.
Details should depend on abstractions.
39Rules for Dependencies
- Direction
- Detail classes depend on General classes
- Mechanisms depend on Policies
- Less stable depends on more stable
- Concrete depends on abstract
- Depend on Abstractions
- Variable types should be Interfaces, not concrete
classes - No Class should derive from a concrete class
- No method should override a method of its base
class (see Liskov Substitution Principle)
40The Dependency Inversion Principle (DIP)
- Dependency Inversion is the strategy of depending
upon interfaces or abstract functions and
classes, rather than upon concrete functions and
classes. - Every dependency in the design should target an
interface, or an abstract class. No dependency
should target a concrete class.
41The Dependency Inversion Principle (DIP)
ExampleDependency Structure of a Procedural
Architecture
42The Dependency Inversion Principle (DIP)
ExampleDependency Structure of an Object
Oriented Architecture
43The Dependency Inversion Principle (DIP)
Discussion
- One motivation behind the DIP is to prevent you
from depending upon volatile modules. - Typically, Concrete things change a lot, abstract
things change much less frequently. - Abstractions are hinge points, they represent
the places where the design can bend or be
extended, without themselves being modified (OCP) - One of the most common places that designs depend
upon concrete classes is when those designs
create instances. By definition, you cannot
create instances of abstract classes. There is an
elegant solution to this problem named Abstract
Factory
44Interface Segregation
- Interfaces can become fat with too many loosely
related operations. This leads to the Interface
Segregation Principle - Clients should not be forced to depend on
operations that they do not use - If the unused operations change, the client is
still affected
45IS 2
Delegated solution
Timer
Door
Timer
Door
Timed Door
DoorTimerAdapter
Naïve approach
TimeOut( )
46The Release Reuse Equivalency Principle (REP)
- The granule of reuse is the granule of release.
47The Release Reuse Equivalency Principle (REP)
- A reusable element, be it a component, a class,
or a cluster of classes, cannot be reused unless
it is managed by a release system of some kind. - Clients will/ should refuse to reuse an element
unless the author promises to keep track of
version numbers, and maintain old versions for
awhile.Therefore, one criterion for grouping
classes into packages is reuse. - Since packages are the unit of release in JAVA,
they are also the unit of reuse. Therefore
architects would do well to group reusable
classes together into packages.
48The Common Closure Principle (CCP)
- Classes that change together, belong together.
49The Common Closure Principle (CCP)
- The work to manage, test, and release a package
is non-trivial in a large system. The more
packages that change in any given release, the
greater the work to rebuild, test, and deploy the
release. Therefore we would like to minimize the
number of packages that are changed in any given
release cycle of the product. - To achieve this, we group together classes that
we think will change together.
50The Common Reuse Principle (CRP)
- Classes that arent reused together should not be
grouped together.
51The Common Reuse Principle (CRP)
- A dependency upon a package is a dependency upon
everything within the package. When a package
changes, and its release number is bumped, all
clients of that package must verify that they
work with the new package - even if nothing they
used within the package actually changed. - Hence, Classes that arent reused together should
not be grouped together in a package.
52The Package Cohesion Principles (REP/CCP/CRP)
Discussion
- These three cannot simultaneously be satisfied.
- The REP and CRP makes life easy for re-users,
whereas the CCP makes life easier for
maintainers. - The CCP strives to make packages as large as
possible (after all, if all the classes live in
just one package, then only one package will ever
change). The CRP, however, tries to make packages
very small. - Early in a project, architects may set up the
package structure such that CCP dominates for
ease of development and maintenance. Later, as
the architecture stabilizes, the architects may
re-factor the package structure to maximize REP
and CRP for the external re-users.
53The Acyclic Dependencies Principle (ADP)
- The dependencies between packages must not form
cycles.
54The Acyclic Dependencies Principle (ADP)
- Once changes to a package are made, developers
can release the packages to the rest of the
project. Before they can do this release,
however, they must test that the package works.
To do that, they must compile and build it with
all the packages it depends upon. - A single cyclic dependency that gets out of
control can make the dependency list very long. - Hence, someone needs to be watching the package
dependency structure with regularity, and
breaking cycles wherever they appear.
55The Acyclic Dependencies Principle (ADP)
ExampleAcyclic Package Network
56The Acyclic Dependencies Principle (ADP)
ExampleCyclic Package Network
57The Acyclic Dependencies Principle (ADP)
Discussion
- In the acyclic scenario to release the protocol
package, the engineers would have to build it
with the latest release of the comm_error
package, and run their tests. - In the cyclic scenario to release protocol, the
engineers would have to build it with the latest
release of the comm_error, gui, comm, process,
modem, file and run their tests. - Breaking the cycle
- Add new package in between
- Add a new Interface
58The Acyclic Dependencies Principle (ADP)
DiscussionBreaking Cycle by introducing an
Interface
59The Stable Abstractions Principle (SAP)
- Stable packages should be abstract packages.
60The Stable Abstractions Principle (SAP)
- Stability is related to the amount of work
required to make a change. A package with lots of
incoming dependencies is very stable because it
requires a great deal of work to reconcile any
changes with all the dependent packages.
61The Stable Abstractions Principle (SAP) Example
62The Stable Abstractions Principle (SAP) Discussion
- The packages at the top are instable and
flexible. But those at the bottom are very
difficult to change. - The highly stable packages at the bottom of the
dependency network may be very difficult to
change, but according to the OCP they do not have
to be difficult to extend. If the stable packages
at the bottom are also highly abstract, then they
can be easily extended. - It is possible to compose our application from
instable packages that are easy to change, and
stable packages that are easy to extend. - The SAP is just a restatement of the DIP.