Date post: | 09-May-2015 |
Category: |
Education |
Upload: | michael-stal |
View: | 1,251 times |
Download: | 0 times |
Dr. Michael Stal: Architecture Refactoring
1
© Dr. Michael Stal, 2010
Architecture Refactoring –Motivation, Approach and PatternsMotivation, Approach and Patterns
accu 2010
Dr. Michael Stal
Panta rhei
There is nothing permanent except change
[Heraclitus, 535–475 BC]
Page 2
Dr. Michael Stal: Architecture Refactoring
2
© Dr. Michael Stal, 2010
Refactoring
Learning objectives
Understand about design erosion and how to avoid it
Learn about the principles of refactoring
Know about activities and best practices necessary for refactoring
Understand how reengineering and rewriting differ from refactoring
Page 3
Motivation and foundation
Agenda
Refactoring
Refactoring
Reengineering
Rewriting
Page 4
Comparison
Summary
Dr. Michael Stal: Architecture Refactoring
3
© Dr. Michael Stal, 2010
Design erosion is the root of all evil
A BackpackBackpack
ABackpack
Detached Extensions In the lifecycle of a software system changesare the rule and not the exception
AComponent
SomeoneElse's Comp
AnotherComponent
The FifthElement
AnotherBackpack
p
New requirements or increments imply modifications or extensions
Engineers must adopt their solutions tonew technologies
Changes in business force changes in IT
Bug fixes require patches or local corrections
Unsystematic approaches ("workarounds")
Page 5
DBAccess Layer
Yet AnotherComponent
Component42
Spaghettidesign
DB accessshortcut
Unsystematic approaches ( workarounds ) cure the symptom but not the problem
After applying several workarounds, software systems often suffer from design erosion
Such systems are doomed to fail as work-arounds have a negative impact on opera-tional and developmental properties
Refactoring is part of the architecture design process
Refactoring is integrated into the iterative-incremental architecture design process:
It improves the structure
It t i k
Refine & AssessArchitecture
Refactor Architecture
Feedback Loop
Page 6
It supports a risk-, requirements- and test-driven approach
Complete?
yesnoExecutable Increment
Dr. Michael Stal: Architecture Refactoring
4
© Dr. Michael Stal, 2010
Yes, but was is it?
Note: external interfaces i h d!
"Code refactoring is the processof changing a software system
remain unchanged!g g y
in such a way that it does not alter the external behavior of the code yet improves its internal structure" [Martin Fowler]
Put more generally: Refactoringis the process of changing asoftware system or process in
Page 7
software system or process insuch a way that it does not alterthe external behavior, yetimproves its internal structure
Why should we refactor?
Architecture refactoring needs to achieve quality improvement in terms of
structural as well as
non-functional qualities
Structural quality indicators include:
Economy
Visibility
Page 8
Spacing
Symmetry
Emergence
Consequently, the goal of architecture refactoring is to achieve or improve such qualities
Dr. Michael Stal: Architecture Refactoring
5
© Dr. Michael Stal, 2010
Motivation and foundation
Agenda
Refactoring
Refactoring
Reengineering
Rewriting
Page 9
Comparison
Summary
Code refactoring
According to Martin Fowler, code refactoring is
Reasons to use refactoring Design improvement and
… the process of changing a software system in such a way that it does not alter the external behavior of the code yet improves its internal structure
… a disciplined way to clean up code that minimizes the chances of introducing bugs
maintenance Better readability Bugs It is the third step in TDD
The Rules of Three Refactor before adding new
functionality, e.g., when t t t i l
Page 10
structure prevents simple additions
Refactor when fixing bugs Refactor after code reviews
toapply improvements
Dr. Michael Stal: Architecture Refactoring
6
© Dr. Michael Stal, 2010
Code smells
Kent Beck's grandmother's saying: y g"If it stinks, change it"
Thus, identify bad smells such as
Code that is duplicated
Methods that span several dozen lines
Subclasses introducing the
Page 11
Subclasses introducing the same method
Usage of temporary variables
Usage of switch statements
Introduction of a "middleman"
Code Refactoring Example: Extract Method
Make code fragment a method of its own
void printFormatted(string text) {
System.out.println("Copyright (c) 2008, Siemens AG");
System.out.println("Author: Michael Stal");
printRest(text);
}
void printFormatted(string text) {
i tH d ()
Page 12
printHeader();
printRest(text);
}
printHeader() {
System.out.println("Copyright (c) 2008, Siemens AG");
System.out.println("Author: Michael Stal");
}
Dr. Michael Stal: Architecture Refactoring
7
© Dr. Michael Stal, 2010
Refactoring to patterns
Refactoring to patterns was introduced by Joshua Kerievsky
Kerievsky's book focuseson design patterns
y y
General idea: Patterns might give guidance how to refactor on the architectural level
Replace your proprietary solution with a pattern that solves the same problem
Page 13
on design patterns
However, we could alsoapply the same principleto architecture patterns
In the latter case, we'll obtainarchitecture refactorings
Introduce symmetry by making sure the same problem is always solved using the same pattern / solution
This represents a precursor of software architecture refactoring
Replace hard-coded notifications with Observer [Joshua Kerievsky]
Do it yourself Observer Pattern
Observer
update
ConcreteObserver
updatedoSomething
Subject
attachdetachnotifysetDatagetData
stateobserverList
*EventSink
updatedoSomething
Subject
notifysetDatagetData
stateobserverList
*
Dynamic Wiring
Page 14
s->getData()
state = X;
notify();
for all observersin observerList do
update();
Subject contains hardwired list of interested components
Dr. Michael Stal: Architecture Refactoring
8
© Dr. Michael Stal, 2010
Architecture refactoring
Architecture smells Duplicate design artifacts
Architecture refactoring is about the semantic-preserving t f ti f ft
Duplicate design artifacts Unclear roles of entities Inexpressive or complex
architecture Everything centralized Home-grown solutions instead of
best practices Over-generic design Asymmetric structure or behavior Dependency cycles
transformation of a software design
It changes structure but not behavior
It applies to architecture-relevant design artifacts such as UML diagrams, models, DSL expressions, aspects
Its goal is to improve quality. You
Page 15
Dependency cycles Design violations (such as relaxed
instead of strict layering) Inadequate partitioning of
functionality Unnecessary dependencies Missing orthogonality
got a "smell"? Use an architecture refactoringpattern to solve it!
Remove unnecessary abstractions (1)
*
AbstractStorage
TransportWay
* AbstractStrategyA true story: In
*g
CompositeStorage
BinDoorDumpEquip-ment
y
*ConcreteStrategy
gy
*
Cart Belt
A true story: In this example architects introduced Transport Way as an additional abstraction. But can't we consider transport ways as just as another
Page 16
ConcreteStrategy
*
AbstractStorage
CompositeStorage
BinDoorDump
AbstractStrategy
Equipment
*
kind of storage? As a consequence the unnecessary abstraction was removed, leading to a simpler and cleaner design.
Dr. Michael Stal: Architecture Refactoring
9
© Dr. Michael Stal, 2010
Remove unnecessary abstractions (2)
Context Eliminating unnecessary design abstractions g y g
Problem Minimalism is an important goal of software architecture, because
minimalism increases simplicity and expressiveness If the software architecture comprises abstractions that could also be
considered abstractions derived from other abstractions, then it is better to remove these abstractions
General solution idea Determine whether abstractions / design artifacts exist that could also
Page 17
be derived from other abstractions If this is the case, remove superfluous abstractions and derive
dependent from other existing abstractions Caveat Don't generalize too much (such as introducing one single hierarchy
level: "All classes are directly derived from Object")
Break dependency cycles (1)
GetState()
SetState {
GetState()
SetState {In this example,
Sensor
SetState {
...
M.GetDate()
}
X BreakCycle
Sensor
SetState {
...
D.GetDate()
}
Date
GetDate()
In this example, the monitor invokes the state getter / setter methods but also provides GetDate()to the sensor, lea-ding to a simple dependency cycle. Providing this me-
Page 18
Monitor
GetDate()
Draw() {
... S.GetState();
}
Monitor
Draw() {
... S.GetState();
}
thod to monitors was a bad design decision, anyway. Introducing a separate date object solves theproblem.
Dr. Michael Stal: Architecture Refactoring
10
© Dr. Michael Stal, 2010
Break dependency cycles (2)
Context
Dependencies between subsystems Dependencies between subsystems
Problem
Your system reveals at least one dependency cycle between subsystems
Subsystem A may either depend directly or indirectly on subsystem B(e.g., A depends on C which depends on B) which is why we always needto consider the transitive hull
Page 19
Dependency cycles make systems less maintainable, changeable, reusable, testable, understandable
Thus, dependency hierarchies should form DAGs (directed acyclic graphs)
General solution idea
Get rid of the dependency cycle by removing one of the dependencies
Merge Subsystems (1)
Example: While tight coupling between subsystems is bad, high cohesion within subsystems is good. Thus, merge tightly coupled subsystems to form a highly cohesive subsystem .
Sensor
Sensor + Utilities
Page 20
Utilities
Special variant: Merge a layer in a layered system
Dr. Michael Stal: Architecture Refactoring
11
© Dr. Michael Stal, 2010
Merge Subsystems (2)
Context Coupling between subsystems
Problem Between two subsystems in a software architecture the degree of
coupling should be rather loose Within a subsystem the number of interdependencies (cohesion)
should be high If the coupling is too tight, then the many interdependencies between
these two subsystems decrease qualities such as Changeability Performance (maybe)
Page 21
Reusability of one of the subsystems General solution idea Tight coupling between subsystems implies that the two subsystems
in fact implement one subsystem Either merge both subsystems to form one, or Move functionality from one subsystem to the other
Split Subsystems (1)
Example: When analyzing interdependencies between entities in a subsystem, two(or more) sets can be determined. Within these sets there is high cohesion; betweenthese sets there is only low cohesion. Thus, split the subsystem into two (or more) parts.
Component Container
LifecycleManagement
Persistence Event Handling
High Cohesion
ComponentContainer
parts.
Page 22
Communication
Communi-cation
Special variant: Split layer in a layered system
Dr. Michael Stal: Architecture Refactoring
12
© Dr. Michael Stal, 2010
Split Subsystems (2)
Context Cohesion within a subsystem
Problem Within a subsystem the interdependencies (cohesion) should be high Between two subsystems in a software architecture, the degree of
coupling should be rather loose If the cohesion between some parts is loose, then some design
decisions seem to be questionable It is recommendable to change this to obtain better modularization and
understandability Another potential problem are subsystems/components with too many
Page 23
responsibilities General solution idea Loose cohesion within a subsystem implies that the functionality can
be split into multiple subsystems Thus, determine areas with high cohesion in a subsystem. All those
areas with low cohesion are candidates for becoming subsystems of their own
Move Entities (1)
Example
Game
getPlayerHistory(p)
x y z
Game
getPlayerHistory()
x y z
Page 24
Player Player
getPlayerHistory()
Dr. Michael Stal: Architecture Refactoring
13
© Dr. Michael Stal, 2010
Move Entities (2)
Context
Moving entities between subsystems Moving entities between subsystems
Problem
This can be considered a generalization of the Split Subsystem refactoring
In a subsystem an entity is defined that fits better semantically in another subsystem to which it has high coupling
General solution idea
Page 25
Again, we should consider cohesion and coupling
If entity e reveals low or zero cohesion to the rest of its subsystem A, but tight coupling to subsystem B, then move e from A to B
Note: This may also help with breaking dependency cycles between subsystems
Move Entities (3)
Some additional issues
In many cases we can't apply the refactoring to single atomic entities In many cases, we can t apply the refactoring to single atomic entities such as a class, method, constant, interface
It is more likely that there will be clusters of atomic entities that together build a complex entity
In the example, it is very likely that there will be classes and interfaces related to the method to be moved
Thus, always consider semantically related entities with high internal
Page 26
y y gcohesion but less cohesion to the rest of their subsystem. If some of them show tight coupling to another subsystem, move the whole group
Moving may not always be a simple operation, but imply several smaller grained transformations (see example)
Dr. Michael Stal: Architecture Refactoring
14
© Dr. Michael Stal, 2010
Reduce Dependencies with Facades (1)
Rental CarBooking
1.3
Example
ClientHotel
Booking
g
FlightBooking1.1
1.2
Rental CarB ki
2.3
Page 27
FacadeTravel
Booking
HotelBooking
Booking
FlightBooking2.1
2.2Client
1
Reduce Dependencies with Facades (2)
Context Client implements workflows that mostly access other components
Problem If a client needs to access different external components for each workflow, it
becomes dependent on details such as the set of available components As soon as the configuration and / or interfaces of these components change,
there will be a direct impact on the client General solution idea Introduce a Facade component that acts as the client's gateway into the set of
required components Implement workflow methods within the facade that represent the different
Page 28
p pworkflows
The facade methods take over the responsibility for accessing the set of required components on behalf of the client, thus decoupling the client from the components
If components are remote, performance is also improved (Session Facade)
Dr. Michael Stal: Architecture Refactoring
15
© Dr. Michael Stal, 2010
Substitute Mediation with Adaptation (1)
Example
I_haveno clue sub 1
Adapter Adapter Adapter
Adapter Adapter Adapter
Integration Layer
A B C
no_clue_sub_1
I_haveno_clue_sub2
I_haveno_clue_sub_3
Do_Everything_Class
a.k.a. Mediator
Page 29
D E FI_have
no_clue_sub_4I_have
no_clue_sub_5
I_haveno_clue_sub_6
Substitute Mediation with Adaptation (2)
Context Centralized system using a mediation level between peers
Problem When interaction between different peers is complex, a mediator
may be the right solution However, extensive use of mediator may reduce scalability If mediation was just applied to build an extremely generic solution,
design erosion is often the price How can we improve this situation?
General solution idea Introduce adapters to uniformly plug in peers
Page 30
Introduce adapters to uniformly plug in peers But make interaction explicit, i.e. subsystems themselves are in
charge to interact with the appropriate peers using an integration layer
Known use: Enterprise Application Integration (Hub and Spoke) => Enterprise Service Bus
Dr. Michael Stal: Architecture Refactoring
16
© Dr. Michael Stal, 2010
Add Uniform Support for Runtime Aspects (1)
Subsys UI Subsys UI
Subsys DB
Subsys ATM
IUIAdmin
IDBAdmin
Subsys DB
Subsys ATM
IAdmin
IAdmin
Page 31
IATMAdmin IAdmin
ManagementConsole
Add Uniform Support for Runtime Aspects (2)
Context Software architecture that must support runtime aspects such as
f flifecycle management, management, configuration Problem Each non-trivial software architecture consists of multiple
components, each of which supports common aspects If every component implements its own interfaces for runtime aspects,
the overall system will lack simplicity and expressiveness Is there a way to provide a uniform approach to support those kinds of
runtime aspects? General solution idea
Page 32
For each aspect, define a common interface (e.g., an interface for runtime configuration)
In each component supporting the aspect, provide this common interface (maybe using Extension Interfaces to prevent bloating)
To achieve orthogonality, provide one runtime component that is in charge of accessing these common interfaces (e.g., a management console)
Dr. Michael Stal: Architecture Refactoring
17
© Dr. Michael Stal, 2010
Add Configuration Subsystem (1)
Example
Subsystem UI
Subsystem DB
IConfig2
IConfig1
Actor
Subsystem UI
Subsystem DB
IConfig
IConfig
ConfigurationSubsystem
Actor
Configuration
Page 33
Subsystem ATM
IConfig3
Subsystem ATM
IConfig
Use patterns such as Component Configurator for this purpose
Add Configuration Subsystem (2)
Context A system with many configurable variabilities
Problem If a system contains a lot of configuration options, then configuration
itself is often tedious and error-prone This holds in particular when the configuration options are (partially)
related to each other How can we refactor the software system so that configuration is
simplified and (partially) automated? General solution idea Instead of providing dozens of configuration options to external actors,
Page 34
we introduce a configuration subsystem that takes a declarative configuration (from a file or a wizard)
The configuration subsystem reacts to configuration change events, reads the passed configuration, and then accesses the configuration interfaces of configurable subsystems
Ideally, all subsystems provide the same generic configuration interface
Dr. Michael Stal: Architecture Refactoring
18
© Dr. Michael Stal, 2010
Where to obtain architecture refactorings?
A whole catalog of architecture refactorings is provided as a starting
i t
1. Rename Entities
2. Remove Duplicates
3. Introduce Abstraction Hierarchies
4. Remove Unncessary Abstractions
5. Substitute Mediation with Adaptation
6. Break Dependency Cycles
7. Inject Dependencies
8. Insert Transparency Layer
9. Reduce Dependencies with Facades
17. Enforce Contract18. Provide Extension Interfaces19. Substitute Inheritance with Delegation20. Provide Interoperability Layers21. Aspectify22. Integrate DSLs23. Add Uniform Support to Runtime Aspects24. Add Configuration Subsystem25. Introduce the Open/Close Principle26. Optimize with Caching27. Replace Singleton
point
Page 35
p
10. Merge Subsystems
11. Split Subsystems
12. Enforce Strict Layering
13. Move Entities
14. Add Strategies
15. Enforce Symmetry
16. Extract Interface
27. Replace Singleton28. Separate Synchronous and Asynchronous
Processing29. Replace Remote Methods with Messages30. Add Object Manager31. Change Unidirectional Association to Bidirectional
Checking correctness
To check the correctness of refactorings, we use the test-driven approach that we introduced.
Available options:
Formal approach: Prove semantics and correctness of program transformation
Implementation approach: Leverage unit and regression tests to verify that the resulting implementation still meets the specification
Architect re anal sis Check the res lting
Page 36
Architecture analysis: Check the resulting software architecture for its equivalence with the initial architecture (consider requirements) – see also CQM methods and tools
Use at least the latter two methods to ensure quality
Dr. Michael Stal: Architecture Refactoring
19
© Dr. Michael Stal, 2010
Refactoring – responsibilities and communication
The process of refactoring requires communication with testers and developers
Detailed project planning
Testability
SW Design
Requirements Engineer Review system
architecture documents
Product Lifecycle Manager
Control product development across lifecycles
SW Project Manager
Define detailed project plans
Assign resources
Head of R&D Control SW
Page 37
y
Software Developer Identify code
smells Apply code
refactorings
Software Architect Analyze architecture smells Identify appropriate software
architecture refactorings e.g., using refactoring catalogs
Apply software architecture refactorings
Verify correctness of refactorings Be involved in design reviews Leverage CQM tools
Control SW process
Act as escalation point
Test Manager Review product architecture Refine master test plan Set up test strategy for integration and
system test Define test environment Define verification and validation
procedure
Application of architecture refactorings
The usage of a concrete application refactoring should never happen in and h t ti I t dad-hoc, unsystematic way. Instead,
the architect's role is to Check the applicability of refactorings Would the refactoring affect parts that
need to remain unchanged, such as integration of third-party APIs? Could the refactoring impact require-
ments in a negative way? Define the order of refactoring
Page 38
Define the order of refactoring Strategic before tactical aspects Requirement priorities drive order!
Apply the refactorings Ensure the quality of the software
architecture after the refactoring (in conjunction with the test manager)
Dr. Michael Stal: Architecture Refactoring
20
© Dr. Michael Stal, 2010
Obstacles to refactoring (1)
Organization / management Considering improvement by refactoring
as less important than providing new p p gfeatures
Ignorance of refactoring necessity typically causes design erosion
“Organization drives architecture” problem
Process support No steps / activities defined in process
for architecture refactoring: Refactoring h ld b dd d li itl i th
Page 39
should be addressed explicitly in the process; responsibilities must be assigned to different roles
Refactorings are not checked for correctness, test manager not involved: Architects should work hand in hand with test manager and leverage means such as tests, architecture reviews
Obstacles to refactoring (2)
Technologies and tools Unavailability of tools: Refactoring must
be done manually, which can be tedious y,and error-prone
Unavailability of refactoring catalog: It is important to collect refactorings and document them
Applicability Refactoring used instead of
reengineering, and vice versa Wrong order of refactorings: Should be
d t i d b i t i it d
Page 40
determined by requirements priority and relevance
Dr. Michael Stal: Architecture Refactoring
21
© Dr. Michael Stal, 2010
Motivation and foundation
Agenda
Refactoring
Refactoring
Reengineering
Rewriting
Page 41
Comparison
Summary
Reengineering – how it differs from refactoring
Scope: Reengineering always affects the entire system;
f t i h t i llrefactoring has typically (many) local effects
Process: Reengineering follows a disassembly / reassembly approach; refactoring is a behavior-preserving, structure transforming process
Result: Reengineering can
Page 42
create a whole new system– with different structure, behavior, and functionality; refactoring improves the structure of an existing system – leaving its behavior and functionality unchanged
Dr. Michael Stal: Architecture Refactoring
22
© Dr. Michael Stal, 2010
Reengineering – when and how to use it
Use reengineering when
The system's documentation
Process Phase I: Reverse engineering
Reverse Forward
The system s documentationis missing or obsolete
The team has only limited understanding of the system, its architecture, and implementation
A bug fix in one place causes bugs in other places
g g Analysis / recovery: determine
existing architecture (consider using CQM) SWOT analysis Decisions: what to keep, what
to change or throw away Phase II: Forward engineering
Page 43
Reverseengineering
Forwardengineering
Code
DesignPick
W orkpiece
LogAlarms
TelegramForwarder
TelegramReceiver
TelegramConverter
SetPointCalculation
Command
LoggingStrategy
CommandProcessor
Logger
The network
creates
executes
applies passescommands to
passes telegrams topasses telegrams to
ConferenceOrganizer
usesConference
Manager
ConferenceParticipant
Conference
ConferenceSession
organizes
manages
Scheduler
uses
has*
*
Documents
usesMedia
Manager
*
participates
Requirements
causes bugs in other places
New system-level require-ments and functions cannot be addressed or integrated appropriately
Motivation and foundation
Agenda
Refactoring
Refactoring
Reengineering
Rewriting
Page 44
Comparison
Summary
Dr. Michael Stal: Architecture Refactoring
23
© Dr. Michael Stal, 2010
Rewriting in a Nutshell
Rewriting is a radical and fresh restart: existing design and code is trashed and replaced by a whole new design and implementation. Depending on focus:Depending on focus: Improves structure regarding:
Simplicity, visibility, spacing, symmetry, emergence
Maintainability, readability, extensibility
Bug fixing
Provides new functionality
Improves its operational qualities
Page 45Page 45
p p q
Improves design and code stability
As a consequence, rewriting addresses all types of software quality: functional, operational, and the various developmental qualities.
Learning from failure
Failure and understanding failure is a keyfactor for successful design!
[Henry Petroski]
Page 46
Before you’re going to rewrite, check what went wrong in the project that developed the previous application
Dr. Michael Stal: Architecture Refactoring
24
© Dr. Michael Stal, 2010
Motivation and foundation
Agenda
Refactoring
Refactoring
Reengineering
Rewriting
Page 47
Comparison
Summary
Refactoring, reengineering, and rewriting comparison (1)
Refactoring, reengineering, and rewriting are complementary approaches to sustain architecture and code quality
Start with refactoring – it is cheap and (mostly) under the radar
Consider reengineering when refactoring does not help – but it is expensive
Consider rewriting when reengineering does not help – but it is expensive and often risky
Reverseengineering
Forwardengineering
Requirements
LoggingStrategy
ConcreteLoggingStrategy
ClientCommandProcessor
StrategyCommandProcessor
Page 48Page 48
Code
DesignPick
Workpiece
LogAlarms
TelegramForwarder
TelegramReceiver
TelegramConverter
SetPointCalculation
Command
LoggingStrategy
CommandProcessor
Logger
The network
creates
executes
applies passescommands to
passes telegrams topasses telegrams to
ConferenceOrganizer
usesConference
Manager
ConferenceParticipant
Conference
ConferenceSession
organizesmanages
Scheduleruses
has*
*
Documents
usesMediaManager
*
participates
q
*
Strategy
ConcreteCommand
Command
CompositeCommand
Memento
Application
Memento
Command &Composite
Dr. Michael Stal: Architecture Refactoring
25
© Dr. Michael Stal, 2010
Refactoring, reengineering, and rewriting comparison (2)
Refactoring Reengineering Rewriting
Scope Many local effects Systemic effect Systemic or local effect
Process Structure transforming Disassembly / reassambly ReplacementProcess Structure transforming
Behavior / semantics preserving
Disassembly / reassambly Replacement
Results Improved structure
Identical behavior
New system New system or new component
Improved qualities
Developmental Functional
Operational
Developmental
Functional
Operational
Developmental
Drivers Complicated design / code evolution
Wh fi i b
Refactoring is insufficient
Bug fixes cause rippling effect
Refactoring and reengineering are insufficient or inappropriate
Page 49Page 49
When fixing bugs
When design and code smell bad
New functional and operational requirements
Changed business case
or inappropriate
Unstable code and design
New functional and operational requirements
Changed business case
When
Part of daily work
At the end of each iteration
Dedicated refactoring iterations in response to reviews
It is the 3rd step of TDD
Requires a dedicated project Requires dedicated effort or a dedicated project, depending on scope
Motivation and foundation
Agenda
Refactoring
Refactoring
Reengineering
Rewriting
Page 50
Comparison
Summary
Dr. Michael Stal: Architecture Refactoring
26
© Dr. Michael Stal, 2010
What we learned
Refactoring changes artifacts without changing external behavior. It helps with quality improvement
d hand necessary changes In contrast, reengineering is a complete redesign of
a complete architecture and typically changes external behavior
If reengineering is not appropriate, it is often the best alternative to rewrite a system or its parts
All methods are essential. Use the right one for the right purpose
Testing and architecture introspections are i t t h f t i i i
Page 51
important when refactoring, reengineering, rewriting
You as a software architect are responsible to Detect architecture smells Find and apply appropriate refactorings Perform QA of refactoring activities
A departing thought
Each problem that I solved became a rule which served afterwards to solve other problems.
[René Descartes, 1596–1650, in "Discours
Page 52
de la Methode"]
Dr. Michael Stal: Architecture Refactoring
27
© Dr. Michael Stal, 2010
Backup
Backup
Page 53
Rename Entities (1)
Example
Comp ShareTicker
Page 54
AnotherComp ShareObserver
Dr. Michael Stal: Architecture Refactoring
28
© Dr. Michael Stal, 2010
Rename Entities (2)
Context
Using non-intuitive namesUsing non intuitive names
Problem
Your software architecture contains entities named in such a way that the whole architecture lacks expressiveness
General solution idea
Introduce intuitive names so that stakeholders can easily understand the role of each subsystem
Change the names of the entities and also consider references to
Page 55
Change the names of the entities and also consider references to these entities
Change only one entity name at a time
Use one predefined naming scheme / strategy throughout the project
Remove Duplicates (1)
Extract:
Example
Client Amain() {...}
Extract:
Connect JNDI
Get Home
Locate bean X
Client A'main() {...}
Bean LocationComponent
1. Connect JNDI
2. Get Home
3. Locate bean
call locateBean(X)
Page 56
Client Bmain() {...}
Extract:
Connect JNDI
Get Home
Locate bean Y
Client B'main() {...}
Extract:
call locateBean(Y)
Dr. Michael Stal: Architecture Refactoring
29
© Dr. Michael Stal, 2010
Remove Duplicates (2)
Context Equivalent design artifacts are replicated throughout the q g p g
architecture Problem DRY (Don't Repeat Yourself) is an essential means to increase
simplicity, expressiveness, orthogonality and reuse On the other hand, if the same design artifacts are repeatedly
implemented and used in a software architecture, then manageability and productivity will suffer
How can we prevent the introduction of equivalent design artifacts?
Page 57
General solution idea Identify common (sequences of) tasks or sequences of tasks
repeatedly used throughout your software system Analyze whether these common (sequences of tasks) can be
provided as a generic component If that's the case, remove duplicates and introduce one common
design artifact instead
Introduce Abstraction Hierarchies (1)
Wi d Wi d
Example: Class hierarchy
Windowdisplay()
Paneldisplay()drawPixel()
Buttondisplay()drawPixel()
Windowdisplay()
PaneldrawPixel()
ButtononClick()
Page 58
drawPixel()onClick()
Separate abstractions
Related abstractions
"is-a" relation
Dr. Michael Stal: Architecture Refactoring
30
© Dr. Michael Stal, 2010
Introduce Abstraction Hierarchies (2)
Context Entities representing related concepts p g p
Problem In the architecture design entities appear that implement almost the
same functionality This leads to design and code replication, as well as to less
expressiveness and simplicity General solution idea Leverage the Liskov Substitution Principle Introduce general abstractions containing the common parts of
Page 59
these entities Define hierarchy of abstractions Derive entities from the most specific abstraction
Different possibilities Adding superclass Adding common interfaces
Remove Unnecessary Abstractions (1)
Example
*
AbstractStorage
TransportWay
* AbstractStrategy
CompositeStorage
BinDoorDumpEquip-ment
*
ConcreteStrategy
*
Cart Belt
Page 60
ConcreteStrategy
*
AbstractStorage
CompositeStorage
BinDoorDump
AbstractStrategy
Equipment
*
Dr. Michael Stal: Architecture Refactoring
31
© Dr. Michael Stal, 2010
Remove Unnecessary Abstractions (2)
Context Eliminating unnecessary design abstractions
Problem Minimalism is an important goal of software architecture, because
minimalism increases simplicity and expressiveness If the software architecture comprises abstractions that could also
be considered abstractions derived from other abstractions, then it is better to remove these abstractions
General solution idea Determine whether abstractions / design artifacts exist that could
also be derived from other abstractions
Page 61
also be derived from other abstractions If this is the case, remove superfluous abstractions; derive entities that depend from removed abstractions to derive
from abstractions being left Challenge: Don't generalize too much (such as introducing one
single hierarchy level: "All classes are directly derived from Object")
Substitute Mediation with Adaptation (1)
Example
I_haveno clue sub 1
Adapter Adapter Adapter
Adapter Adapter Adapter
Integration Layer
A B C
no_clue_sub_1
I_haveno_clue_sub2
I_haveno_clue_sub_3
Do_Everything_Class
a.k.a. Mediator
Page 62
D E FI_have
no_clue_sub_4I_have
no_clue_sub_5
I_haveno_clue_sub_6
Dr. Michael Stal: Architecture Refactoring
32
© Dr. Michael Stal, 2010
Substitute Mediation with Adaptation (2)
Context Centralized system using a mediation level between peers
Problem When interaction between different peers is complex, a mediator
may be the right solution However, extensive use of mediator may reduce scalability If mediation was just applied to build an extremely generic solution,
design erosion is often the price How can we improve this situation?
General solution idea Introduce adapters to uniformly plug in peers
Page 63
Introduce adapters to uniformly plug in peers But make interaction explicit, i.e. subsystems themselves are in
charge to interact with the appropriate peers using an integration layer
Known use: Enterprise Application Integration (Hub and Spoke) => Enterprise Service Bus
Break dependency cycles (1)
GetState()
SetState {
GetState()
SetState {In this example,
Sensor
SetState {
...
M.GetDate()
}
X BreakCycle
Sensor
SetState {
...
D.GetDate()
}
Date
GetDate()
In this example, the monitor invokes the state getter / setter methods but also provides GetDate()to the sensor, lea-ding to a simple dependency cycle. Providing this me-
Page 64
Monitor
GetDate()
Draw() {
... S.GetState();
}
Monitor
Draw() {
... S.GetState();
}
thod to monitors was a bad design decision, anyway. Introducing a sepa-rate date object solves theproblem.
Dr. Michael Stal: Architecture Refactoring
33
© Dr. Michael Stal, 2010
Break dependency cycles (2)
Context
Dependencies between subsystems Dependencies between subsystems
Problem
Your system reveals at least one dependency cycle between subsystems
Subsystem A may either depend directly or indirectly on subsystem B(e.g., A depends on C which depends on B) which is why we always needto consider the transitive hull
Page 65
Dependency cycles make systems less maintainable, changeable, reusable, testable, understandable
Thus, dependency hierarchies should form DAGs (directed acyclic graphs)
General solution idea
Get rid of the dependency cycle by removing one of the dependencies
Break dependency cycles – Dependency Inversion
Attach(Observer)
Notify(
interface IObserver { Handle() }
Attach(IObserver)
Notify(
Subject'
Notify(
observer.Handle();
)
Subject
Notify(
iObserver.Handle();
)
Dependency inver-sion constitutesanother variant
Page 66
Observer'
Handle()
StartUp() {
subj.Attach(obs);
)
Observer
class Obs implements IObserver {
Handle() { ...} }
StartUp(
subj.Attach(obs);
)
Dr. Michael Stal: Architecture Refactoring
34
© Dr. Michael Stal, 2010
Inject Dependencies (1)
Component c {
S s = new S(.....);
Component c {
inject(S p){ s=p;}
Example
Component c
s.m();
}
Component c
s.m();
}
Page 67
Component s Container m Component s
C c = new C();
S.S = new S(.....);
c.inject(s);
Inject Dependencies (2)
Context
Components that need to create and use other componentsComponents that need to create and use other components
Problem
If a component c needs access to other components, it most somehow create these components or locate them
However, this makes c too dependent on the knowledge about specific component location or factory mechanisms
How can we avoid these additional dependencies?
General solution idea
Page 68
General solution idea
Move functionality for discovery and creation of components to separate component m
Introduce configuration to let component c specify its requirements and let m be responsible for creating, discovering and then injecting required component references to c
Dr. Michael Stal: Architecture Refactoring
35
© Dr. Michael Stal, 2010
Insert Transparency Layer (1)
Example
ClientSubsystem B
Client
Subsystem BTRANSPARENCY
LAYER
Page 69
Insert Transparency Layer (2)
Context Decoupling clients or from subsystems and vice-versa P bl Problem When a client accesses subsystems directly, some dependencies are
introduced, e.g., On the exact physical location of the subsystems, or On specific data or document formats the subsystem expects
The client must have knowledge about how to locate and access the subsystems
Finally, the client also becomes dependent on subsystem implementation aspects
General solution idea
Page 70
General solution idea To avoid such dependencies, we introduce transparency layers to
decouple these dependencies from clients Transparency layers also open additional optimization possibilities This is basically a whole class of architecture refactorings, because for
the implementation of transparency layers we rely on patterns such as Proxy, Facade, Wrapper-Facade, Business Delegate, Service Locator, Proxy
Dr. Michael Stal: Architecture Refactoring
36
© Dr. Michael Stal, 2010
Example: Introducing Protection Layers
Typically, an architectural entity depends directly on other architectural entities
This becomes a problem when the target of the Client
p gdependency is subject to change (volatility)
In this case add a layer to protect your entity from directly depending on a volatile entity
Note: this decreases inter-component coupling
Server
Protection Layer
Page 71
Server
Reduce Dependencies with Facades (1)
Rental CarBooking
1.3
Example
ClientHotel
Booking
g
FlightBooking1.1
1.2
Rental CarB ki
2.3
Page 72
FacadeTravel
Booking
HotelBooking
Booking
FlightBooking2.1
2.2Client
1
Dr. Michael Stal: Architecture Refactoring
37
© Dr. Michael Stal, 2010
Reduce Dependencies with Facades (2)
Context Client implements workflows that mostly access other componentsp y p
Problem If a client needs to access different external components for each
workflow, it becomes dependent on details such as the set of available components
As soon as the configuration and / or interfaces of these components change, there will be a direct impact on the client
General solution idea Introduce a Facade component that acts as the client's gateway into
Page 73
Introduce a Facade component that acts as the client s gateway into the set of required components
Implement workflow methods within the facade that represent the different workflows
The facade methods take over the responsibility for accessing the set of required components on behalf of the client, thus decoupling the client from the components
If components are remote, performance is also improved (Session Facade)
Merge Subsystems (1)
Example: While tight coupling between subsystems is bad, high cohesion within subsystems is good. Thus, merge tightly coupled subsystems to form a highly cohesive subsystem .
Sensor
Sensor + Utilities
Page 74
Utilities
Special variant: Merge a layer in a layered system
Dr. Michael Stal: Architecture Refactoring
38
© Dr. Michael Stal, 2010
Merge Subsystems (2)
Context Coupling between subsystems
Problem Between two subsystems in a software architecture the degree of
coupling should be rather loose Within a subsystem the number of interdependencies (cohesion)
should be high If the coupling is too tight, then the many interdependencies between
these two subsystems decrease qualities such as Changeability Performance (maybe)
Page 75
Reusability of one of the subsystems General solution idea Tight coupling between subsystems implies that the two subsystems
in fact implement one subsystem Either merge both subsystems to form one, or Move functionality from one subsystem to the other
Split Subsystems (1)
Example: When analyzing interdependencies between entities in a subsystem, two(or more) sets can be determined. Within these sets there is high cohesion; betweenthese sets there is only low cohesion. Thus, split the subsystem into two (or more) parts.
Component Container
LifecycleManagement
Persistence Event Handling
High Cohesion
ComponentContainer
parts.
Page 76
Communication
Communi-cation
Special variant: Split layer in a layered system
Dr. Michael Stal: Architecture Refactoring
39
© Dr. Michael Stal, 2010
Split Subsystems (2)
Context Cohesion within a subsystem
Problem Within a subsystem the interdependencies (cohesion) should be high Between two subsystems in a software architecture, the degree of
coupling should be rather loose If the cohesion between some parts is loose, then some design
decisions seem to be questionable It is recommendable to change this to obtain better modularization and
understandability Another potential problem are subsystems/components with too many
Page 77
responsibilities General solution idea Loose cohesion within a subsystem implies that the functionality can
be split into multiple subsystems Thus, determine areas with high cohesion in a subsystem. All those
areas with low cohesion are candidates for becoming subsystems of their own
Enforce Strict layering (1)
Example
Application Application
Component 3.1
Component 3.2
Component 3.3
Layer 3
Component 2.1
Component 2.2
Layer 2
pp
Component 3.1
Component 3.2
Component 3.3
Layer 3
Component 2.1
Component 2.2
Layer 2
Application
Brokenlayering
Page 78
Component 1.1
Component 1.2
Component 1.3
Layer 1
Component 1.1
Component 1.2
Component 1.3
Layer 1
Dr. Michael Stal: Architecture Refactoring
40
© Dr. Michael Stal, 2010
Enforce Strict Layering (2)
Context
An architecture with broken layering An architecture with broken layering
Problem
Layers are introduced to reduce dependencies, increase adaptability and reusability, as well as maintainability
Sometimes relaxed layering is used intentionally to solve problems such as performance issues
At other times relaxed layering is used accidentally without any
Page 79
y g y yjustification and value
In the latter case, how can we cure the broker layering?
General solution idea
Redesign so that the broken layering is substituted with strict layering
Enforce Strict Layering (3)
Unfortunately, broken layering is often hard to cure To resolve the broken layering between component 3.1 and component
1.1, we must differentiate the following cases: Case 1: All the types of functionality of component 1.1 that component
3.1 uses are also available in layer 2 In component 3.1, substitute all usages of component 1.1 by
equivalent functionality of layer 2 Case 2: Not all the types of functionality of component 1.1 that
component 3.1 uses are available in layer 2 Substitute all those component 1.1 invocations with equivalent
invocations of layer 2 For the rest of the invocations move
Page 80
invocations of layer 2. For the rest of the invocations, move functionality from layer 3 to layer 2
In case 2 it is sometimes not advisable to move functionality from layer 3 to layer 2. Consider reengineering activities instead!
Note: This is one of the refactorings that can also be applied in the opposite direction. If strict layering is not feasible (e.g., due to performance), relax the strict layering!
Dr. Michael Stal: Architecture Refactoring
41
© Dr. Michael Stal, 2010
Move Entities (1)
Example
Game
getPlayerHistory(p)
x y z
Game
getPlayerHistory()
x y z
Page 81
Player Player
getPlayerHistory()
Move Entities (2)
Context
Moving entities between subsystems Moving entities between subsystems
Problem
This can be considered a generalization of the Split Subsystem refactoring
In a subsystem an entity is defined that fits better semantically in another subsystem to which it has high coupling
General solution idea
Page 82
Again, we should consider cohesion and coupling
If entity e reveals low or zero cohesion to the rest of its subsystem A, but tight coupling to subsystem B, then move e from A to B
Note: This may also help with breaking dependency cycles between subsystems
Dr. Michael Stal: Architecture Refactoring
42
© Dr. Michael Stal, 2010
Move Entities (3)
Some additional issues
In many cases we can't apply the refactoring to single atomic entities In many cases, we can t apply the refactoring to single atomic entities such as a class, method, constant, interface
It is more likely that there will be clusters of atomic entities that together build a complex entity
In the example, it is very likely that there will be classes and interfaces related to the method to be moved
Thus, always consider semantically related entities with high internal
Page 83
y y gcohesion but less cohesion to the rest of their subsystem. If some of them show tight coupling to another subsystem, move the whole group
Moving may not always be a simple operation, but imply several smaller grained transformations (see example)
Add Strategies (1)
Warning
Example
Client
Request Dispatcher
Client
Middleware
DispatcherStrategy
Middleware
Warning
Never introduce strategies in an uncontrolled way
A strategy basically means "I have no idea which option to use at runtime"
St t S d i
Page 84
Protocol Protocol
ConcreteDispatcher Strategy Syndrome is
a serious disease that leads to an over-generic architecture which is doomed to design erosion
Dr. Michael Stal: Architecture Refactoring
43
© Dr. Michael Stal, 2010
Add Strategies (2)
Context
Enable selection and exchange of implementationsEnable selection and exchange of implementations
Problem
Services and algorithms in a component can be implemented in various ways. Their implementation often also depend on client requirements
The client should not depend on the concrete implementations used
Hard-coding a service or algorithm and then exchanging it at source code level is a possible solution
Page 85
code level is a possible solution
Unfortunately, this does not hold for runtime configurability
How can we refactor a component to support this kind of runtime configuration?
General solution idea
Use the Strategy pattern
Enforce Symmetry (1)
Examples for structural symmetry
Subsystem UIBased on MVCand Observer
Subsystem Accounts
Subsystem UIBased on MVCand Observer
Some places in the architecture throw exceptions, while other return error codes
One subsystem leverages the Observer pattern
Page 86
yUses hardwiredconfiguration ofevent handlers
Subsystem AccountsUses Observer
Observer pattern, another one a hardwired event notification strategy
Dr. Michael Stal: Architecture Refactoring
44
© Dr. Michael Stal, 2010
Enforce Symmetry (2)
Context
Refactor an asymmetric solutionRefactor an asymmetric solution
Problem
Asymmetry reduces conceptual integrity and simplicity
Structural asymmetry deals with the usage of the same solution concepts throughout an architecture
Functional asymmetry is relevant on the method level ("When there is an open, there should be a close operation")
General solution idea
Page 87
Prioritize strategy over tactics
For identical problem contexts in your architecture, apply the same solutions (e.g., the same patterns)
Use refactoring-to-patterns where applicable
All unmotivated asymmetries should be removed step-by-step
Enforce Symmetry (3)
AbstractProductA
ProductA
AbstractFactory
create productA
Example for functional symmetry: Abstract Factory with missing dispose method
ProductA
ConcreteProductA
ProductA
create_productA
ConcreteFactory
create_productA
Page 88
AbstractFactory
create_productAdispose_productA
ConcreteFactory
create_productAdispose_productA
AbstractProductA
ProductA
ConcreteProductA
ProductA
Dr. Michael Stal: Architecture Refactoring
45
© Dr. Michael Stal, 2010
Extract Interface (1)
<< interface >>
ISensor
Example
Sensor
temperature()useCelsius()useFahrenheit()initialize()reset()
ISensorgetTemperature(UNIT)
Sensor
temperature()useCelsius()useFahrenheit()
if (UNIT == CELSIUS)
useCelsius();
else
useFarhrenheit();
Adapter
Page 89
getAverage(from,to)storeRecord()getRecord(DateTime)getDateTime()
initialize()reset()getAverage(from,to)storeRecord()getRecord(DateTime)getDateTime()
return temperature();
Extract Interface (2)
Context
(Sub)system needs to export functionality(Sub)system needs to export functionality
Problem
An existing (sub)system needs to export some of its internal functionality to external users
How can the (sub)system developer make internal functionality available externally?
General solution idea
Define abstract interface and contract
Page 90
Provide wiring of interface methods to internal functionality
Apply patterns such as Bridge, Decorator, or Adapter to separate interface from implementation
Optionally, use the Extension Interface pattern to manage multiple interfaces
Dr. Michael Stal: Architecture Refactoring
46
© Dr. Michael Stal, 2010
Enforce Contract (1)
<< interface >> << interface >>
Example
<< interface >>
IFileHandle open(File)Write(Handle, buf)
close(Handle)
<< interface >>
IFileHandle open(File)
Write(buf)close()
Contract Implementation
Open/IsValid(File)
Open/not IsValid(File)
Write/notEmpty(buf)
Close
Page 91
File System File System( )
Enforce Contract (2)
Context
Enforcing contract of an interfaceEnforcing contract of an interface
Problem
A contract defines what the user of an interface must provide and what the user can expect in turn, e.g., preconditions, postconditions, required order of method execution
Often, interface implementations do not enforce the contract
A contract that is never enforced is of limited value
How can we enforce the interface contract?
Page 92
General solution idea
Define contract explicitly: For example, use a state machine to define method execution order. Specify interface invariants. For each method, determine preconditions and postconditions
Enrich interface implementation to enforce contract
Dr. Michael Stal: Architecture Refactoring
47
© Dr. Michael Stal, 2010
Provide Extension Interfaces (1)
Factory Client
create()find()
RootInterface
get_extension()
Extension Extension
Interface1
service_1.1();service_1.2();service_1.3();service_1.4();......
Page 93
Interface1
service_1()
Interface2
service_2()
Component
svc_1_impl()svc_2_impl()
Component
.service_1.n();
Provide Extension Interfaces (2)
Context Managing interfaces of a component
Problem If a component evolves over time, often new functionality is added to
the component's interfaces This may lead to severe interface bloating, which has a negative impact
on quality attributes such as usability and manageability (Swiss Army Knife anti-pattern)
Unsystematic interface evolution may also break client code How can we add an interface management for systematically evolving
and providing interfaces?
Page 94
and providing interfaces? General solution idea Apply Extension Interface Patterns [POSA2] Introduce a common protocol for all provided interfaces (including
interface navigation) Integrate additional functionality so that clients can discover existing
component interfaces and navigate between them
Dr. Michael Stal: Architecture Refactoring
48
© Dr. Michael Stal, 2010
Provide Extension Interfaces (3)
Client Factory
Notes
create Comp.Root
Interface
ExtInter-face1
ExtInter-face2
service1
InterfaceID1
InterfaceID1
Interface1Interface
1
InterfaceID2 get_extension
This refactoring alters components and clients. Thus, handle with care!
Interface navigation should be reflexive, symmetric, transitive
But you may also
Page 95
ID2
Interface2
service2
But you may also introduce a dedicated interface that includes the navigation functionality
Substitute Inheritance with Delegation (1)
Example
Playerplay()
NinjalookupExperience()
IPlayerplay()
Player
play(IPlayer)play()
NinjalookupExperience()play()Player pdelegate
Page 96
play(IPlayer ip) {// pre-processingthis.play();// post-processing}
Dr. Michael Stal: Architecture Refactoring
49
© Dr. Michael Stal, 2010
Substitute Inheritance with Delegation (2)
Context Using delegation when inheritance is not available or not suitable
Problem Assume an entity D should logically inherit from entity B, but
inheritance is not available on component or subsystem level, or inheritance cannot be used due to other restrictions
In these cases, using inheritance is not the right approach How can we get rid of (the necessity to use) inheritance but get the
same possibilities? General solution idea Keep B as it is
Page 97
Keep B as it is Within entity D add reference to B Provide public interface of B also in D but forward all invocations to
referred B (optionally passing a reference to D for back calls) Additional note This is another one of the reversible patterns that may also be
applicable in the opposite direction!
Provide Interoperability Layers (1)
Example
Application layer
Adapter layer
Page 98
Provide bidirectional interoperability between two subsystems usingWeb Services
Extract subsystem interfaces and use adapters (e.g., session facades, business delegates) to integrate them with their subsystems
Dr. Michael Stal: Architecture Refactoring
50
© Dr. Michael Stal, 2010
Provide Interoperability Layers (2)
Context
Enable interoperability between two subsystemsEnable interoperability between two subsystems
Problem
Sometimes formerly collocated subsystems need to be distributed, or
An application needs to interoperate with other legacy applications
Unfortunately, both of them were not designed to interoperate
General solution idea
Insert adapter layers to provide interoperability
Adapt subsystem A to B or subsystem B to A or use a bidirectional
Page 99
Adapt subsystem A to B or subsystem B to A, or use a bidirectional adaptation
Adaptation is a holistic approach. You may adapt communication, data, policies
Use patterns such as Adapter or Wrapper Facade. In other words, apply the Insert Transparency Layer refactoring
Provide Interoperability (3)
Aspects for interoperability in object-oriented, distributed systems
Interoperability is not restricted to data transformation and Interoperability is not restricted to data transformation and communication
For example, we also need to adapt error management
Likewise, object models must be mapped onto each other
Policies (e.g., security, memory management) must be mapped onto each other
The simpler the adaptation layers provided, the more work developers
Page 100
p p y p , pmust spend to close the semantic gap
Integration products such as EAI solutions just provide a set of proprietary interoperability solutions
Dr. Michael Stal: Architecture Refactoring
51
© Dr. Michael Stal, 2010
Aspectify (1)
Example
+
Page 101
Logging functionality
Logging functionality
Aspectify (2)
Context Dealing with cross-cutting concernsg g
Problem Cross-cutting concerns such as security mechanisms, distribution
mechanisms and other invasive functionalities lead to extensive replication of design and code
OO mechanisms such as inheritance can't solve the problem, due to infeasible centralization of these concerns
Nonetheless, separation of concerns should be supported General solution idea
Page 102
Express concern as a centralized package Introduce injection mechanism to integrate wrapped concern in all
required places of software architecture If cross-cutting concerns are interdependent, allow injection
mechanism to take care of interdependencies, for example by using prioritization or a generative approach
Dr. Michael Stal: Architecture Refactoring
52
© Dr. Michael Stal, 2010
Integrate DSLs (1)
Example
Abstract
Monitor
StorageMgr Capacity ServiceX
RootInterface
InterfaceFactory
Abstract
Monitor
StorageMgr Capacity ServiceX
RootInterface
InterfaceFactory
Hook
AbstractInterceptor
*
*
StorageMgrInterface
CapacityInterface
ServiceXInterface
StorageCapacity
*
AbstractVisitor
LeafsOnly
AbstractStrategy
LoadInLayers
AbstractIterator
*
StorageManager
HazardousSOC
SOCFactory
*Bin
*AbstractStorage
Warehouse
Successor
AtomicStorage
CompositeStorage
Aisle
Hook
AbstractInterceptor
*
*
StorageMgrInterface
CapacityInterface
ServiceXInterface
StorageCapacity
*
AbstractVisitor
LeafsOnly
AbstractStrategy
LoadInLayers
AbstractIterator
*
StorageManager
HazardousSOC
SOCFactory
*Bin
*AbstractStorage
Ware-house
Successor
AtomicStorage
CompositeStorage
Aisle
Page 103
Microsoft's XAML (eXtensible Application Markup Language) is usedfor specifying workflows declaratively within and between applications
In the past, developers had to hardcode these workflows in thearchitecture using implementation languages such as C#
RealBin
HazardousValue
ThreadMutex Socket
RealBin
HazardousValue
ThreadMutex Socket
Integrate DSLs (2)
Context A system that covers many different domains
Problem Mastering complexity in a software system is an important challenge.
Complexity is often cause by the usage of multiple domains (based on different concepts and paradigms)
If many domains are involved, such as the business logic itself, accessing databases, specifying workflows, UI design, then impedance mismatch becomes an essential issue
In addition, the work of different domain experts is more difficult, because all domain experts must understand the full software system
Page 104
General solution idea Instead of hard-coding domain-specific functionality manually, define a
metamodel / DSL that allows domain experts to define their logic Implement generators that transform models / DSLs to implementation
artifacts Define an integration subsystem for each of these domains that
integrates the generated artifacts with the rest of the system
Dr. Michael Stal: Architecture Refactoring
53
© Dr. Michael Stal, 2010
Add Uniform Support for Runtime Aspects (1)
Subsys UI Subsys UI
Subsys DB
Subsys ATM
IUIAdmin
IDBAdmin
Subsys DB
Subsys ATM
IAdmin
IAdmin
Page 105
IATMAdmin IAdmin
ManagementConsole
Add Uniform Support for Runtime Aspects (2)
Context Software architecture that must support runtime aspects such as
f flifecycle management, management, configuration Problem Each non-trivial software architecture consists of multiple
components, each of which supports common aspects If every component implements its own interfaces for runtime aspects,
the overall system will lack simplicity and expressiveness Is there a way to provide a uniform approach to support those kinds of
runtime aspects? General solution idea
Page 106
For each aspect, define a common interface (e.g., an interface for runtime configuration)
In each component supporting the aspect, provide this common interface (maybe using Extension Interfaces to prevent bloating)
To achieve orthogonality, provide one runtime component that is in chargeof accessing these common interfaces (e.g., a management console)
Dr. Michael Stal: Architecture Refactoring
54
© Dr. Michael Stal, 2010
Add Configuration Subsystem (1)
Example
Subsystem UI
Subsystem DB
IConfig2
IConfig1
Actor
Subsystem UI
Subsystem DB
IConfig
IConfig
ConfigurationSubsystem
Actor
Configuration
Page 107
Subsystem ATM
IConfig3
Subsystem ATM
IConfig
Use patterns such as Component Configurator for this purpose
Add Configuration Subsystem (2)
Context A system with many configurable variabilities
Problem If a system contains a lot of configuration options, then configuration
itself is often tedious and error-prone This holds in particular when the configuration options are (partially)
related to each other How can we refactor the software system so that configuration is
simplified and (partially) automated? General solution idea Instead of providing dozens of configuration options to external actors,
Page 108
we introduce a configuration subsystem that takes a declarative configuration (from a file or a wizard)
The configuration subsystem reacts to configuration change events, reads the passed configuration, and then accesses the configuration interfaces of configurable subsystems
Ideally, all subsystems provide the same generic configuration interface
Dr. Michael Stal: Architecture Refactoring
55
© Dr. Michael Stal, 2010
Introduce the Open/Close Principle (1)
Example
Sensor Sensor
ObserverInterface
Page 109
SensorMonitor
Introduce the Open / Close Principle (2)
Context Opening a subsystem for external monitoring or modification (e.g.,
)eventing or interception) Problem An existing subsystem needs to be opened to let other subsystems
observe this subsystem or intercept specific behavior So far, the subsystem has been a black box How can we introduce mechanisms for opening our architecture in a
controlled way? General solution idea Apply the Open / Close Principle
Page 110
Design the states and state transitions of the subsystem and decide which transitions should be observable or interceptable For all these observation and interception points, introduce
appropriate hooks with which other subsystems can register for observation or interception If interception is required, design the required context object and
subsystem internal functionality required for interception
Dr. Michael Stal: Architecture Refactoring
56
© Dr. Michael Stal, 2010
Optimize with Caching (1)
Example
Cache
acquire()release()
ClientSubsystem
getResource()
Resource
Client
*
allocates andcreates
Subsystem
getResource()
Page 111
release()
Resource*
maintains
Optimize with Caching (2)
Context Optimize access to reusable resources using caching
Problem When a subsystem provides resources to other parties, resource
access becomes a potential bottleneck If high usage frequency of the same resources leads to performance
bottlenecks due to costs for resource creation, allocation, initialization, additional means of accelerating resource access are necessary
How can we optimize resource access? General solution idea Introduce a fast cache where reusable resources are maintained
Page 112
Define an appropriate resource allocation (activation) and a de-allocation (eviction) strategy
Provide a transparency layer to resource users
Dr. Michael Stal: Architecture Refactoring
57
© Dr. Michael Stal, 2010
Optimize with Caching (3)
The choice of allocation / de-allocation strategy is important Example A web site that returns weather maps based on zip codes Suppose zip-based maps and weather data are stored in databases.
When a new request arrives A servlet reads the map reads the weather data then uses image processing to calculate the map with weather
information which it finally returns to the client
When we introduce a cache, how do we decide which maps to store?
Page 113
Should we always remove the least frequently used maps? Or could we just store the "empty" map in the cache?
How should we deal with updates (weather changes)? For example, can we introduce timeout values (leasing)?
Replace Singleton (first variant) (1)
Singleton MethodClass
Example
+m1();+m2();+m3();
state
+m1();+m2();+m3();
t t
Page 114
state
State is now a singleton, while behavior isn't In the end it's all about identity Known use: EJB Entity Beans
Dr. Michael Stal: Architecture Refactoring
58
© Dr. Michael Stal, 2010
Replace Singleton (first variant) (2)
Context Resources / objects with state that should be available at most once
Problem Assume that a resource type (objects within a class, threads in a
thread pool, …) that represents a particular state should only contain one single instance
For this purpose, the Singleton Pattern is often applied Unfortunately, the Singleton pattern acts like a global declaration Are there any alternatives for our problem context?
General solution idea In a singleton, state and behavior are integrated. The non-transient
Page 115
parts of the state represent the identity of the singleton object Thus, the singleton mustn't be replicated, as this would duplicate its
identity Replace the singleton class with a class that provides the same public
interface (value class), but store all the non-transient state in a central place (database, TSS, hash table, …) with the key known to the new class. The behavior and transient state, however, may be replicated
Replace Singleton (first variant) (3)
HalfObject1
service1()
Virtual Objects The same concept is applicable to
HalfObject2
service1()se ce ()service2()
protocol()
Here There
local
introduce operational qualities such as availability or fault-tolerance through replication
In this case a number of objects provide the same services but store their state in a central place
For clients all objects appear as one single (virtual) object
For state synchronization between
Half Objects
se ce ()service3()
protocol()
Page 116
remote
Protocol
For state synchronization between physical objects (that represent the same virtual object), a protocol is required
Patterns to be applied: Half-Sync-Plus-Protocol Object Group
Dr. Michael Stal: Architecture Refactoring
59
© Dr. Michael Stal, 2010
Replace Singleton (second variant)
Another possible way to remove singletons is to introduce a wrapper, such as an object manager or factory
f f
Singleton
+m1();+m2();+m3();
state
User
The wrapper is in control of lifecycle management and thus able to ensure that a specific type has only one instance
Page 117
Wrapper
The wrapper ensures that at most one instance of the singleton is created. Note that a singleton might be removed and then reactivated, e.g., due to leasing.
Separate Synchronous and Asynchronous Processing (1)
Example
sy_svc_1 sy_svc_2 sy_svc_3
sy_svc_1 sy_svc_2 sy_svc_3
Synchronous Service Layer
as_svc_1 as_svc_2 as_svc_3
AsyncService
QueueSync
Service
interrupt
msg
Page 118
as_svc_1 as_svc_2 as_svc_3
Asynchronous Service Layer
Queuing Layer
interrupt
notifyenqueue
work
msg
de-queue
msg
Dr. Michael Stal: Architecture Refactoring
60
© Dr. Michael Stal, 2010
Separate Synchronous and Asynchronous Processing (2)
Context Distributed systems that include a mixture of synchronous and y y
asynchronous processing Problem Both asynchronous and synchronous services should be decoupled,
so that neither suffers from the deficiencies of the other Both asynchronous and synchronous services should be able to
communicate with one another efficiently using their own communication paradigm
How can we refactor an architecture where synchronous and
Page 119
asynchronous services are tightly coupled? Solution Separate synchronous and asynchronous services from one another
by dedicated layers and add a queuing layer between them to mediate communication between services (Half Sync / Half Async Pattern)
Replace Remote Methods with Messages (1)
Client C Side Proxy C Side Pro
Example
Client
service_c()
C_Side_Proxy
service_s()
snd_request()rcv_response()
Broker
est_connection()find_server()register_svt()
Bridge
est_connection()find_server()
*
C_Side_Proxy
service_s()
snd_request()rcv_response()
Queue
listen()enqueue()dequeue()
Direct connection
Page 120
Servant
service_s()
S_Side_Proxy
rcv_request()snd_response()
unregister_svt()
*S_Side_Proxy
rcv_request()snd_response()
dequeue()
Dr. Michael Stal: Architecture Refactoring
61
© Dr. Michael Stal, 2010
Replace Remote Methods with Messages (2)
Context Asynchronous problem using synchronous remote methods
Problem In many distributed systems remote method invocation is the primary
choice of communication. Asynchronicity is often introduced in the application layer, but implemented on top of remote methods
However, message-oriented middleware is often the better choice for dealing with event-based or asynchronous communication
Is there a migration path to move from synchronous to asynchronous communication without huge impact?
General solution idea
Page 121
General solution idea Keep the Broker-based architecture unchanged but integrate an
additional asynchronous layer that relies on messaging middleware For synchronous communication, still use the synchronous layer For asynchronous communication, use the asynchronous layer instead
(message queues, asynchronous completion tokens, …) Future functionality may then directly rely on asynchronous
messaging
Add Object Manager (1)
Example
Client Factory
create()delete()find()
Object
ClientObject Manager
createObject()deleteObject()insertObject()removeObject()findObject()objectIterator()
Page 122
service() ManagedObject
service()
*
Dr. Michael Stal: Architecture Refactoring
62
© Dr. Michael Stal, 2010
Add Object Manager (2)
Context
Management support for objectsManagement support for objects
Problem
Objects (and resources) for client usage often need to implement management functionality in addition to business logic, e.g., lifecycle management, persistence management
This burden leads to memory usage and responsibility overload
Clients shouldn't be responsible for object management either
Different applications may require different management policies
Page 123
How can we provide such additional management?
General solution idea
Separate object usage from object management
Introduce an manager component that manages objects throughout their lifetime
Change Unidirectional Association to Bidirectional (1) (Fowler)
Order Order
Example
Customer
*
1
Customer
*
1
class Order...Customer getCustomer() {return _customer;}
class Customer...private Set _orders = new HashSet;void addOrder(Order arg) {arg.SetCustomer(this);
Page 124
}void setCustomer(Customer arg) {_customer = arg;}Customer customer;
arg.SetCustomer(this);}Set friendOrders() {return _orders;}
class Order...void setCustomer(Customer arg) {_if (_customer != NULL)customer.friendOrders().add(this);
}
Dr. Michael Stal: Architecture Refactoring
63
© Dr. Michael Stal, 2010
Change Unidirectional Association to Bidirectional (2) (Fowler)
Context Two classes referring to each other
Problem Class A refers to a class B, but there is no backward navigation from B
to A However, after some development activities, you recognize that B also
requires a reference to all A objects referring to it How can we transform the unidirectional to a bidirectional navigation?
General solution idea Add a back link in class B that allows navigation from B to the referring
A object(s)
Page 125
Decide which class is in charge of controlling the association, e.g. The composite controls its constituents In one-to-many relationships, make the one-side (i.e., side with
multiplicity of one) the controller Introduce helper method in the non-controlling side If the existing modifier is on the controlling side, modify it to update
back references, otherwise create a controlling method on the controlling side and call it from the existing modifier
Refactoring
References
Page 126
Dr. Michael Stal: Architecture Refactoring
64
© Dr. Michael Stal, 2010
Refactoring
Fowler Martin et al 1999 Refactoring: Improving
References
Fowler, Martin et al. 1999. Refactoring: Improving the Design of Existing Code. Addison-Wesley
Demeyer, S; Ducasse, S; Nierstrasz, O. 2002. Object-Oriented Reengineering Patterns. Morgan Kaufmann
Kerievsky, Joshua. 2004. Refactoring to Patterns. Addison-Wesley
Page 127
Ambler, Scott W.; Sadalage, Pramodkumar J, 2006. Refactoring Databases: Evolutionary Database Design. Addison-Wesley
Thanks to Dr. Michael Stal for his permission to use his material in this presentation