0% found this document useful (0 votes)
321 views

Binus University Code Reengineering Abstraction Smell

The document discusses abstraction smells that can occur during code design. It describes missing abstraction, imperative abstraction, incomplete abstraction, multifaceted abstraction, unnecessary abstraction, unutilized abstraction, and duplicate abstraction smells. For each smell, it provides the component definition, rationale, potential causes, examples, and suggested refactorings to address the smells. The goal is to apply object-oriented design principles like abstraction and encapsulation to improve code quality.

Uploaded by

raymond setiawan
Copyright
© © All Rights Reserved
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
321 views

Binus University Code Reengineering Abstraction Smell

The document discusses abstraction smells that can occur during code design. It describes missing abstraction, imperative abstraction, incomplete abstraction, multifaceted abstraction, unnecessary abstraction, unutilized abstraction, and duplicate abstraction smells. For each smell, it provides the component definition, rationale, potential causes, examples, and suggested refactorings to address the smells. The goal is to apply object-oriented design principles like abstraction and encapsulation to improve code quality.

Uploaded by

raymond setiawan
Copyright
© © All Rights Reserved
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 47

Subject : CODE REENGINEERING

Year : 2017

Abstraction Smell
Session 15 & 16
Learning Outcomes

LO 2: Apply Advanced refactoring and its application

COMP6047 - Algorithm and Programming


Sub Topics

- Missing Abstraction
- Imperative Abstraction
- Incomplete Abstraction
- Multifaceted Abstraction
- Unnecessary Abstraction
- Unutillized Abstraction
- Duplicate Abstraction

COMP6047 - Algorithm and Programming


Abstraction

The principle of abstraction advocates the simplification of entities through


reduction and generalization: reduction is by elimination of unnecessary details
and generalization is by identification and specification of common and
important characteristics.

Abstraction is a powerful principle that provides a means for effective yet simple
communication and problem solving. Company logos and traffic signs are
examples of abstractions for communication. Mathematical symbols and
programming languages are examples of abstraction as a tool for problem
solving.
Abstraction Key Enabling
Technique
Abstraction Key Enabling
Technique

Provide a crisp conceptual boundary and an identity. Each abstraction


should have a crisp and clear conceptual boundary and an identity.

Map domain entities. There should be a mapping of vocabulary from the


problem domain to the solution domain, i.e., objects recognized by the
problem domain should be also represented in the solution domain.

Ensure coherence and completeness. An abstraction should completely


support a responsibility.

Assign single and meaningful responsibility. Ensure that each abstraction


has a unique non-trivial responsibility assigned to it.

Avoid duplication. Ensure that each abstraction—the name as well as its


implementation—appears only once in design.
Design Smell and Key Enabling

Violated Enabling Technique Design Smell


Provide a crisp conceptual boundary Missing Abstraction
and an identity
Map Domain Entity Imperative Abstraction
Ensure coherence and correctness Incomplete Abstraction
Assign single and meaningful Multifaceted Abstraction, Unnecessary
responsibilities Abstraction, Unutilized Abstraction
Avoid Duplication Duplicate Abstraction
Missing Abstraction

Component Description
Definition This smell arises when clumps of data or encoded
strings are used instead of creating a class or an
interface.
Rationale • It can expose implementation details to
different abstractions, violating the principle of
encapsulation.
• When data and associated behavior are spread
across abstractions, it can
lead to tight coupling between entities,
resulting in brittle and non-reusable code.
Potential Causes • Inadequate design analysis
• Lack of refactoring
• Misguided focus on minor performance gain
Missing Abstraction Example I

Consider a library information management application. Storing and


processing ISBNs (International Standard Book Numbers) is very important in
such an application. It is possible to encode/store an ISBN as a primitive type
value (long integer/ decimal type) or as a string. However, it is a poor choice
in this application. Why? To understand that, let’s take a quick look at ISBNs.

The digits in an ISBN have meaning; for example, an ISBN-13 number


