54
© S G Ganesh, Girish Suryanarayana, Tushar Sharma Refactoring for Design Smells - With Examples from OpenJDK S G Ganesh Tushar Sharma Girish Suryanarayana

Refactoring for design smells

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar Sharma

Refactoring for Design Smells - With Examples from OpenJDK

S G Ganesh Tushar Sharma

Girish Suryanarayana

Page 2: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 2

Outline

9-May-13

Introduction

Abstraction smells

Encapsulation smells

Modularization smells

Hierarchy smells

Key takeaways and conclusion

Page 3: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 3

Why care about smells?

• Quality Attributes Reusability Flexibility Understandability Extendibility …

9-May-13

Product Quality

Design Quality

Design

Smells

Maintainability: Affected by flexibility & extendibility

Reliability: Impacted by poor understandability

Indicators Rigidity & Fragility Immobility & Opacity Needless complexity Needless repetition …

Page 4: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar Sharma

What is a smell?

Smells have been defined differentlyWe embrace all the following definitions!

9-May-13

“We suggest that, like any living creature, system designs are subject to diseases, which are design smells (code smells and anti-patterns). Design smells are conjectured in the literature to impact the quality and life of systems.” – Hassaine et al. [HKGH10]

“Smells are certain structures in the code that suggest (sometimes they scream for) the possibility of refactoring.” – M. Fowler [Fow99]

“Code and design smells are poor solutions to recurring implementation and design problems.” – Moha et. al. [MGDM10]

“Design smells are the odors of rotting software.” – R C Martin [Mar03]

Page 5: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar Sharma

“Design smells” aka…

9-May-13

Page 6: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar Sharma

Symptoms of rotting design

9-May-13

Page 7: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar Sharma

Why care about design quality?

9-May-13

* http://sqgne.org/presentations/2012-13/Jones-Sep-2012.pdf

Design quality is important

• Our organization develops critical, large, and/or reusable software

Design errors are costly

• Capers Jones*: Up to 64% of software defects can be traced back to errors in software design in enterprise software!

A good designer is one who knows the design solutions

A great designer is one who understands the impact of design defects/smells and

knows how to address them

Design quality means: flexibility, extensibility, understandability, reusability,...

Page 8: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar Sharma

Capers Jones on design errors in industrial software

9-May-13* http://sqgne.org/presentations/2012-13/Jones-Sep-2012.pdf

Page 9: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 9

So, what causes design smells?

9-May-13

Design is impacted by changing requirements. Often, a holistic approach is not adopted resulting in different parts of design evolving in isolation

Changing requirements

Either design principles are not applied or are applied wrongly

Misapplication of design principles

Refactoring that is done incompletely or inappropriately

Lack of proper refactoring

Often teams lack competent resources or the budget for supporting the above practices

Lack of competent resources

Page 10: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 10

Smells as violations of fundamental principles

9-May-13

Why does smell indicate something problematic? Because they indicate violation of fundamental design principles

We use Booch’s fundamental principles for classification and naming of smells

This helps identify cause of the smell and potential refactoring as well

Page 11: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 11

Principle-based approach to design smells

9-May-13

This presentation on design smells is based on research work completed over a period of two years

The focus of our work is only on structural design smells

Published in Journal of Object Technology (Vol. 12, No. 2, 2013)

S G Ganesh, Tushar Sharma, Girish Suryanarayana. Towards a Principle-based Classification of Structural Design Smells. In Journal of Object Technology, vol. 12, no. 2, 2013, pages 1:1–29.doi:10.5381/jot.2013.12.2.a1URL: http://www.jot.fm/issues/issue_2013_06/article1.pdf (open access)

Creational Structural Behavioral

Architectural

Design

Implementation

Page 12: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 12

Abstraction smells

9-May-13

Page 13: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 13

Encapsulation smells

9-May-13

Page 14: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 14

Modularization smells

9-May-13

Page 15: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 15

Hierarchy smells

9-May-13

Page 16: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 16

A note on examples in this presentation

9-May-13

Only around 50% of the smells are described with examplesCannot cover all of them in detail due to lack of time

All examples are from OpenJDK 7.0 (open source)All illustrations are mostly as UML diagrams so no need to know Java (though you’ll appreciate more if you know Java)

Page 17: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaApril 10, 2023 Slide 17

How smells are related to technical debt?

“When, due to constraints, I design quickly and dirty, my project is loaded with technical debt”

– Ward Cunningham, 1992

Page 18: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 18

Outline

9-May-13

Introduction

Abstraction smells

Encapsulation smells

Modularization smells

Hierarchy smells

Key takeaways and conclusion

Page 19: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 19

Incomplete abstraction

9-May-13

This smell arises when a type does not support a responsibility completely

Specifically, the public interface of the type is incomplete in that it does not support all behavior needed by objects of its type

Page 20: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 20

Incomplete abstraction – Example

