0% found this document useful (0 votes)
5 views58 pages

Abstraction Smell (2)

The document discusses various types of abstraction smells in software design, including Missing Abstraction, Imperative Abstraction, Incomplete Abstraction, Multifaceted Abstraction, Unnecessary Abstraction, Unutilized Abstraction, and Duplicate Abstraction. It outlines the definition, rationale, potential causes, examples, and suggested refactoring for each type, emphasizing the importance of proper abstraction for maintainability and reliability in code. The learning outcomes focus on applying advanced refactoring techniques to address these abstraction issues.

Uploaded by

Sherly Chantika
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views58 pages

Abstraction Smell (2)

The document discusses various types of abstraction smells in software design, including Missing Abstraction, Imperative Abstraction, Incomplete Abstraction, Multifaceted Abstraction, Unnecessary Abstraction, Unutilized Abstraction, and Duplicate Abstraction. It outlines the definition, rationale, potential causes, examples, and suggested refactoring for each type, emphasizing the importance of proper abstraction for maintainability and reliability in code. The learning outcomes focus on applying advanced refactoring techniques to address these abstraction issues.

Uploaded by

Sherly Chantika
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 58

ABSTRACTION SMELL

SESSION 17 & 18

SUBJECT MATTER EXPERT


D6580 – Muhammad Amien
Ibrahim, S.Kom., M.Sc.
SUB TOPICS

• Abstraction
• Missing Abstraction
• Imperative Abstraction
• Incomplete Abstraction
• Multifaceted Abstraction
• Unnecessary Abstraction
• Unutilized Abstraction
• Duplicate Abstraction
LEARNING OUTCOMES

At the end of this lecture, students are able to:

LO 2: Apply Advanced refactoring and its application


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

• 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.
ABSTRACTION KEY ENABLING TECHNIQUE

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
Multifaceted Abstraction,
Assign single and meaningful
Unnecessary Abstraction,
responsibilities
Unutilized Abstraction
Avoid Duplication Duplicate Abstraction
MISSING ABSTRACTION

Component Description
This smell arises when clumps of data or
Definition encoded strings are used instead of creating a
class or an interface.
• It can expose implementation details to
different abstractions, violating the principle of
encapsulation.
Rationale • When data and associated behavior are spread
across abstractions, it can
lead to tight coupling between entities, resulting
in brittle and non-reusable code.
• Inadequate design analysis
Potential Causes • 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.
MISSING ABSTRACTION

Example I

• 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

• 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:
MISSING ABSTRACTION

Example II

• 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 II

• 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.
MISSING ABSTRACTION

Example II

• The StackTraceElement is the “Missing Abstraction” in the original


design. It was introduced in Java 1.4 as follows:
MISSING ABSTRACTION

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.
MISSING ABSTRACTION

Impacted Quality

• 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.
MISSING ABSTRACTION

Practical Consideration (Avoid Over-engineering)

Sometimes, entities are merely data elements and don’t have any
behavior associated with them. In such cases, it may be over-
engineering to represent them as classes or interfaces. Hence, a
designer must carefully examine the application context before
deciding to create an explicit abstraction. For example, check if the
following are needed (a non-exhaustive list) to determine if creating
an abstraction is warranted:
• 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
This smell arises when an operation is turned into
Definition a class. This smell manifests as a class that has
only one method defined within the class.
• The founding principle of object-orientation is to
capture real world objects and rep- resent them
as abstractions. By following the enabling
Rationale
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
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.
IMPERATIVE ABSTRACTION

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
IMPERATIVE ABSTRACTION

Practical Consideration

Reification is the promotion or elevation of something that is not an


object into an object. When we reify behavior, it is possible to store it,
pass it, or transform it. Reification improves flexibility of the system at
the cost of introducing some complexity. Many design patterns employ
reification. Examples:
• 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
This smell arises when an abstraction does not
support complementary or interrelated methods
Definition completely. For instance, the public interface of
an abstraction may provide an initialize() method
to allocate resources.
• One of the key enabling techniques for
abstraction is to “create coherent and complete
abstractions.” One of the ways in which
Rationale
coherence and completeness of an abstraction
may be affected is when interrelated methods
are not supported by the abstraction.
• Missing Overall Perspective
Potential Causes
• 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).
INCOMPLETE ABSTRACTION

Example

• Hence, to avoid breaking existing clients, the getGroup() method was


added in its derived class DefaultButtonModel in JDK version 1.3
INCOMPLETE ABSTRACTION

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.
INCOMPLETE ABSTRACTION

Practical Consideration - 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.
INCOMPLETE ABSTRACTION

Practical Consideration - 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
This smell arises when an abstraction has more
Definition
than one responsibility assigned to it.
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
Rationale
Responsibility Principle says that an abstraction
should have a single well-defined responsibility
and that responsibility should be entirely
encapsulated within that abstraction.
• General purpose abstraction
• Evolution without periodic refactoring
Potential Causes
• The burden of process
• Mixing up concern
MULTIFACETED ABSTRACTION