consists of these elements: Prefix Element, Registration Group Element,
Registrant Element, Publication Element, and Checksum. The last digit of an
ISBN number is a checksum digit, which is calculated as follows: starting
from the first digit, the values of the odd digits are kept the same, and the
values of even numbered digits are multiplied by three; the sum of all the
values modulo 10 is the value of the last digit. So, given a 10 or 13 digit
number, you can validate whether the given number is a valid ISBN number
Missing Abstraction Example I

Implementation of a library information management application involves


logic that accepts, validates, processes, or converts between ISBN numbers. It
is possible to encode ISBN numbers as strings or as a primitive type value in
such an application. However, in such a case, the logic that processes the
numbers will be spread as well as duplicated in many places. In the context of
a library information system that has considerable logic involving ISBN
numbers, not encapsulating ISBN numbers as class(es) indicates a Missing
Abstraction smell.
Missing Abstraction Example I Suggested
Refactoring

The example discussed the need to handle different kinds of ISBN numbers
such as ISBN-10 and ISBN-13. A potential refactoring would be to abstract
ISBN as an abstract class or interface with common operations in it. ISBN-10
and ISBN-13 can be subclasses that extend the ISBN supertype
Missing Abstraction Example II Suggested
Refactoring

Revisiting the example of the drawing application, a refactoring suggestion would


be to abstract the required fields into a new class—say Rectangle class or
SelectedRegion class—and move methods operating on these fields to the new
class.
Missing Abstraction Example III
Strings are often used to encode information. In the case of APIs (Application
Programming Interface), the problem with encoding data in strings is that
once the API is released, it is very difficult to change the encoding format
since clients that depend on the API will be affected. Let us take a look at a
detailed example from Java Development Kit (JDK).
From 1.0 version of Java, the stack trace was printed to the standard error
stream as a string with printStackTrace() method:

Client programs that needed programmatic access to the stack trace elements
had to write code to process the stack trace to get, for example, the line
numbers or find if the bug is a duplicate of another already-filed bug. Due to
this dependence of client programs on the format of the string, the designers of
JDK were forced to retain the string encoding format in future versions of
JDK.
Missing Abstraction Example III Suggested
Refactoring

The Java API was improved in version 1.4 to have programmatic access to the
stack trace through the introduction of StackTraceElement class. Note that
even though a new method is added, the method printStackTrace() and the
format of the stack trace has been retained to support the existing clients.

The StackTraceElement is the “Missing Abstraction” in the original design. It was


introduced in Java 1.4 as follows:
Impacted Quality

Understandability—When a key entity is not represented as an abstraction and


the logic that processes the entity is spread across the code base, it becomes
difficult to understand the design.

Changeability and Extensibility—It is difficult to make enhancements or changes


to the code when relevant abstractions are missing in design. First, it is difficult
even to figure out the parts of the code that need to be modified to implement a
change or enhancement.

Reusability and Testability—Since some abstractions that correspond to domain


or conceptual entities are missing, and the logic corresponding to the entities is
spread in the code base, both reusability and testability of the design are
impacted.

Reliability—An abstraction helps provide the infrastructure to ensure the


correctness and integrity of its data and behavior. In the absence of an
abstraction, the data and behavior is spread across the code base; hence,
integrity of the data can be easily compromised. This impacts reliability.
Practical Consideration (Avoid Over-
engineering)

default initialization of data values using constructors

validation of the data values

support for pretty printing the data values

acquired resources (if any) are to be released


Imperative Abstraction

Component Description
Definition This smell arises when an operation is turned into
a class. This smell manifests as a class that has only
one method defined within the class.
Rationale The founding principle of object-orientation is to
capture real world objects and rep- resent them as
abstractions. By following the enabling technique
map domain entities, objects recognized in the
problem domain need to be represented in the
solution domain, too.

Potential Causes • Procedural Thinking


