+
The Dark Art of Refactoring
Systems Analysis and Design
Michael Heron
+
Introduction
 Refactoring is sometimes thought of as a vaguely ‘black art’ of
programming.
 That stems from a basic misunderstanding of what refactoring
is all about.
 Part of this impression comes from a sense of the subjectivity
that goes into refactoring.
 While it’s easy to identify code that is wrong, it’s less easy to
identify which is the ‘best’ out of bits of ‘good’ code.
 The ease at which a system can be refactored is influenced by
the way that system is designed.
+
Refactoring and the Impact of
Change
 Refactoring is an ‘invisible’ action.
 If you do it right, no-one should know you’ve done anything at
all.
 Refactoring is not about adding extra functionality.
 It may be a precursor to this.
 Refactoring is not about fixing bugs.
 Bugs may be fixed as a consequence of it.
 Refactoring is about fixing the problems caused by code you
‘would have done correctly’ given the opportunity.
+
Refactoring and the Impact of
Change
 Refactoring is highly dependent on the impact of change that
goes with your modifications.
 In object orientation, this is predicted by the visibility of your
methods and attributes.
 The benefit of techniques such as abstraction and
encapsulation can readily be seen when it comes time to
refactor code.
 Refactoring an abstracted, encapsulated method is easy.
 Refactoring a system with complex side-effects and distributed
processing is a nightmare.
 Refactoring allows us to redesign architecture in running
programs.
+
The Aim of Refactoring
When we refactor, we’re looking to improve
the quality of the code.
 Remove dead code
 Make inefficient code more efficient
 Make code more readable
 Self-describing code is the best documentation you can
provide.
 Make code more maintainable.
When we refactor, we try to get the code
looking like it would have done, if we’d have
written it properly the first time.
+
A Simple Example
 Consider the following simple class:
public class Example {
private int bing;
public int getValue() {
return bing;
}
public void setValue (int b) {
bing = b;
}
}
 Refactoring can be as simple as changing the name of a
variable to be more meaningful.
+
A Simple (?) Example
 public class Example {
public int bing;
public int getValue() {
return bing;
}
public void setValue (int b) {
bing = b;
}
}
 Depending on the impact of change, even changing a
variable name can be problematic.
+
Impact of Change
 There’s a simple hierarchy we can follow when working out the
impact of change of refactoring:
 Private variables and methods have low impact
 Protected variables and methods have medium impact
 Public variables and methods have high impact.
 Impact of Change relates to the maintainability of your code.
 How much of your code do you have to change?
 As developers, we strive to ensure minimum impact of change.
 You need to labour under the assumption that if someone has
access to a method or variable, they have taken advantage of that.
+
Impact of Change
 Structural elements of a system usually carry with them a high
impact of change.
 It’s usually safe to specialise, it’s usually not safe to generalise.
 In all cases, we want to refactor in such a way that our changes
have no impact on anyone else.
 Fellow developers, mainly.
 All of this relates to the code that we produce at the end of a
systems development project.
 But as a direct consequence is relates to our design.
 We don’t stop designing or analysing just because a system is
‘finished’
+
Another Example
 Let’s say we have a method to which we need to add a
parameter – say we need getValue to accept an integer
parameter:
public class Example {
private int bing;
public int getValue() {
return bing;
}
public void setValue (int b) {
bing = b;
}
}
+
Another Example
 How do we do this? There are two real choices:
 Add the parameter to the method definition.
 High impact of change – every other class making use of
getValue will need to change.
 Add in an overloaded method.
 Low impact of change.
 However, there’s a trade-off here:
 Adding a parameter may require lots of code to change.
 Adding an overloaded method may reduce internal
consistency.
+
Remit of Refactoring
Where does your remit for refactoring lie?
 It depends on how much of the code for which you
are responsible.
 This may extend over a whole program.
 It may extend over a handful of classes.
 You can unilaterally refactor only those elements of
