View
5.274
Download
3
Category
Preview:
DESCRIPTION
Citation preview
© S G Ganesh, Girish Suryanarayana, Tushar Sharma
Refactoring for Design Smells - With Examples from OpenJDK
S G Ganesh Tushar Sharma
Girish Suryanarayana
© 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
© 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 …
© 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]
© S G Ganesh, Girish Suryanarayana, Tushar Sharma
“Design smells” aka…
9-May-13
© S G Ganesh, Girish Suryanarayana, Tushar Sharma
Symptoms of rotting design
9-May-13
© 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,...
© 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
© 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
© 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
© 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
© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 12
Abstraction smells
9-May-13
© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 13
Encapsulation smells
9-May-13
© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 14
Modularization smells
9-May-13
© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 15
Hierarchy smells
9-May-13
© 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)
© 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
© 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
© 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
© 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!
© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 21
Can you find this smell in this GoF pattern?
9-May-13
© 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
© 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
© 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
}
© 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();
}
© 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).
© 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
© 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.
© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 29
Abstraction smells
9-May-13
© 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
© 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.)
© 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
© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 33
Encapsulation smells
9-May-13
© 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
© 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
© 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!
© 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”)
© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 38
Cyclic-dependency modularization
9-May-13
Cyclic hierarchy
Cyclic references
Central references
© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 39
Modularization smells
9-May-13
© 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
© 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
© 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!)
© 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.
© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 44
Complex hierarchy – Example
9-May-13
© 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
© 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.
© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 47
Broken hierarchy – Example
9-May-13
Polygonal hierarchy
Broken hierarchy
© 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
© 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''.
© 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.
© 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
© 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
© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 53
References
9-May-13
© S G Ganesh, Girish Suryanarayana, Tushar SharmaSlide 54
Thank you!
9-May-13
Recommended