Example

• java.util.Calendar class as an example of a class having multiple


responsibilities. 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.
MULTIFACETED ABSTRACTION

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.
MULTIFACETED ABSTRACTION

Impacted Quality

• 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.
UNNECESSARY ABSTRACTION

Component Description
This smell occurs when an abstraction that is
Definition actually not needed (and thus could have been
avoided) gets introduced in a software design.
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
Rationale 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.
• Procedural thinking in object oriented language
• Using inappropriate language features for
Potential Causes
convenient
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.
UNNECESSARY ABSTRACTION

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.
UNNECESSARY ABSTRACTION

Practical Consideration - Delegating abstractions in design


patterns

• Some design patterns (e.g., Mediator, Proxy, Façade, and Adapter)


that employ delegation have a class that may appear to be an
Unnecessary Abstraction.

• For example, in case of the Object Adapter pattern, the Adapter class
may appear to merely delegate client requests to the appropriate
method on the Adaptee. However, the primary intention behind the
Adapter class is to fulfill the specific, well-defined responsibility of
adapting the Adaptee’s interface to the client needs.

• Hence, one has to carefully consider the context before deciding


whether an abstraction that just performs delegation is unnecessary
or not.
UNNECESSARY ABSTRACTION

Practical Consideration - Accommodating variations

• Consider the example of java.lang.Math and java.lang.StrictMath


classes, which provide almost similar math-related functionality.

• The JavaDoc for StrictMath notes: “By default many of the Math
methods simply call the equivalent method in StrictMath for their
implementation.”

• It may appear from the JavaDoc description that Math is an


Unnecessary Abstraction that simply delegates calls to StrictMath.
UNNECESSARY ABSTRACTION

Practical Consideration - Accommodating variations

• However, although both support math-related functionality,


StrictMath methods return exactly the same results irrespective of
the platform because the implementation conforms to the relevant
floating-point standard, whereas Math methods may use native
hardware support for floating-point numbers and hence return
slightly different results.

• Here, note that Math is less portable but can result in better
performance when compared to StrictMath.

• Therefore, it is a conscious design decision to create two abstractions


to accommodate variation in objectives, i.e., portability and
performance.
UNUTILIZED ABSTRACTION

Component Description
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
Definition
not being used by anyone. Orphan abstractions—Stand-alone
interfaces/abstract classes that do not have any derived
abstractions
One of the enabling techniques for applying the principle of
abstraction is to assign a single and meaningful responsibility to
Rationale 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.
• Speculative Design
Potential
• Changing Requirement
Causes
• Leftover garbage
UNUTILIZED 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.
UNUTILIZED ABSTRACTION

Example

• 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.


UNUTILIZED 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.
UNUTILIZED ABSTRACTION

Practical Consideration - 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
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
Definition
whether they share similar behavior. Identical implementation
—This is when two or more abstractions have semantically
identical member definitions
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
Rationale
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.
• Copy paste programming
Potential
• Ad hoc maintenance
Causes
• Lack of communication
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
DUPLICATE ABSTRACTION

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.
DUPLICATE ABSTRACTION

Practical Consideration - Accommodating variations

• One reason why duplicate abstractions may exist is to support


synchronized and unsynchronized variants.

• A synchronized variant of an abstraction may have used


synchronization constructs heavily and this may lead to creating
separate abstractions (that suffer from Duplicate Abstraction smell)
corresponding to the two variants. An example of this is seen in
java.util.Vector and java. util.ArrayList classes that have similar
method definitions.

• The main difference between these classes is that the former is


thread-safe and the latter is not thread-safe.
DUPLICATE ABSTRACTION

Practical Consideration - Duplicate type names in different


contexts

• It is hard to analyze and model large domains and create a unified


domain model. In fact, “total unification of the domain model for a
large system will not be feasible or cost-effective”.

• One solution offered by Domain Driven Design is to divide the large


system into “Bounded Contexts.” In this approach, the resulting
models in different contexts may result in types with same names.

• Since Bounded Context is one of the patterns that help deal with the
larger problem of modeling large domains, such types with same
names in different contexts is acceptable.
DUPLICATE ABSTRACTION

Practical Consideration - Lack of language support for


avoiding duplication

• Many methods and classes are duplicated in JDK because generics


support is not available for primitive types.

• For example, the code for methods such as binarySearch, sort, etc.
are duplicated seven times in the java.util.Arrays class because it is
not possible to write a single generic method that takes different
primitive type arrays.

• This results in the class suffering from a bloated interface.


REFERENCES

• Samarthyam, Ganesh;Sharma, Tushar;Suryanarayana, Girish,


“Refactoring for Software Design Smells: Managing Technical Debt”,
Morgan Kaufmann, 2015

You might also like