Imperative Abstraction Example
Consider the case of a large-sized financial application. This application
employs classes named CreateReport, CopyReport, DisplayReport, etc. to
deal with its report generation functionality. Each class has exactly one
method definition named create, copy, display, etc., respectively, and suffers
from Imperative Abstraction smell. The data items relating to a report such as
name of the report, data elements that need to be displayed in the report,
kind of report, etc. are housed in a “data class” named Report.
Imperative Abstraction Example Suggested
Refactoring
For the report generation part of the financial application, a suggested
refactoring is to move the methods in each of the classes suffering from
Imperative Abstraction to the Report class itself. Moving all the report-related
operations to the Report class makes the Report class a proper “abstraction”
and also removes the Imperative Abstraction smell. The design becomes
cleaner and less complex.
Impacted Quality

Understandability—An abstraction with this smell does not have a direct


mapping to the problem domain;

Changeability and Extensibility—The presence of an Imperative Abstraction


smell does not impact the changeability and extensibility of the abstraction itself.

Reusability—Consider the report generation functionality that was discussed in


Example .

Testability—If an abstraction with Imperative Abstraction smell is self-suf- ficient,


it is easy to test it
Practical Consideration (Reification)

State pattern: Encoding a state-machine.

Command pattern: Encoding requests as command objects. A permitted


exception for this smell is when a Command pattern has been used to objectify
method requests.

Strategy pattern: Parameterizing a procedure in terms of an operation it uses.


Incomplete Abstraction

Component Description
Definition This smell arises when an abstraction does not
support complementary or interrelated methods
completely. For instance, the public interface of an
abstraction may provide an initialize() method to
allocate resources.
Rationale One of the key enabling techniques for abstraction
is to “create coherent and complete abstractions.”
One of the ways in which coherence and
completeness of an abstraction may be affected is
when interrelated methods are not supported by
the abstraction.
Potential Causes • Missing Overall Perspective
• Not Adhering to language or library convention
Incomplete Abstraction Example

An interesting instance of “Incomplete Abstraction” is observed in JDK’s


javax. swing.ButtonModel interface. It provides setGroup() method,
which according to its documentation, “identifies the group the button
belongs to—needed for radio but- tons, which are mutually exclusive
within their group.” The ButtonModel interface does not provide the
symmetric getGroup() method and hence suffers from Incomplete
Abstraction smell.
Incomplete Abstraction Example Suggested
Refactoring

The refactoring for the ButtonModel example


from JDK ideally involves defining the
getGroup() method in the ButtonModel
interface itself. However, since JDK is a public
API, adding a method to an interface would
break the existing classes that implement that
interface (remember that all methods declared
in an interface must be defined in a class that
implements the interface). Hence, to avoid
breaking existing clients, the getGroup()
method was added in its derived class
DefaultButtonModel in JDK version 1.3
Impacted Quality

Understandability—Understandability of the abstraction is adversely impacted


because it is difficult to comprehend why certain relevant method(s) are missing
in the abstraction.

Changeability and Extensibility—Changeability and extensibility of the


abstraction are not impacted.

Reusability—If some of the operations are not supported in an abstraction, it is


harder to reuse the abstraction in a new context because the unsupported
operations will need to be provided explicitly.

Reliability—An Incomplete Abstraction may not implement the required


functionality, resulting in defects.
Practical Consideration (Reification)

Disallowing certain behavior

Sometimes, a designer may make a conscious design decision to not provide


symmetric or matching methods. For example, in a read-only collection, only add()
method may be provided without the corresponding remove() method. In such a
case, the abstraction may appear incomplete, but is not a smell.

Using a single method instead of a method pair

Sometimes, APIs choose to replace symmetrical methods with a method that


takes a boolean argument (for instance, to enforce a particular naming
convention such as JavaBeans naming convention that requires accessors to have
prefixes “get,” “is,” or “set”).
Multifaceted Abstraction

Component Description
Definition This smell arises when an abstraction has more
than one responsibility assigned to it.
Rationale An important enabling technique to effectively
apply the principle of abstraction is to assign single
and meaningful responsibility for each abstraction.
In particular, the Single Responsibility Principle
says that an abstraction should have a single well-
defined responsibility and that responsibility
should be entirely encapsulated within that
abstraction.
Potential Causes • General purpose abstraction
• Evolution without periodic refactoring
• The burden of process
• Mixing up concern
Multifaceted Abstraction Example

