Refactoring for Software Design Smells: Managing Technical Debt

Category: Programming
Author: Girish Suryanarayana, Ganesh Samarthyam, Tushar Sharma
4.8
This Month Stack Overflow 1

Comments

by anonymous   2019-01-13

Whenever I hear about Anti-patterns, I recollect another term viz. Design smell.

"Design smells are certain structures in the design that indicate violation of fundamental design principles and negatively impact design quality”. (From "Refactoring for Software Design Smells: Managing technical debt")

There are many design smells classified based on violating design principles:

Abstraction smells

Missing Abstraction: This smell arises when clumps of data or encoded strings are used instead of creating a class or an interface.

Imperative Abstraction: This smell arises when an operation is turned into a class.

Incomplete Abstraction: This smell arises when an abstraction does not support complementary or interrelated methods completely.

Multifaceted Abstraction: This smell arises when an abstraction has more than one responsibility assigned to it.

Unnecessary Abstraction: This smell occurs when an abstraction that is actually not needed (and thus could have been avoided) gets introduced in a software design.

Unutilized Abstraction: This smell arises when an abstraction is left unused (either not directly used or not reachable).

Duplicate Abstraction: This smell arises when two or more abstractions have identical names or identical implementation or both.

Encapsulation smells

Deficient Encapsulation: This smell occurs when the declared accessibility of one or more members of an abstraction is more permissive than actually required.

Leaky Encapsulation: This smell arises when an abstraction “exposes” or “leaks” implementation details through its public interface.

Missing Encapsulation: This smell occurs when implementation variations are not encapsulated within an abstraction or hierarchy.

Unexploited Encapsulation: 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.

Modularization smells

Broken Modularization: This smell arises when data and/or methods that ideally should have been localized into a single abstraction are separated and spread across multiple abstractions.

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.

Cyclically-Dependent Modularization: This smell arises when two or more abstractions depend on each other directly or indirectly (creating a tight coupling between the abstractions).

Hub-Like Modularization: This smell arises when an abstraction has dependencies (both incoming and outgoing) with a large number of other abstractions.

Hierarchy smells

Missing Hierarchy: This smell arises when a code segment uses conditional logic (typically in conjunction with “tagged types”) to explicitly manage variation in behavior where a hierarchy could have been created and used to encapsulate those variations.

Unnecessary Hierarchy: This smell arises when the whole inheritance hierarchy is unnecessary, indicating that inheritance has been applied needlessly for the particular design context.

Unfactored Hierarchy: This smell arises when there is unnecessary duplication among types in a hierarchy.

Wide Hierarchy: This smell arises when an inheritance hierarchy is “too” wide indicating that intermediate types may be missing.

Speculative Hierarchy: This smell arises when one or more types in a hierarchy are provided speculatively (i.e., based on imagined needs rather than real requirements).

Deep Hierarchy: This smell arises when an inheritance hierarchy is “excessively” deep.

Rebellious Hierarchy: This smell arises when a subtype rejects the methods provided by its supertype(s).

Broken Hierarchy: This smell arises when a supertype and its subtype conceptually do not share an “IS- A” relationship resulting in broken substitutability.

Multipath Hierarchy: This smell arises when a subtype inherits both directly as well as indirectly from a supertype leading to unnecessary inheritance paths in the hierarchy.

Cyclic Hierarchy: This smell arises when a supertype in a hierarchy depends on any of its subtypes.


The above definition and classification is described in "Refactoring for software design smells: Managing technical debt". Some more relevant resources could be found here.