9-May-13

In this case, the MutableTreeNode supports only setUserObject but no corresponding getUserObject (which is provided in its derived class!)

Hence, MutableTreeNode has Incomplete Abstraction smell

How to fix it? Provide all the necessary and relevant methods required for satisfying a responsibility completely in the class itself

In case of public APIs (as in this case), it is often “too late” to fix it!

Page 21: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 21

Can you find this smell in this GoF pattern?

9-May-13

Page 22: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 22

How to refactor & in future avoid this smell?

9-May-13

For each abstraction (especially in public interface) look out for symmetrical methods or methods that go together

For example, methods for comparing equality of objects and getting hash code (in Java/C#) Look out for missing matching methods in symmetrical methods (see table)

min/max open/close create/destroy get/set

read/write print/scan first/last begin/end

start/stop lock/unlock show/hide up/down

source/target insert/delete first/last push/pull

enable/disable acquire/release left/right on/off

Page 23: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 23

Missing abstraction

9-May-13

This smell arises when an entity that ideally should be a first class abstraction is not represented using an abstraction. Two variants:

a) Using a primitive type or a group of primitive types instead of creating a class or interface b) Using string(s) for encoding values instead of creating a class or interface

Page 24: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 24

Missing abstraction – Example

9-May-13

From 1.0 version of Java, the stack trace was printed to the standard output as a string with printStackTrace() method

Programs that needed programmatic access to the stack trace elements had to write code to process the stack trace, for example, to get the line numbers, or find if the bug is a duplicate of another already filed bug. So what?

public class Throwable {

// following method is available from

// Java 1.0 version.

// Prints the stack trace as a string

// to standard output

// For processing a stack trace, we need

// to write regular expressions

public void printStackTrace();    

    // other methods elided

}

Page 25: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 25

Missing abstraction – Example

9-May-13

So what is the problem with this?Such programs depend on the format of the elements in the string. If the format of the elements in the stack trace is changed, it would break the existing code that "parses" the stack trace and depended on that string format.

Solution? Java 1.4 provided programmatic access to the stack trace through the introduction of StackTraceElement classThis fixed the “missing abstraction” smell!

public class Throwable {

public void printStackTrace();    

// Since 1.4

public StackTraceElement[] getStackTrace();  

// other methods elided

}

// Since 1.4

public final class StackTraceElement {

public String getFileName();

public int getLineNumber();

public String getClassName();

public String getMethodName();

public boolean isNativeMethod();

}

Page 26: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 26

Multifaceted abstraction

9-May-13

This smell arises when an abstraction has more than one responsibility assigned to it (violating Single Responsibility Principle).

Page 27: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 27

Multifaceted abstraction – Example

9-May-13

java.sql.DataTruncation serves as either an exception or a warning depending upon the context where it is used!

to indicate problems in “write” operations while executing SQL queries, it is thrown as an exception object to the callerto indicate problems in “read” operations while executing SQL queries, the DataTruncation object will not be thrown but will be attached to other objects so that the callers can retrieve it

Page 28: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 28

Multifaceted abstraction – Example

9-May-13

Suggested refactoring: Split the current DataTruncation class into two separate classes.

The DataTruncationWarning class would inherit from the SQLWarning class and would be used to indicate problems in the “read” operations. The DataTruncationException class would inherit from the SQLException class and be used to indicate problems in the “write” operations.

Page 29: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 29

Abstraction smells

9-May-13

Page 30: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 30

Outline

9-May-13

Introduction

Abstraction smells

Encapsulation smells

Modularization smells

Hierarchy smells

Key takeaways and conclusion

Page 31: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 31

Lenient encapsulation

9-May-13

This design smell arises when the hiding is deficient so that clients can access the implementation aspects of the abstraction directly

(An abstraction should expose only the interface to the clients and hide the implementation details.)

Page 32: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 32

Lenient encapsulation – Example

9-May-13

The java.awt.Rectangle class has 4 pubic fields: x, y, width, and height in addition to public getter and setter methods for these fields!

Smell since the accessibility of its members is more permissive (i.e., are declared public) than actually required (i.e., they are required to be declared private)

Refactoring suggestion: Make all data members private

Lenient encapsulation

Insufficient modularization

Page 33: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 33

Encapsulation smells

9-May-13

Page 34: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 34

Outline

9-May-13

Introduction

Abstraction smells

Encapsulation smells

Modularization smells

Hierarchy smells

Key takeaways and conclusion

Page 35: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 35

Insufficient modularization

9-May-13

This smell arises when an existing abstraction could be further decomposed thereby reducing its interface size, implementation complexity or both. Two variants: a) When an abstraction has a large number of members in its interface, its implementation, or bothb) When an abstraction has one or more methods with excessive complexity

Page 36: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 36

Insufficient modularization – Example

9-May-13

D:\Tools\OpenJDK\jdk\src\share\classes\j