the program for which you have responsibility.
There are few things more frustrating than
finding your programs no longer work
because of someone else’s refactoring…
+
A Third Example
 public class Example {
private int value;
public int makeDesposit (int value, int rate) {
if (value > 100) {
return -1;
}
else if (valid < 0) {
return -1;
}
else {
if (rate < 30) {
value = value * rate;
}
else {
value = value * rate;/2
}
}
}
return value;
}
}
+
Code Aesthetics
 The aesthetics of your code are important.
 They are usually a hint at the maintainability.
 However, it’s important not to just discard
complicated code as needing total refactoring.
 Old code may not be ugly, it may be battle-scarred.
 However, as you gain in experience and
confidence as a developer, you can generally tell
where the bits of code needing attention lie.
 Everyone has their own way of doing this – you
might investigate the Wodehouse Method of
Refactoring for one interesting example.
+
Code Aesthetics
 One way to improve the aesthetics is to break complicated
functionality out into separate methods.
 This fulfils a general rule of object oriented programming, in that
each method should have one responsibility only.
 Complicated and nested structures are usually a good warning
sign of the need to refactor.
 Your activity diagrams will help you understand this.
 Look for lots of loops, lots of branches.
 If you see complicated structures, seek to simplify them.
 Strive to reduce the cyclomatic complexity.
+
Cyclomatic Complexity
 Cyclomatic complexity is a software metric, and is used to
determine how complicated a piece of code is.
 It’s computed using the flow of logic through the constituent
paths of a unit.
 A function, a class, or a whole system.
 The higher the complexity, the more complicated the code.
 The more complicated the code, the harder it is to modify.
 Think of your complexity as ‘the number of decisions made in a
piece of source code’
 It’s more complicated than that, but that’s good enough for now.
+
A Third Example
 public class Example {
public int value;
private boolean getValid (int value) {
if (value > 100 || value < 0) {
return false;
}
return true;
}
private int getRate (int rate) {
int rate;
if (rate > 20 && rate < 30) {
rate = value;
}
else {
rate = value / 2;
}
return rate;
}
public int makeDeposit (int value, int rate) {
int rate;
if (getValid (value) == false) {
return -1;
}
rate = getRate (rate);
value = value * rate;
return value;
}
}
+
Some Common Structual
Refactoring Tasks
 Generalising classes.
 Specialising classes.
 Improving encapsulation.
 Lowering impact of change
 Merging subclasses into fields
 Extracting fields into subclasses
 Implementing interfaces
 Adding abstract classes
 Making better use of polymorphism.
 Reduce inconsistency.
+
Some Common Internal
Refactoring Tasks
 Simplifying internal structures.
 Improving variable names.
 Simplifying logical comparisons
 Substituting one algorithm for another
 Consolidating conditionals
 Extracting functionality into separate methods.
+
Refactoring and Test Driven
Development
 Refactoring introduces no new functionality.
 You can thus use the tests you have put in place previously.
 Having a comprehensive, full test-suite ensures that your
refactored code behaves identically to the previous code.
 The importance of that in a multi-developer environment cannot be
stressed enough.
 We’ve referred to test driven development in the past.
 We write the tests before we write the code, so that when we make
any change we can ensure the whole thing works.
 Refactoring doesn’t violate this rule.
 If it does, you’re not refactoring at all.
+
When Do We Refactor?
Refactoring is an ongoing process we do all
the time.
 Yeah, right.
The ideal case is that we refactor our code
on a continual basis. The realities of life
dictate that we must prioritise.
 Generally, we refactor code that is actively getting
in the way.
 There’s also often a ‘wish list’ we keep as
developers of code that we regret…
+
When Do We Refactor?
 We refactor when code ‘smells bad’
 Code is duplicated across locations.
 There are unjustifiable ‘god objects’
 When cohesion is too low
 When coupling is too high
 When people have been ‘too clever’ for their own good.
 http://www.soberit.hut.fi/mmantyla/BadCodeSme
llsTaxonomy.htm has a good list of ‘bad smells’
+
Conclusion
 Refactoring is the process of looking back at old designs and
fixing them before they become problems.
 Preventative maintenance, in other words.
 You are going to make mistakes when you develop a system.
 Nobody is perfect.
 The ease with which you can fix those mistakes is going to be