In his book, Neal Ford mentions java.util.Calendar class as an example of a class


having multiple responsibilities [64]. A class abstracting real-world calendar
functionality is expected to support dates, but the java.util.Calendar class supports
time related functionality as well, and hence this class suffers from Multifaceted
Abstraction smell.
Multifaceted Abstraction Example Suggested
Refactoring

For the Calendar class, a possible refactoring is to extract time-related


functionality from the Calendar class into a new Time class and move the
relevant methods and fields into the newly extracted class. Java 8 has
introduced new classes supporting date and time (and other classes such as
clocks, duration, etc.) in a package named java.time so that future clients can
use this new package instead.
Impacted Quality

Understandability—A Multifaceted Abstraction increases the cognitive load due


to multiple aspects realized into the abstraction

Changeability and Extensibility—When an abstraction has multiple


responsibilities, it is often difficult to figure out what all members within the
abstraction need to be modified to address a change or an enhancement.

Reusability—Ideally, a well-formed abstraction that performs a single


responsibility has the potential to be reused as a unit. When an abstraction has
multiple responsibilities, the entire abstraction must be used even if only one of
the responsibilities needs to be reused.

Testability—When an abstraction has multiple responsibilities, these


responsibilities may be entwined with each other, making it difficult to test each
responsibility separately.

Reliability—The effects of modification to an abstraction with intertwined


responsibilities may be unpredictable and lead to runtime problems.
Practical Consideration (Reification)

None
Unnecessary Abstraction

Component Description
Definition This smell occurs when an abstraction that is actually not
needed (and thus could have been avoided) gets introduced in
a software design.
Rationale A key enabling technique to apply the principle of abstraction
is to assign single and meaningful responsibility to entities.
However, when abstractions are created unnecessarily or for
mere convenience, they have trivial or no responsibility
assigned to them, and hence violate the principle of
abstraction. Since the abstraction is needlessly introduced in
the design, this smell is named Unnecessary Abstraction.
Potential Causes • Procedural thinking in object oriented language
• Using inappropriate language features for convenient
• Over Engineering
Unnecessary Abstraction Example

Consider the case of an e-commerce application that has two classes: namely, Best-
SellerBook and Book. Whenever the client wants to create a best-seller book, it
creates an instance of a BestSellerBook. Internally, BestSellerBook delegates all
the method calls to the Book class and does nothing else. Clearly, the BestSeller-
Book abstraction is unnecessary since its behavior is exactly the same as the Book
abstraction.
Unnecessary Abstraction Example Suggested
Refactoring

For the case of the e-commerce application that has two classes; namely,
Best-SellerBook and Book, there are many possible refactoring solutions. One
solution, for instance, is to remove the BestSellerBook class and instead add
an attribute named isBestSeller (along with a getter and a setter) in the Book
class. Now, when the client code wants to indicate if a book is a bestseller,
it will set the attribute isBestSeller instead of creating an instance of the
erstwhile BestSellerBook class.
Impacted Quality

Understandability—Having needless abstractions in the design increases its


complexity unnecessarily and affects the understandability of the overall design.

Reusability—Abstractions are likely to be reusable when they have unique and


well-defined responsibilities. An abstraction with trivial or no responsibility is less
likely to be reused in a different context.
Practical Consideration (Reification)

Delegating abstractions in design patterns

Accommodating variations
Unutilized Abstraction
Component Description
Definition This smell arises when an abstraction is left unused (either not
directly used or not
reachable). This smell manifests in 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
Rationale One of the enabling techniques for applying the principle of
abstraction is to assign a single and meaningful responsibility
to an entity. When an abstraction is left unused in design, it
does not serve a meaningful purpose in design, and hence
violates the principle of abstraction.
Potential Causes • Speculative Design
• Changing Requirement
• Leftover garbage
• Fear of breaking code
Unnecessary Abstraction Example

This example is paraphrased from a bug report on unused classes in JDK.4


