chapter 7: bad code smells omar meqdadi se 3860 lecture 7 department of computer science and...
TRANSCRIPT
Chapter 7: Bad Code Smells
Omar Meqdadi
SE 3860 Lecture 7
Department of Computer Science and Software Engineering University of Wisconsin-Platteville
2
Topic Covered
Bad Code Smells Smells Categorization Smells Refactoring
Bad Code Smells
The most common design problems result from code because of bad code smells such as: Code duplication Unclear code Complicated Code
Bad Code Smells: Example
Why is this implementation bad? How can you improve it? Provide a list of improvements.
class Animal { int MAMMAL = 0, BIRD = 1, REPTILE = 2; int myKind; // set in constructor ... string getSkin() { switch (myKind) { case MAMMAL: return "hair"; case BIRD: return "feathers"; case REPTILE: return "scales"; default: return "integument"; } }}
5
Bad Code Smells: Example
Bad Implementation because of switch statements are very rare in properly designed object-
oriented code A switch statement should not be used to distinguish
between various kinds of object
Code improvement solution Refactoring : the simplest is the creation of subclasses
Bad Code Smells: Example
Improved code:
class Animal { string getSkin() { return "integument"; } } class Mammal extends Animal { string getSkin() { return "hair"; } } class Bird extends Animal { string getSkin() { return "feathers"; } } class Reptile extends Animal { string getSkin() { return "scales"; } }
Bad Code Smells: Example
How is this an improvement? Adding a new animal type, such as Insect, does not require
revising and recompiling existing code Mammals, birds, and reptiles are likely to differ in other
ways, and we’ve already separated them out (so we won’t need more switch statements)
we’re now using Objects the way they were meant to be used
Bad Code Smells: Types
Duplicate code Long method Conditional Complexity Data Class Solution Sprawl Switch statements Large class Lazy class Combinatorial Explosion Long Parameter List Data Clumps Comments
Bad Code Smells: Categorization
Bloaters Something that has grown so large that it cannot be effectively handled
Object-Orientation Abusers Cases where the solution does not fully exploit the possibilities of
object-oriented design
Change Preventers Smells that hinder changing or further developing the software.
Dispensable Something unnecessary that should be removed from the source code
Couplers: coupling-related smells
Bad Code Smells: Indications
Frequent failures Overly complex structure Very large components Excessive resource requirements Deficient documentation High personnel turnover Different technologies in one system
Bad Code Smells: Solution
Resolving the code smells is a Reengineering Process Steps:
Program Comprehension (Understanding) Refactoring
A set of transformations that are guaranteed to preserve the behavior while they can remove bad code smells
Refining
Bad Code Smells Refactoring
Duplicate Code
Duplicate Code (Code Clones): The same, or very similar code, appears in many places
Problems: Bug introducing Makes code larger that it needs to be
Duplicate Code
Case1: occurs within a single class Refactoring: Extract
Method Turn the fragment into
a method whose name explains the purpose of the method
Any local variables that the method requires can be passed as parameters
int sum1 = 0;
int sum2 = 0;
int average1 = 0;
int average2 = 0;
for (int i = 0; i < size1; i++){
sum1 += array1[i]; }
average1 = sum1/size1;
for (int i = 0; i < size2; i++){
sum2 += array2[i];
}
average2 = sum2/size2;
int calcAverage (int A[] , int size){
int sum = 0;
for (int i = 0; i < size; i++) {
sum += A[i]; }
return sum/size;}
Duplicate Code
Case2: occurs in sibling classes Refactoring:
Use Extract Method in each class Be sure the code is identical If necessary, adjust the method signatures to be identical Pull Up Method :Copy the extracted method to the common
superclass Delete the method from subclasses
Duplicate Code
Example: consider the ocean scenario: Fish move about randomly A big fish can move to where
a little fish is (and eat it) A little fish will not move to where
a big fish is
General move method:public void move() { choose a random direction; // same for both find the location in that direction; // same for both check if it’s ok to move there; // different if it’s ok, make the move; // same for both}
BigFish
move()
Fish
<<abstract>>move()
LittleFish
move()
Duplicate Code
Refactoring solution: Extract the check on whether it’s ok to moveIn the Fish class, put the actual move() methodCreate an abstract okToMove() method in the Fish class Implement okToMove() in each subclass
BigFishokToMove(locn):boolean
Fishmove()
<<abstract>>okToMove(locn):boolean
BigFishokToMove(locn):boolean
Duplicate Code
Case3: occurs in unrelated classes Refactoring Solution1:
Use Extract Method in one class Then , invoke the method from the other class
Refactoring Solution2: Use Extract Superclass if that is possible
Duplicate Code: Example
class Car{private:int mpg;double fuelInTank;public:Car() { mpg = 25; fuelInTank = 5.056;}void MoreGas(double g){fuelInTank = fuelInTank + g;}void move(double miles){double used = miles / mpg;fuelInTank = fuelInTank - used;}}
class Van{private:int mpg;double fuelInTank;public:Van() { mpg = 20; fuelInTank = 9.056;}void MoreGas(double g){fuelInTank = fuelInTank + g;}void move(double miles){double used = miles / mpg;fuelInTank = fuelInTank - used;}}
class Vehicle{private:int mpg;double fuelInTank;public:void MoreGas(double g){fuelInTank = fuelInTank + g;}void move(double miles){double used = miles / mpg;fuelInTank = fuelInTank - used;}}
class Car: public Vehicle {public:Car() { mpg = 25; fuelInTank = 5.056;}}class Van: public Vehicle {public:Van() { mpg = 20; fuelInTank = 9.056;}}
Long Method
Long method Over 20 LOC is usually bad. Under 10 lines is typically good. Problems: Difficult to
Understand Change Reuse
Refactoring Solution: Method Extraction Take portions of code from inside long method, and make a
new method Then, Call new method inside the now-not-so-long method
Long Method
Example:
void methodA(){Statement 1;Statement 2;..Statement 30;
}
void methodA(){Statement 1;Statement 2;..Statement 30;
}
private void m1(){Statement 1;..Statement 9;
}
private void m2(){Statement 10;..Statement 19;
}
private void m3(){Statement 20;..Statement 30;
} public void methodA(){ m1(); m2(); m3(); }
private void m1(){Statement 1;..Statement 9;
}
private void m2(){Statement 10;..Statement 19;
}
private void m3(){Statement 20;..Statement 30;
} public void methodA(){ m1(); m2(); m3(); }
Feature Envy
Feature Envy A method in one class uses primarily data and methods
from another class to perform its work Indicates the method was incorrectly placed in the wrong
class Problems:
High class coupling Difficult to change , understand, and reuse
Refactoring Solution:Extract Method & Method Movement Move the method with feature envy to the class containing
the most frequently used methods and data items
Feature Envy
Example: Method updateItemPanel
is defined in class
OrderItemPanel, but the
method interests in class
ItemPanel
class OrderItemPanel { private: itemPanel _itemPanel; void updateItemPanel( ) { Item item = getItem(); int quant = getQuantity( ); if (item == null) _itemPanel.clear( ); else{ _itemPanel.setItem(item); _itemPanel.setInstock(quant); }} }
Feature Envy
Example: Refactoring solution:
Extract method doUpdate
in class OrderItemPanel Move method doUpdate
to class ItemPanel
class OrderItemPanel { private: itemPanel _itemPanel; void updateItemPanel( ) { Item item = getItem(); int quant = getQuantity( ); _itemPanel.doUpdate(item, quant);}} class ItemPanel {public: void doUpdate(Item item, int quantity){ if (item == null) clear( ); else{ setItem(item); setInstock(quantity); }} }
Large Classes
Large Classes Classes try to do too much (many methods and data
members) Problems:
Low class cohesion Difficult to change and understand Bug introducing
Refactoring Solution: Class Extraction
Take a subset of the instance variables and methods and create a new class with them
Method Movement
PhoneNumber
Customer
nameareaCodenumber
String: getPhoneNumber()
PhoneNumber: getPhoneNumber()
Large Classes
Switch Statements
Switch Statements The cases in a switch statement contain logic for different
types of instances of the same class In object-oriented code, this indicates new subclasses should be
created Problems:
Introducing code clones The same switch/case structure appears in many places
Refactoring Solution: Create new subclasses Extract method to move case block logic into methods on the
new subclasses
Switch Statements
Example:class Animal { int MAMMAL = 0, BIRD = 1, REPTILE = 2; int myKind; // set in constructor ... string getSkin() { switch (myKind) { case MAMMAL: return "hair"; case BIRD: return "feathers"; case REPTILE: return "scales"; default: return “skin"; } }}
class Animal { string getSkin() { return “skin"; }
}class Mammal : public Animal {
string getSkin() { return "hair"; }}class Bird: public Animal {
string getSkin() { return "feathers"; }}class Reptile : public Animal {
string getSkin() { return "scales"; }}
After Refactoring
Data Classes
Data Classes A class that has only class variables, get/set methods Acting as a data holder only Problems:
Other classes have methods with feature envy That is, there are usually other methods that primarily
manipulate data in the data class Indicates these methods should really be on the data class
Refactoring Solution: Method Movement Find methods that use data class attributes, and then
move them to the data class
Data Classes
Example: After Refactoring
Long Parameter List
Long Parameter List Many parameters passed into a method Problems:
Hard to understand , change, and reuse Refactoring Solution:
Replace Parameter with Method Introduce Parameter Object Preserve Whole Object
Long Parameter List
Example: void paint (Graphics gr, double x, double y, double width, double height, bool shouldValidate) {.....} Caller side: public void someMethod(Graphics gr){ double x = 100; double y = 200; double width = 100; double height = width/2; paint(gr, x, y, width, height, true);}
void paint
(Graphics gr, Shape rec , bool shouldValidate)
{.....}
Caller side:
void someMethod(Graphics gr){
double x = 100;
double y = 200;
double width = 100;
double height = width/2;
Shape rect = new Shape( x, y, width, height);
paint(gr, rect, true);
}
After Refactoring
Shotgun Surgery
Shotgun Surgery Making one change requires changing code in multiple
places Problems:
Hard to change Refactoring Solution:
Method Movement Data members(fields) movement Inline classes
Lazy Class
Lazy Class A class just doesn’t do enough Refactoring Solution:
If a class is very similar to its superclass, you can try to use Collapse Hierarchy to merge the two classes
Use Inline class If a class just isn’t doing very much, move all its features into
another class with Move Field and Move Method
Lazy Class
Example: Salesman and Employee are not very different So , merge them
Data Clumps
Data Clumps Sets of variables usually passed together in multiple places Refactoring Solution:
Extract Classes Introduce Parameter Object
Data Clumps
Example:
bool SubmitCreditCardOrder (string creditCardNumber, int expirationMonth, int expirationYear, double saleAmount){ }
bool Isvalid (string creditCardNumber, int expirationMonth, int expirationYear){ }
bool Refund(string creditCardNumber, int expirationMonth, int expirationYear, double Amount){ }
class CreditCard {
private:
string creditCardNumber;,
int expirationMonth;
int expirationYear;
};
bool SubmitCreditCardOrder ( CreditCard card, double saleAmount){ }
bool Isvalid (CreditCard card){ }
bool Refund(CreditCard card , double Amount){ }
After Refactoring
Middle Man
Middle Man Delegation : one class simply delegates many of its
requests to another class Refactoring Solution:
Replace Delegation with Inheritance Use Remove Middle Man and talk to the object that really
knows what is going on Use Inline Method to absorb a few small methods into the
caller
Middle Man
Example: using Inline Method
int getRating() { return (moreThanFiveLateDeliveries()) ? 2 : 1; }
boolean moreThanFiveLateDeliveries() { return _numberOfLateDeliveries > 5; }
int getRating() { return (moreThanFiveLateDeliveries()) ? 2 : 1; }
boolean moreThanFiveLateDeliveries() { return _numberOfLateDeliveries > 5; }
int getRating() { return (_numberOfLateDeliveries > 5) ? 2 : 1;
}
int getRating() { return (_numberOfLateDeliveries > 5) ? 2 : 1;
}
Temporary Field
Temporary Field We expect an object to use all its fields. However, it’s a
problem when a field is used only in certain cases Or , a case in which a variable is in the class scope, when it
should be in method scope Refactoring Solution:
Use Extract Class to create a home for these variables
LaborItem
JobItem
getTotalPrice()getUnitPrice()getEmployee()
Do all the JobItem objects need have getEmployee function?
getUnitPrice()
Temporary Field
Refused Bequest
Refused bequest Subclasses may inherit unwanted methods from their
superclasses Refactoring Solution:
Extract Classes and Move Methods Create a new subclass and use method movement on the
unused methods Replace inheritance with delegation
Replace inheritance with delegation A subclass uses only part of a superclasses interface or does not
want to inherit data. Create a field for the superclass, adjust methods to delegate to the
superclass, and remove the subclassing.
Refused Bequest
Comments
Comments Often used as deodorant for other smells
may indicate areas where the code is not as clear as it could be
Refactoring Solution: Extract Method: if you need a comment to explain what a
block of code does Rename Method: if you need a comment to explain what a
method does Introduce Assertion: if you need to describe the required
state of the system
Comments
Example:
void PrintBill ( Customer C , double Amount){
PrintBanner();
string name = c.getname();
// print details
cout << “name: ” << name << endl;
cout << ”amount: ” << Amount << endl;
}
void PrintBill ( Customer C , double Amount){
PrintBanner();
PrintDetails (c.getname() , Amount);
}
void PrintDetails(string name , double amount){
cout << “name: ” << name << endl;
cout << ”amount: ” << Amount << endl;
}
Comment deleted , temp variable removed
After Extract Method Refactoring