The abstract class java.awt.Component is an example of insufficient modularization

It is a massive class with 332 methods (of which 259 are public!) 11 nested/inner classes107 fields (including constants)source file spans 10,102 lines of code!

The Component serves as a base class and the hierarchy is deep

Derived classes inherit the members => life is quite difficult!

Page 37: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 37

Cyclic-dependency modularization

9-May-13

This smell arises when two or more class-level abstractions depend on each other directly or indirectly (creating a tight coupling among the abstractions). (This smell is commonly known as “cyclic dependencies”)

Page 38: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 38

Cyclic-dependency modularization

9-May-13

Cyclic hierarchy

Cyclic references

Central references

Page 39: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 39

Modularization smells

9-May-13

Page 40: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 40

Outline

9-May-13

Introduction

Abstraction smells

Encapsulation smells

Modularization smells

Hierarchy smells

Key takeaways and conclusion

Page 41: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 41

Polygonal hierarchy

9-May-13

This smell arises when a base abstraction is repeatedly inherited in descendant abstraction(s) forming “polygons” in the inheritance graph. Two forms: a) Polygonal hierarchy of interfaces – leads to a cluttering of the inheritance

hierarchy

b) Polygonal hierarchy of implementation – with multiple implementation inheritance, common base abstractions get duplicated

Page 42: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 42

Polygonal hierarchy – Example

9-May-13

There are three (not two!) polygons in this hierarchy

This repeated inheritance complicates the hierarchyFor example, by extending AbstractQueue, ConcurrentLinkedQueue already implements Queue, and hence it is redundant for the ConcurrentLinkedQueue to explicitly implement Queue

Redundant inheritance path can be safely removed (without affecting clients!)

Page 43: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 43

Complex hierarchy

9-May-13

This smell arises when the inheritance graph is tangled, or excessively wide, or deep, or skewed.

Page 44: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 44

Complex hierarchy – Example

9-May-13

Page 45: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 45

Complex hierarchy – Example

9-May-13

What is the problem with this hierarchy?

It is 8 levels deep! the class has 15 base types; each of these types potentially introduces or overrides methods; hence it is difficult to understand methods available in the DatagramChannel and its semanticsan abstract class, which means it needs to be extended!

Summary: cognitively difficult task for users to extend and use the class

Page 46: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 46

Broken hierarchy

9-May-13

This smell arises when the base abstraction and its derived abstraction(s) conceptually do not share “IS-A” relationship (resulting in broken substitutability).

This design smell arises when inheritance is used wrongly instead of using composition.

Page 47: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 47

Broken hierarchy – Example

9-May-13

Polygonal hierarchy

Broken hierarchy

Page 48: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 48

Broken hierarchy – Example

9-May-13

A Stack is not a Vector, and hence this inheritance relationship is an example of broken hierarchy

Because of this design mistake, it is possible to insert or remove elements anywhere from a Stack, since Vector allows such methods and Stack inherits them!

If the subclass inherits from the super class for reusing the functionality, apply Fowler’s “replace inheritance with delegation” refactoring

Stack-Vector example fits this requirement and hence can be suitably refactored using this technique

Page 49: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 49

Cyclic hierarchy

9-May-13

This smell arises when a supertype refers to any of its subtypes. Here the term ``reference'' means:

(a) a supertype contains an object of one of its subtypes (b) a supertype refers to the type name of one of its subtypes (c) a supertype accesses data members or calls methods of one of

its subtypes.

Such a reference can be either direct or indirect. Since subtype knowledge introduces a cycle in the inheritance graph annotated with class relationships, this smell is termed as ``cyclic hierarchy''.

Page 50: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 50

Cyclic hierarchy – Example

9-May-13

The private modInverse() method in MutableBigInteger class implements an algorithm that requires the use of signed integer and hence it creates instances of SignedMutableBigInteger; this instance creation introduces a reference from the supertype method to the subtype resulting in cyclic hierarchy smell.

One potential refactoring for addressing this smell is to re-implement the modInverse() method (say, using an alternative algorithm if available) in such a way that it does not require creating instances of SignedMutableBigInteger.

Page 51: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 51

Outline

9-May-13

Introduction

Abstraction smells

Encapsulation smells

Modularization smells

Hierarchy smells

Key takeaways and conclusion

Page 52: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 52

Key takeaways and conclusion

9-May-13

Design quality is important in our organizational context (where we develop large, complex, and/or reusable software)

A great designer is one who understands the impact of design defects/smells and knows how to address them

Design smells are candidates for refactoring

Design smells can be viewed as violation of underlying fundamental design principles

Some design smells can’t be fixedFor such smells, the only way is to avoid introducing them in the first place!

Context is important for smellsGiven the liability of a smell, in certain contexts, a designer may make a conscious decision to live with that smell

Page 53: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 53

References

9-May-13

Page 54: Refactoring for design smells

© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 54

Thank you!

9-May-13