based on the designs that you produce.
 Think of refactoring as ‘design band aids’.
 Bearing this in mind at the design stage can save much
heartache in the long run.

SAD10 - Refactoring

  • 1.
    + The Dark Artof Refactoring Systems Analysis and Design Michael Heron
  • 2.
    + Introduction  Refactoring issometimes thought of as a vaguely ‘black art’ of programming.  That stems from a basic misunderstanding of what refactoring is all about.  Part of this impression comes from a sense of the subjectivity that goes into refactoring.  While it’s easy to identify code that is wrong, it’s less easy to identify which is the ‘best’ out of bits of ‘good’ code.  The ease at which a system can be refactored is influenced by the way that system is designed.
  • 3.
    + Refactoring and theImpact of Change  Refactoring is an ‘invisible’ action.  If you do it right, no-one should know you’ve done anything at all.  Refactoring is not about adding extra functionality.  It may be a precursor to this.  Refactoring is not about fixing bugs.  Bugs may be fixed as a consequence of it.  Refactoring is about fixing the problems caused by code you ‘would have done correctly’ given the opportunity.
  • 4.
    + Refactoring and theImpact of Change  Refactoring is highly dependent on the impact of change that goes with your modifications.  In object orientation, this is predicted by the visibility of your methods and attributes.  The benefit of techniques such as abstraction and encapsulation can readily be seen when it comes time to refactor code.  Refactoring an abstracted, encapsulated method is easy.  Refactoring a system with complex side-effects and distributed processing is a nightmare.  Refactoring allows us to redesign architecture in running programs.
  • 5.
    + The Aim ofRefactoring When we refactor, we’re looking to improve the quality of the code.  Remove dead code  Make inefficient code more efficient  Make code more readable  Self-describing code is the best documentation you can provide.  Make code more maintainable. When we refactor, we try to get the code looking like it would have done, if we’d have written it properly the first time.
  • 6.
    + A Simple Example Consider the following simple class: public class Example { private int bing; public int getValue() { return bing; } public void setValue (int b) { bing = b; } }  Refactoring can be as simple as changing the name of a variable to be more meaningful.
  • 7.
    + A Simple (?)Example  public class Example { public int bing; public int getValue() { return bing; } public void setValue (int b) { bing = b; } }  Depending on the impact of change, even changing a variable name can be problematic.
  • 8.
    + Impact of Change There’s a simple hierarchy we can follow when working out the impact of change of refactoring:  Private variables and methods have low impact  Protected variables and methods have medium impact  Public variables and methods have high impact.  Impact of Change relates to the maintainability of your code.  How much of your code do you have to change?  As developers, we strive to ensure minimum impact of change.  You need to labour under the assumption that if someone has access to a method or variable, they have taken advantage of that.
  • 9.
    + Impact of Change Structural elements of a system usually carry with them a high impact of change.  It’s usually safe to specialise, it’s usually not safe to generalise.  In all cases, we want to refactor in such a way that our changes have no impact on anyone else.  Fellow developers, mainly.  All of this relates to the code that we produce at the end of a systems development project.  But as a direct consequence is relates to our design.  We don’t stop designing or analysing just because a system is ‘finished’
  • 10.
    + Another Example  Let’ssay we have a method to which we need to add a parameter – say we need getValue to accept an integer parameter: public class Example { private int bing; public int getValue() { return bing; } public void setValue (int b) { bing = b; } }
  • 11.
    + Another Example  Howdo we do this? There are two real choices:  Add the parameter to the method definition.  High impact of change – every other class making use of getValue will need to change.  Add in an overloaded method.  Low impact of change.  However, there’s a trade-off here:  Adding a parameter may require lots of code to change.  Adding an overloaded method may reduce internal consistency.
  • 12.
    + Remit of Refactoring Wheredoes your remit for refactoring lie?  It depends on how much of the code for which you are responsible.  This may extend over a whole program.  It may extend over a handful of classes.  You can unilaterally refactor only those elements of the program for which you have responsibility. There are few things more frustrating than finding your programs no longer work because of someone else’s refactoring…
  • 13.
    + A Third Example public class Example { private int value; public int makeDesposit (int value, int rate) { if (value > 100) { return -1; } else if (valid < 0) { return -1; } else { if (rate < 30) { value = value * rate; } else { value = value * rate;/2 } } } return value; } }
  • 14.
    + Code Aesthetics  Theaesthetics of your code are important.  They are usually a hint at the maintainability.  However, it’s important not to just discard complicated code as needing total refactoring.  Old code may not be ugly, it may be battle-scarred.  However, as you gain in experience and confidence as a developer, you can generally tell where the bits of code needing attention lie.  Everyone has their own way of doing this – you might investigate the Wodehouse Method of Refactoring for one interesting example.
  • 15.
    + Code Aesthetics  Oneway to improve the aesthetics is to break complicated functionality out into separate methods.  This fulfils a general rule of object oriented programming, in that each method should have one responsibility only.  Complicated and nested structures are usually a good warning sign of the need to refactor.  Your activity diagrams will help you understand this.  Look for lots of loops, lots of branches.  If you see complicated structures, seek to simplify them.  Strive to reduce the cyclomatic complexity.
  • 16.
    + Cyclomatic Complexity  Cyclomaticcomplexity is a software metric, and is used to determine how complicated a piece of code is.  It’s computed using the flow of logic through the constituent paths of a unit.  A function, a class, or a whole system.  The higher the complexity, the more complicated the code.  The more complicated the code, the harder it is to modify.  Think of your complexity as ‘the number of decisions made in a piece of source code’  It’s more complicated than that, but that’s good enough for now.
  • 17.
    + A Third Example public class Example { public int value; private boolean getValid (int value) { if (value > 100 || value < 0) { return false; } return true; } private int getRate (int rate) { int rate; if (rate > 20 && rate < 30) { rate = value; } else { rate = value / 2; } return rate; } public int makeDeposit (int value, int rate) { int rate; if (getValid (value) == false) { return -1; } rate = getRate (rate); value = value * rate; return value; } }
  • 18.
    + Some Common Structual RefactoringTasks  Generalising classes.  Specialising classes.  Improving encapsulation.  Lowering impact of change  Merging subclasses into fields  Extracting fields into subclasses  Implementing interfaces  Adding abstract classes  Making better use of polymorphism.  Reduce inconsistency.
  • 19.
    + Some Common Internal RefactoringTasks  Simplifying internal structures.  Improving variable names.  Simplifying logical comparisons  Substituting one algorithm for another  Consolidating conditionals  Extracting functionality into separate methods.
  • 20.
    + Refactoring and TestDriven Development  Refactoring introduces no new functionality.  You can thus use the tests you have put in place previously.  Having a comprehensive, full test-suite ensures that your refactored code behaves identically to the previous code.  The importance of that in a multi-developer environment cannot be stressed enough.  We’ve referred to test driven development in the past.  We write the tests before we write the code, so that when we make any change we can ensure the whole thing works.  Refactoring doesn’t violate this rule.  If it does, you’re not refactoring at all.
  • 21.
    + When Do WeRefactor? Refactoring is an ongoing process we do all the time.  Yeah, right. The ideal case is that we refactor our code on a continual basis. The realities of life dictate that we must prioritise.  Generally, we refactor code that is actively getting in the way.  There’s also often a ‘wish list’ we keep as developers of code that we regret…
  • 22.
    + When Do WeRefactor?  We refactor when code ‘smells bad’  Code is duplicated across locations.  There are unjustifiable ‘god objects’  When cohesion is too low  When coupling is too high  When people have been ‘too clever’ for their own good.  http://www.soberit.hut.fi/mmantyla/BadCodeSme llsTaxonomy.htm has a good list of ‘bad smells’
  • 23.
    + Conclusion  Refactoring isthe process of looking back at old designs and fixing them before they become problems.  Preventative maintenance, in other words.  You are going to make mistakes when you develop a system.  Nobody is perfect.  The ease with which you can fix those mistakes is going to be based on the designs that you produce.  Think of refactoring as ‘design band aids’.  Bearing this in mind at the design stage can save much heartache in the long run.