Title: Designing Systems That Evolve Quickly and Cleanly
1Designing Systems That Evolve Quickly and Cleanly
2Evolving the System
- Is the system in a state that allows you to
easily add/modify a feature? - If no, refactor it to allow what you need.
- Add/modify the feature.
- Do this, and everything will be easy -)
3Refactoring
- Refactoring is the practice of changing the
structure of code without changing what it does. - Modern IDEs (most notably IntelliJ IDEA) have
automated much of this. - When a tool cant do a change in a verifiably
correct way, its vital that you have tests to
make sure you didnt just break something!
4Overview
- Defining the Problem
- Code Rot
- Coupling Cohesion
- Technologies
- Test Driven Development
- Interfaces
- Dependency Injection
- Mixins
- Package Management
- Tools
5 6What Is Code Rot?
- Code Rot is where code goes progressively bad
over time. - Once clean designs become a tangled mess. Any
changes are increasingly error-prone. - Makes refactoring a challenge.
7Five Signs of Code Rot
- Rigidity
- Fragility
- Immobility
- Obscurity
- Dispensability
http//www.cincomsmalltalk.com/userblogs/buck/bl
ogView?entry3320476400
8Code Rot Rigidity
- A small change to the design requires
wide-sweeping changes to the code. - Also known as the Local Change, Local Effect
principle.
9Code Rot Fragility
- A small change in one spot causes many things to
break. - The But I just made one little thing in an
unrelated part of the system effect.
10Code Rot Immobility
- Code is difficult to reuse in contexts other than
the original one. - Encourages a lot of reinventing the wheel.
- Virtually impossible to test in isolation.
11Code Rot Obscurity
- The code is difficult to read and understand.
- This is a major cause of maintenance bugs.
- Leads to a lot of redundancy, because if you have
no idea what something is really going to do, you
are unlikely to reuse it.
12Code Rot Dispensability
- The code is redundant or not needed at all.
- Is this class used any more? Maybe. Who knows,
but I'm sure not going to touch it.
13Coupling Cohesion
- Coupling is a measure of the strength of the
connections in a system. - Rigidity, fragility, immobility, dispensability
- Cohesion is a measure of the logical sameness
between parts how specific they are to a
particular task. - Fragility, obscurity, dispensability, immobility
14Tight Coupling
- Tight Coupling means that parts are tightly
connected. Pieces are not interchangeable. - class MyClass
- private OracleDatasource ds
- new OracleDatasource("uri",
- "username", "password")
- private Vector people new Vector()
- // business logic
15Tight Coupling
- Tight Coupling means that parts are tightly
connected. Pieces are not interchangeable. - class MyClass
- private OracleDatasource ds
- new OracleDatasource("uri",
- "username", "password")
- private Vector people new Vector()
- // business logic
Can only use that particular database server.
What if you want to change to use a different
database type?
16Loose Coupling
- Loose Coupling means that parts are loosely
connected. Pieces are easily interchangeable. - class MyClass
- private DataSource ds
- new OracleDatasource("uri", "username",
- "password")
- public void setDataSource(DataSource ds)
- this.ds ds
-
- // business logic
17Loose Coupling
- Loose Coupling means that parts are loosely
connected. Pieces are easily interchangeable. - class MyClass
- private DataSource ds
- new OracleDatasource("uri", "username",
- "password")
- public void setDataSource(DataSource ds)
- this.ds ds
-
- // business logic
You can change what data source is used at any
time. For example, one for testing and one for
production.
DataSource is an interface. So you can use a
different kind of database, mock it out, put a
wrapper around a web service, etc.
18Low Cohesion
- Low Cohesion means that there is little that
pieces have in common. - This tends to make systems
- difficult to understand, because its not clear
what the purpose of a module really is - difficult to maintain, because it is hard to know
if youre working on all the right parts - difficult to reuse a module, because few other
areas will need a random bunch of functionality
19High Cohesion
- High Cohesion means that a module is tightly
focused on a particular task. - This tends to make systems
- easy to understand, because its clear what the
purpose of a module really is - easy to maintain, because it is easy to know if
youre working on all the right parts - easy to reuse a module, because other areas can
use exactly what they need with no extra overhead
20 21Test Driven Development
- Test Driven Development (TDD) is the practice of
using tests to design and verify your code. - Its much easier to fix a bug you did five
minutes ago than five months ago. - A requirement isnt a requirement unless you can
verify (test) that youve met it. - Because writing code that is quickly and easily
testable requires that you make it highly
cohesive and loosely coupled, doing TDD means
that its easier to do things right than to do
them wrong.
22TDD Lifecycle
23Interfaces and Abstract Classes
- An interface is a class definition with no
behavior given. Use of an interface requires
implementing all of the methods of the interface. - An abstract class is a class that has behavior
specified, but parts of the behavior have been
left empty for further extension.
24Why Are Interfaces Vital?
- In Java a class can only be in a single hierarchy
(single inheritance). - You can have a class implement an arbitrary
number of interfaces, allowing a class to
participate in orthogonal functional
specifications. (e.g., It can be both a
Collection and Serializable) - It forces you to focus on a class' contract
rather than how it implements something.
25Dependency Injection
- How do you make sure that a class is not coupled
to something else until it absolutely has to be? - By minimizing assumptions about the environment
and handing it everything it needs at runtime. - This gives you the maximum possible mobility by
giving as close to true plug and play as you
can get. - What this means is that you can take something
like a ShoppingCart and use it in a web server, a
test case, a session bean, a message-driven bean,
a stand-alone application, etc.
26Dependency Injection Example
- public class ShoppingCart
- private Store store
- private User user
- private Collection items new ArrayList()
- public ShoppingCart(Store store, User user)
... - public void addItem(Item item)
items.add(item) - public void removeItem(Item item)
items.remove(item) - public void checkOut()
- store.sell(user, items)
-
27Dependency Injection Containers
- The primary problem with Dependency Injection is
that it can be a hassle to manage making sure all
the dependencies get injected correctly. - There are containers most notably the Spring
Framework that handle this for you while having
a near zero footprint in your code.
28What Is A Mixin?
- A mixin is code that is able to be mixed in
with other code. - Allows you to get the benefits of multiple
inheritance without many of the problems by using
delegation. - For example, you can use Spring to mix in code
for remoting, security, transactions, logging,
etc.
29Using Proxies to Do Mixins pt1
- public interface Lockable
- void lock()
- void unlock()
- boolean locked()
- We would like to be able to make it so that we
can add Lockable capabilities to any class,
including ones that already are a part of a class
hierarchy. - Again, you can add as many interfaces as you
want. - What would be great is to be able to
arbitrarily add interfaces and behavior to
classes.
30Using Proxies to Do Mixins pt2
- public class LockMixin implements Lockable,
InvocationHandler - private boolean locked
- private Object obj
- protected LockMixin(Object obj) this.obj
obj - public void lock() this.locked true
- public void unlock() this.locked false
- public boolean locked() return this.locked
- public static Object createInstance(Object obj)
- Class inters obj.getClass().getInterfaces(
) - Class interfaces new Classinters.length
1 - System.arraycopy(inters, 0, interfaces, 0,
inters.length) - interfacesinters.length Lockable.class
- return Proxy.newProxyInstance(LockMixin.class.
getClassLoader(), interfaces, new
LockMixin(obj)) -
- // ...
31Using Proxies to Do Mixins pt3
- // ...
- public Object invoke(Object proxy, Method
method, Object args) throws Throwable - if (locked() method.getName().equals("add")
) - throw new LockedException()
- if (method.getDeclaringClass().equals(Lockable
.class)) - return method.invoke(this, args)
- return method.invoke(obj, args)
-
-
- List list new ArrayList()
- List lockedList (List)LockMixin.createInstance(l
ist) - lockedList.add("Hi")
- ((Lockable)lockedList).lock()
- lockedList.add("Bye") // throws LockedException
32Packages as Modules
- For anything more than very basic functionality,
we need a larger unit than a class to define a
module - In Java this is typically done with packages.
- JAR files also act as fundamental modular units
(made more useful with systems like Maven and
OSGi)
33Dependencies and Abstractions
- Low-abstraction packages should depend upon
high-abstraction packages. - Allows the high-abstraction packages to be reused
independently. - The high-abstraction packages are then extensible
to an open set of implementations.
34No Cycles
- There should never be a cyclic dependency between
packages - A -gt B -gt A
- This simple rule goes a long way toward
encouraging loose coupling. - It is hard to do, even with practice.
- But it is possible, with the Spring Framework
being a major example.
Bad
35Cyclic Dependencies
- A cyclic dependency is an extremely tight
coupling between modules, limiting your ability
to make changes in the future. - No one does them on purpose, but they emerge
(quickly) over time, and is a major code smell
that the code is rotting. - Cycles between packages make it impossible to
split the packages into separate JARs.
36Conceptual Management Unit
- Modules have a physical nature to them, both in
terms of source management and deployment. - The conceptual nature of the module its logical
cohesion must be maintained. - Both are important!
- Using good tools, like Maven 2 and OSGi, make
source management and deployment considerations
much easier.
37Cyclic Example 1
- We have two classes, employee.Employee and
manager.Manager. - The Employee has a instance of a Manager, and the
Manager has a collection of Employees.
38Cyclic Example 1
We have a cyclic dependency! The employee package
depends on the manager package, and vice versa.
- We have two classes, employee.Employee and
manager.Manager. - The Employee has a instance of a Manager, and the
Manager has a collection of Employees.
39Cyclic Example 1 - Fix
- Move them into the same package.
- If they were in separate packages because of
several support classes, make Employee and
Manager interfaces (high level abstractions) with
implementations (low level abstractions) in
subpackages. - person.Employee, person.Manager,
person.employee.EmployeeImpl, person.employee.Vaca
tionCalculator, etc. - Note that person.employee depends on person but
not the other way around!
40Cyclic Example 2
- person.manager.CompanyCalendar is used by
ManagerImpl. - person.employee.VacationCalculator is used by
EmployeeImpl and uses CompanyCalendar. - person.manager.ManagerImpl needs to do some
vacation calculations as well.
41Cyclic Example 2
Calling VacationCalculator would introduce a
cycle!
- person.manager.CompanyCalendar is used by
ManagerImpl. - person.employee.VacationCalculator is used by
EmployeeImpl and uses CompanyCalendar. - person.manager.ManagerImpl needs to do some
vacation calculations as well.
Don't Repeat Yourself! (DRY) It's almost never a
good idea to cut paste.
42Cyclic Example 2 - Fix
- Move VacationCalculator and CompanyCalendar to a
new package.
person
person.employee
person.manager
person
person.utils
person.employee
person.manager
43 44JDepend
- http//clarkware.com/software/JDepend.html
- Will generate a number of different metrics about
packages, including watching for cycles. - Can be used to generate reports, like
jdepend-maven-plugin - Can be used in an automated environment, such as
JUnit or FIT tests.
45(No Transcript)
46Maven 2
- http//maven.apache.org/
- A complete build system that encourages industry
best practices - Easily manages module dependencies
- Makes development of focused modules easy
- Encourages keeping automated testing a strong
part of development - Easily generates tons and tons of reports,
including unit test results, test coverage, PMD,
JDepend, etc. - and much, much more
47FindBugs
- http//findbugs.sourceforge.net/
- A very nice (and free) static analyzer that will
find an amazing number of bugs and future bugs
for you.
48Q A