Title: 8' Refactoring
18. Refactoring
- Repeated software changes lead to the
- loss of structure,
- loss of understanding
- loss of maintainability
- loss of evolvability
- Remedy Refactoring
- Refactoring restructuring reengineering
architecture repair
2Examples of refactoring
- encapsulate part of the code as a function
- macroexpand a function in a place of call
- rename
- move a member function from one class into
another - merge and divide classes
- factor out a base class
3Changes in software
- localized changes good changes
- ideal change affects one class only (or just few)
- delocalized changes bad changes
- affect many classes
- difficult
- risky
- risk increases with every additional module
visited
4Opportunistic refactoring
- Driven by the need for a specific change
- Before attempting the change, restructure
- Restructuring will make a change easier
- Restructuring consists of a sequence of behavior
preserving transformations
5Examples of opportunistic refactoring
- Line-oriented input/output ought to be replaced
by graphics user interface - first restructure and encapsulate entire
input/output - then replace line-orinted classes with GUI
classes - The program contains delocalized inference
- to be replaced by a commercially available
component
6Should architecture localize all changes?
- we cannot create/maintain such architecture
- difficult to predict what the changes will be
- Cusumano, Selby (Microsoft)
- at most 70 requirements predicted
- how many changes can we predict?
- architecture deteriorates during the maintenance
- result is further delocalization
7When is restructuring necessary
- the change is too difficult or too risky
- restructuring will localize it
- examples
- localize all line-oriented I/O operations before
introducing GUI - localize all inference computations before
introducing commercial inference engine
8Two-step process
- delocalized change is divided into two steps
- restructuring
- changes the structure, not behavior ("behavior
preserving") - localizes the intended change
- change itself
- now localized
- change in two steps is easier
9Create a base class
- Creation of base class is an example of behavior
preserving transformation - Observation
- In the design process, derived classes always
come before the base classes - Designers may miss some base classes
- Refactoring base class out of derived class will
correct these omissions
10class matrix
- protected
- int elements 10000, columns, rows
- public
- matrix()
- matrix inverse()
- matrix multiply (matrix)
- int get (int,int)
- void put(int,int)
- // dense matrix
11Refactoring class matrix
- We may need to add sparse matrix to the code.
- Sparse matrix uses the same algoritm for
multiply and inverse. - Only access to the elements are different
(functions get and put) - Refactor matrix, derive denseMatrix and
sparseMatrix from it
12Steps of refactoring
- create a new class abstractMatrix
- rename matrix to denseMatrix, make it derived
from abstractMatrix - replace all references to the elements by get
and put - move variables columns and rows to
abstractMatrix - move inverse and multiply to abstractMatrix
- add virtual functions get and put into
abstractMatrix
13Results
- After refactoring, it is easy to add
sparseMatrix - Refactoring preserves the behavior
- Tools for behavior preserving transformations
would be handy - A tool could guarantee the equivalence of the old
and new program - Without a tool, these transformations can be
costly, require retesting - Tools of this category are being researched
14Function insertion
15Actions of function insertion tool
- The function header is inserted into the class
specification. - Change access to the members of the target class
- The function header must be qualified by the
class identifier. - The parameter that is now replaced by membership
must be removed. - All function calls must be qualified with a class
instance. - All forward declarations of the function are
replaced by the new function declaration in the
target class specification.
16Restrictions of Function insertion tool
- The function cannot be a member of any class.
- It cannot be called through a pointer.
- No overloading
- It cannot have a variable number of parameters
- No template parameters.
- It can be inserted only into a class of one of
its parameters. - If the parameter is a pointer to a class then it
cannot be array, or involved in any pointer
arithmetic expression
17Function encapsulation
void foo(char c) int i,count,len char
strMAX cingtgtstr lenstrlen(str) newfun(count
,len,str,c) coutltltstrltlt" "ltltcountltlt"\n" newfu
n(int count,int len, char str,char c) int
i count0 for(i0iltleni) if(stric)
count stri'\n'
18function encapsulation
- select a block of code for encapsulation
- tool decides whether the block is syntactically
complete - tool creates new function
- tool encapsulates the selected block as a
function body - tool replaces the code block with the function
call - variables are classified
- Local variable
- Global variable
- Parameter passed by value
- Parameter passed by reference
19Function expulsion
- Removes a member function from a class.
- The function header is removed from class
specification - Generate public access functions for
private/protected data - Change access to private/protected functions
- The class qualifier must be removed from the
function header - Additional parameter (the source class type) must
be added - The class members are accessed through the
additional parameter in the function body. - The qualifying instance is converted to parameter
in all calls - A forward declaration of the function is included
into the file that holds the source class
specification.
20Scenarios
- Human - machine interactions
- some tasks done by computer, others by human
- Accomplish a specific task in specific
circumstances - The three restructuring tools are used in
scenarios
21Move a function from using to used class
- class A // A is using class
- foo(B) // B is used class
-
- Scenario of the move
- Expulse the function from the using class
- Insert the function into the used class
22Move function from composite to component
- class A // A is composite
- B b // B is component
-
- Add a new parameter of the composite type to the
function - Access all component members through the new
parameter - Expulse the misplaced function from the composite
- Insert the misplaced function into the component
23Move function - example
24Separate two concepts mixed in one class
Example
XComponent functionality and GUI
Asynchronous GUI calls
Rest of the system
25Scenario
- Create a new user interface class as a component
of the original class - Move the user interface data into the user
interface class. - Encapsulate all user interface code as new
functions - Encapsulate the remaining functional code as new
functions - Move all user interface functions into the user
interface class
26Example
class XComponent public Widget top_level()
void change_color(int) int update_component()
... protected Widget dialog Widget
text_field double data ...
class XComponent public Widget top_level()
void change_color(int) int update_component()
... protected CompWin w Widget dialog
Widget text_field double data ... class
CompWin public XComponent x
27Result
28Shorter change propagation
- Long change propagation is a problem
- it would be advantageous to shorten it
- opportunistic refactoring
- moves code affected by change into fewer classes
- splitting the roles
29Refactoring Drawlets
- each class that creates new figures has the
function basicNewFigure(...) - two main parts
- Create a new figure
- If the figure was created at wrong location, move
it. - We refactored a new method called moveFigure(...)
- made it a member of the base class
ConstructionTool
30refactoring
AbstractFigure
LocatorConnectionHandle
Locator
Figure
StylePalette
SequenceOfFigures
SimpleApplet
DrawingCanvas
CanvasTool
SimpleDrawingCanvas
ToolBar
SelectionTool
ConstructionTool
ToolPalette
LabelTool
ShapeTool
PrototypeConstructionTool
EllipseTool
RectangleTool
RectangularCreationTool
PG_RectImageTool
31splitting of roles in Drawlets
- the same method used in two different roles
- the same code can do both jobs
- the propagating change highlights the differences
- only one of the roles needs to be updated
- the other one can stay unchanged
- splitting the two roles and updating only one of
them shortens the propagation
32Splitting the roles
- function move(...) in AbstractFigure used in two
ways - to move the figure as requested by the user
- must check user identity
- as a part of the new figure creation
- does not need to check user identity
- We split move(...)and secureMove()
33Numerical data
- propagation refactoring splitting
- classes added 2 2 2
- interfaces modified 1 1 1
- classes modified 13 8 5
- LOC modified 91 95 87
34Conclusions Software Evolution
- Software evolution is a large part of software
engineering - Iterative program development is what software
engineers truly practice - Incremental change is based on
- Concepts location
- program dependencies change propagation
- Refactoring Fixing the architecture