Refactoring For DesignSmells 2021
Refactoring For DesignSmells 2021
Introduction
Abstraction smells
Encapsulation smells
Modularization smells
Hierarchy smells
100
Percentage Contribution
80
Adminstrative Errors
Documentation Errors
60
Bad Fixes
Coding Errors
40 Design Errors
Requirements Errors
20
0
IBM SPR TRW MITRE Nippon Electric
Corportation Corporation Corporation Corporation Corp
(MVS) (Client Studies)
Software Technical
Quality Debt
Documentatio
Code debt Design debt Test debt
n debt
No
Static analysis Lack of tests documentation
…
Design smells
tool violations for important
concerns
“Design smells are certain structures in the design that indicate violation
of fundamental design principles and negatively impact design quality.”
Impacted Quality
Desig
▪ Reusability
n
▪ Changeability Qualit
▪ Understandability y
Product Impacted Quality
▪ Extensibility
Quality ▪ Reliability: Impacted by
▪ …
poor understandability
▪ Performance: Impacted
by high complexity
▪ Maintainability: Affected
by changeability &
Restricted © Siemens AG 2018
extensibility
Page 9 04.06.2018 Slide 9 ▪ … CT RDA SSI RSE-IN
What causes design smells?
A good designer is
one who knows the
design solutions
Quality
Definition
Attribute
Understandability The ease with which the design fragment can be comprehended.
The ease with which a design fragment can be modified (without causing ripple
Changeability
effects) when an existing functionality is changed.
The ease with which a design fragment can be enhanced or extended (without
Extensibility
ripple effects) for supporting new functionality.
The ease with which a design fragment can be used in a problem context other
Reusability
than the one for which the design fragment was originally developed.
The ease with which a design fragment supports the detection of defects within
Testability
it via testing.
The extent to which the design fragment supports the correct realization of the
Reliability
functionality and helps guard against the introduction of runtime problems.
Restricted © Siemens AG 2018
Page 21 04.06.2018 Slide 21 CT RDA SSI RSE-IN
Restricted © Siemens AG 2018
Page 22 04.06.2018 Slide 22 CT RDA SSI RSE-IN
A note on smells and examples in this presentation
Introduction
Abstraction smells
Encapsulation smells
Modularization smells
Hierarchy smells
DefaultButton Model
+getGroup():ButtonGroup
… other methods
Imagine a car
with an
accelerator but
without a brake!
For each abstraction (especially in public interface) look out for symmetrical
methods or methods that go together
Look out for missing matching methods in symmetrical methods (see table)
This smell manifests as a class that has only one method defined within the
class. At times, the class name itself may be identical to the one method
defined within it.
org.eclipse.jdt.internal org.eclipse.jdt.
.compiler.parser core.compiler
About 3500 lines of code (i.e. 99% of the code) are identical
across these two classes!
This smell arises when two or more abstractions have identical names or
identical implementation or both.
▪ “Avoid duplication”
1. Change name of one of the
abstractions
2. Factor out the commonality into a
third abstraction which can be used
by the original abstractions
3. Remove a duplication from one of
the abstractions and make it refer
to the other abstraction for that
particular functionality
This class is used by Formatter class, and holds three flag values used
in format specifiers, namely, “-“, “S” and “#”. These flags are encoded
as integer values 1, 2, and 4.
Restricted © Siemens AG 2018
Page 39 04.06.2018 Slide 39 CT RDA SSI RSE-IN
Unnecessary Abstraction
The smell occurs when an abstraction which is actually not needed (and thus
could have been avoided) gets introduced in a software design
This smell arises when clumps of data or encoded strings are used
instead of creating a class or an interface.
Restricted © Siemens AG 2018 Caution – Check if there is behavior associated with data
Page 45 04.06.2018 Slide 45 CT RDA SSI RSE-IN
In addition to methods
supporting dates, Calendar
class has methods for
supporting time as well!
java.util.Calendar
This smell arises when an abstraction has more than one responsibility
assigned to it.
This smell arises when an abstraction is left unused (either not directly
used or not reachable). Two forms:
• Unreferenced abstractions – Concrete classes that are not being used
by anyone
• Orphan abstractions – Stand-alone interfaces/abstract classes that do
not have any derived abstractions
Introduction
Abstraction smells
Encapsulation smells
Modularization smells
Hierarchy smells
This design smell occurs when the declared accessibility of one or more
members of a class is more permissive than actually required.
(An abstraction should expose only the interface to the clients and hide
the implementation details.)
Lenient
encapsulation
Insufficient
Restricted © Siemens AG 2018 modularization
Page 58 04.06.2018 Slide 58 CT RDA SSI RSE-IN
Deficient Encapsulation – Caution
Nested classes
non-static nested class (i.e. an inner class) has access to all the
members (including private members) of its enclosing class
Caution – When you are aware that new variations will not emerge, a
refactoring may not be required. It could result in over-engineering.
Restricted © Siemens AG 2018
Page 68 04.06.2018 Slide 68 CT RDA SSI RSE-IN
switch (transferType) {
case DataBuffer.TYPE_BYTE:
byte bdata[] = (byte[])inData; Data
pixel = bdata[0] & 0xff; Buffer
length = bdata.length;
break;
case DataBuffer.TYPE_USHORT:
short sdata[] = (short[])inData;
pixel = sdata[0] & 0xffff;
length = sdata.length;
DataBuffer DataBuffer DataBuffer
break;
Byte UShort Int
case DataBuffer.TYPE_INT:
int idata[] = (int[])inData;
pixel = idata[0];
length = idata.length;
break;
default:
throw new UnsupportedOperationException("This method has not been
"+ "implemented for transferType " + transferType);
}
This smell arises when client code uses explicit type checks (using chained if-
else or switch statements that check for the type of the object) instead of
exploiting the variation in types already encapsulated within a hierarchy.
switch (transferType) {
case DataBuffer.TYPE_BYTE:
byte bdata[] = (byte[])inData;
pixel = bdata[0] & 0xff;
length = bdata.length;
break;
case DataBuffer.TYPE_USHORT:
short sdata[] = (short[])inData; pixel = dataBuffer.getPixel();
pixel = sdata[0] & 0xffff;
length = sdata.length;
length = dataBuffer.getSize();
break;
case DataBuffer.TYPE_INT:
int idata[] = (int[])inData;
pixel = idata[0];
length = idata.length;
break;
default:
throw new
UnsupportedOperationException("This method
has not been "+ "implemented for transferType "
Restricted © Siemens AG 2018 + transferType);
Page 71 }
04.06.2018 Slide 71 CT RDA SSI RSE-IN
Restricted © Siemens AG 2018
Page 72 04.06.2018 Slide 72 CT RDA SSI RSE-IN
Outline
Introduction
Abstraction smells
Encapsulation smells
Modularization smells
Hierarchy smells
This smell arises when members of an abstraction are broken and spread
across multiple abstractions (when ideally they should have been localized
into a single abstraction). Two forms of this smell:
• Data and methods that ideally belong to an abstraction are split
among two or more abstractions.
• Methods in a class that are interested in members of other classes.
Caution:
DTOs
What’s the smell in our
Restricted © Siemens AG 2018 refactored solution?
Page 79 04.06.2018 Slide 79 CT RDA SSI RSE-IN
Restricted © Siemens AG 2018
Page 80 04.06.2018 Slide 80 CT RDA SSI RSE-IN
Insufficient Modularization
This smell arises when an abstraction exists that has not been completely
decomposed and a further decomposition could reduce its size, implementation
complexity, or both.
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”)
This smell arises when a class-level abstraction has dependencies with large
number of other class-level abstractions (high incoming as well as outgoing
dependencies).
Caution:
Core abstractions (example - java.lang.Class has more than 1000 incoming and 40
outgoing dependencies)
Introduction
Abstraction smells
Encapsulation smells
Modularization smells
Hierarchy smells
design context.
Are the subtypes having specialized behavior when compared to the base class?
Since the actual rendering of the line with different styles is done by the OS,
supertype and subtypes have same behavior.
Restricted © Siemens AG 2018
Page 99 04.06.2018 Slide 99 CT RDA SSI RSE-IN
Refactoring Unnecessary Hierarchy
Caution:
Inadequate language support to avoid duplication
Lack of support for multiple inheritance (in which case consider delegation
as the solution) or generics for primitive types (e.g. Java)
This smell arises when a subtype rejects the methods provided by its
supertype(s):
• throw an exception,
• provide an empty (or NOP i.e., NO Operation) method
• provide a method definition that just prints “should not implement”
message
Caution:
Yet-to-be implemented functionality
Performance considerations
Some specific types may override supertype methods with
NOP to improve performance, because those methods are
actually unnecessary for those specific subtypes and invoking
methods on them will lead to unnecessary computation
overhead
E.g. DefaultListCellRenderer subtype of JLabel overrides
validate, invalidate, revalidate, repaint, isOpaque methods
with empty implementation
Caution:
Class adapter pattern
This smell arises when an inheritance hierarchy is “too” wide and shallow
indicating that intermediate types may be missing.
Caution:
Language/library could require extending a type (ListResourceBundle manages
resources for a locale and is a class extended by 443 classes.)
Restricted © Siemens AG 2018
Page 124 04.06.2018 Slide 124 CT RDA SSI RSE-IN
Developer expects to
support an annotated
report in future
“Collapse” interfaces/classes
• InterruptibleChannel interface is a marker
interface that can be removed (annotations
made marker interfaces irrelevant)
• SelectableChannel abstract class contains a
few method declarations and only one
method definition that can be pushed to its
subclass.
This smell arises when a subtype inherits both directly as well as indirectly
from a supertype leading to unnecessary inheritance paths in the hierarchy.
Introduction
Abstraction smells
Encapsulation smells
Modularization smells
Hierarchy smells
Comprehension tools
Imagix 4D, Structure 101
Critique, code-clone detectors, and metric tools
Infusion, Designite, Ndepend
Simian, CPD
Understand, Source-monitor, Designite
Technical debt quantification and visualization tools
Sonar Qube
Refactoring tools
Refactoring inferencing tools – SCOUT, Jdeoderant
Refactoring performing tools – Eclipse, Resharper
Design quality is important for software Design smells can be viewed as violation
(especially in case of large, complex, of underlying fundamental design
and/or reusable software) principles
A great designer is one who understands Some design smells can’t be fixed
the impact of design defects/smells and For such smells, the only way is to
knows how to address them avoid introducing them in the first
place!
Design smells are candidates for
refactoring Context is important for smells
Given the liability of a smell, in
certain contexts, a designer may
make a conscious decision to live
with that smell
Shrinath Gupta
Technical Expert
Thank you! CT RDA SSI SDT-IN
[email protected]
Shubham Sahu
Associate Engineer
CT RDA DS AA TEC
[email protected]