The pack- age sun.misc has classes that date back to early releases of JDK,
and they were used by other classes in JDK internally. Later, many of the
services provided by sun.misc package were provided as part of the public
API. Eventually, the original clients of sun.misc package started using the
services provided by the public API. Due to this, many of the original classes
in sun.misc package became unreferenced abstractions. One such example is
the internal class sun.misc.Service that was introduced in JDK 1.3, which was
made redundant by the introduction of the class java.util. ServiceLoader in
JDK version 1.6 (which is part of the public API). Hence, the original
sun.misc.Service is an Unutilized Abstraction.
Unnecessary Abstraction Example Suggested
Refactoring

All the uses of the sun.misc.Service class can be replaced by the


use of java.util. ServiceLoader class. Therefore, for all practical
purposes, there is no need for sun. misc.Service class and hence the
suggested refactoring is to remove it from the code base. In
fact, sun.misc.Service has been removed from JDK source code
and JDK 9 will not have it.5
Impacted Quality

Understandability—Presence of unused abstractions in design pollutes the


design space and increases cognitive load. This impacts understandability.

Reliability—The presence of unused abstractions can sometimes lead to run-


time problems. For instance, when code in unused abstractions gets accidentally
invoked, it can result in subtle bugs affecting the reliability of the software.
Practical Consideration (Reification)

Unutilized Abstractions in APIs

Class libraries and frameworks usually provide extension points in the form of
abstract classes or interfaces. They may appear to be unused within the library or
framework. However, since they are extension points that are intended to be
used by clients, they cannot be considered as Unutilized Abstractions.
Duplicate Abstraction
Component Description
Definition This smell arises when:
Identical name—This is when the names of two or more
abstractions are identical. While two abstractions can
accidentally have the same name, it needs to be analyzed
whether they share similar behavior.
Identical implementation—This is when two or more
abstractions have semantically identical member definitions

Rationale Avoid duplication is an important enabling technique for the


effective application of the principle of abstraction.
If two or more abstractions have an identical name, it affects
understandability of the design. Developers of client code will
be confused and unclear about the choice of the abstraction
that should be used by their code.
Potential Causes • Copy paste programming
• Ad hoc maintenance
• Lack of communication
• Classes declared non-extensible
Duplicate Abstraction Example

Classes java.util.Date and its derived class java.sql.Date share the same
name. The compiler does not complain that the base and derived classes
have the same name because these classes belong to different packages.
However, it is very confusing for the users of these classes. For example,
when both these classes are imported in a program, it will result in a
name ambiguity that must be resolved manually by explicitly qualifying
the class names.
Duplicate Abstraction Example Suggested
Refactoring

Further since java.sql.Date conforms to SQL DATE, it is preferable to rename


it as java.sql.SQLDate which will clearly differentiate it from the plain class
name java.util.Date
Impacted Quality

Understandability—Developers can become confused about which abstraction


to use when there are two or more abstractions with identical names or imple-
mentation.

Changeability and Extensibility—Change or enhancement involving one


abstraction potentially requires making the same modification in the duplicate
abstractions as well.

Reusability—Duplicate abstractions often have slightly different implementations


(especially Type 3 and Type 4 clones). The differences in implementations are
usually due to the presence of context

Reliability—When two abstractions have identical names, a confused devel- oper


may end-up using the wrong abstraction.
Practical Consideration (Reification)

Accommodating variations

Duplicate type names in different contexts

Lack of language support for avoiding duplication


References

Girish Suryanarayana, Ganesh Samarthyam and Tushar Sharma, Chapter 2 - Design Smells, In
Refactoring for Software Design Smells, edited by Girish Suryanarayana and Ganesh
SamarthyamTushar Sharma, Morgan Kaufmann, Boston, 2015, Pages 9-19, ISBN
9780128013977, http://dx.doi.org/10.1016/B978-0-12-801397-7.00002-3.

M. Fowler and K. Beck, "Bad Smells in Code," in Refactoring: Improving the Design of
Existing Code, Addison-Wesley, 2000

W. Stevens, G. Myers and L. Constantine, "Structured Design," IBM Syst J, vol. 13, no. 2, pp.
115-139, 1974.

COMP6047 - Algorithm and Programming

You might also like