Why Refactoring Matters: Master Code Quality with SOLID, Design Patterns, and Best Practices
This article explains why continuous refactoring is essential for maintaining clean, maintainable software, outlines common code smells, introduces SOLID principles and design patterns, and provides practical techniques such as method extraction, composition over inheritance, and test‑driven development to improve code quality.
About Refactoring
Why Refactor
In a continuously evolving project, code accumulates and, without responsibility for quality, becomes increasingly tangled. When the codebase reaches a point where maintenance cost exceeds the cost of rewriting, refactoring becomes impossible.
The main causes are:
Lack of effective design before coding
Cost‑driven, feature‑stacking development
Missing code‑quality supervision mechanisms
Industry practice suggests continuously removing “code smells” through ongoing refactoring.
What Is Refactoring
Refactoring (noun): a structural adjustment of software aimed at improving understandability without changing observable behavior. Refactoring (verb): applying a series of refactoring techniques to adjust structure while preserving behavior.
Refactoring can be divided into large‑scale and small‑scale:
Large‑scale refactoring targets top‑level design such as system, module, and class relationships, using layering, modularization, decoupling, and reusable abstractions. It involves many code changes, higher risk, and longer time.
Small‑scale refactoring focuses on class, method, or variable level—renaming, eliminating large classes or methods, extracting duplicated code, etc. It is quick, low‑risk, and should be performed whenever new features, bug fixes, or code reviews reveal smells.
Code Smells
Code duplication
Identical logic and execution flow
Long methods
Statements span multiple abstraction levels
Complex logic requiring many comments
Procedural style instead of object‑oriented
Large classes
Too many responsibilities
Excessive instance variables and methods
Name does not describe purpose
Scattered logic
Frequent divergent changes across classes
Shot‑gun modifications affecting many classes
Excessive coupling
Methods over‑use members of other classes
Data clumps / primitive obsession
Repeated fields or parameters in classes/method signatures
Should use value objects (e.g., Money, Range) instead of primitives
Bad inheritance hierarchy
Inheritance breaks encapsulation and forces subclasses to depend on parent implementation details
Subclasses must evolve with parent unless the parent is designed for extension
Too many conditional branches
Overly long parameter lists
Excessive temporary variables
Confusing temporary fields
Fields used only for specific cases should be extracted into dedicated classes
Pure data classes
Only fields and getters/setters; should remain minimally mutable
Poor naming
Names do not accurately describe behavior
Names do not follow common conventions
Excessive comments
Problems of Bad Code
Difficult to reuse – high coupling prevents extracting reusable parts
Hard to change – a single change forces many modifications, harming stability
Hard to understand – messy naming and structure impede readability
Difficult to test – many branches and dependencies reduce test coverage
What Is Good Code
Code quality is subjective, but maintainability, readability, and extensibility are the most important criteria. Achieving high‑quality code requires applying object‑oriented design, design principles, design patterns, coding standards, and refactoring techniques.
How to Refactor
SOLID Principles
Single Responsibility Principle
A class should have only one responsibility or reason to change.
By avoiding “god classes” and keeping responsibilities separate, classes become highly cohesive and loosely coupled, improving maintainability. Over‑splitting can backfire, reducing cohesion.
Open‑Closed Principle
New functionality should be added by extending existing code (adding modules, classes, methods) rather than modifying existing code.
The principle does not forbid modification, but aims to minimize the cost of changes.
Many design principles, ideas, and patterns aim to improve extensibility, guided by the Open‑Closed Principle. Common techniques include polymorphism, dependency injection, and programming to interfaces.
Liskov Substitution Principle
Subtype objects must be replaceable for their base type without altering program correctness.
Subclasses can extend behavior but must not change existing behavior of the base class.
Implemented methods in a superclass set contracts; overriding them inconsistently can break the inheritance hierarchy.
Interface Segregation Principle
Clients should not depend on interfaces they do not use; interfaces should be fine‑grained.
Dependency Inversion Principle
High‑level modules should not depend on low‑level modules; both should depend on abstractions.
Law of Demeter
Objects should know as little as possible about other objects.
Composition Over Inheritance
Prefer composition/aggregation to inheritance.
Inheritance can make software fragile because it breaks encapsulation; composition keeps implementations independent.
Design Patterns
Design patterns are reusable solutions to common problems in software development.
Creational : solve object creation, decouple creation from use.
Structural : combine classes/objects to reduce coupling.
Behavioral : manage interactions between classes/objects.
Code Layering
server_main – configuration layer (module management, Maven, resources)
server_application – application entry layer (RPC interfaces, message handling, scheduled tasks); business logic should not reside here
server_biz – core business layer (use‑case services, domain entities, events)
server_irepository – resource interface layer (exposes resource APIs)
server_repository – resource layer (proxy access, isolates changes; emphasizes weak business, strong data responsibilities)
server_common – common utilities, value objects, etc.
Code should follow the conventions of each layer and respect dependency direction.
Naming Conventions
A good name must accurately describe what it does and follow common conventions.
Project names: all lowercase, words separated by hyphens (e.g., spring-cloud)
Package names: all lowercase (e.g., com.alibaba.fastjson)
Class/interface names: PascalCase (e.g., ParserConfig, DefaultFieldDeserializer)
Variable names: camelCase (e.g., password, userName)
Constant names: UPPER_SNAKE_CASE (e.g., CACHE_EXPIRED_TIME)
Method names: camelCase verbs (e.g., read(), getById())
Class Naming
Class names use UpperCamelCase and are usually nouns; interfaces may also use adjectives to indicate capability (e.g., Cloneable, Callable).
Abstract or Base prefix for abstract classes (e.g., BaseUserService)
Enum suffix for enumerations (e.g., GenderEnum)
Utils suffix for utility classes (e.g., StringUtils)
Exception suffix for exception classes (e.g., RuntimeException)
Impl suffix for interface implementations (e.g., UserServiceImpl)
Builder, Factory, etc., for design‑pattern related classes
Handler, Predicate, Validator for specific responsibilities
Controller, Service, ServiceImpl, Dao suffix for typical layers
Ao, Param, Vo, Config, Message for value objects
Test suffix for test classes (e.g., UserServiceTest)
Method Naming
Method names use lowerCamelCase and are usually verbs or verb phrases that describe the action performed.
Boolean‑returning methods start with is/can/has/needs/should (e.g., isValid, canRemove)
Validation methods start with ensure/validate (e.g., ensureCapacity, validateInputs)
Conditional execution methods end with IfNeeded, try, OrDefault, OrElse (e.g., drawIfNeeded, tryCreate)
Data‑related methods start with get/search/save/update/batchSave/batchUpdate/saveOrUpdate/insert/delete (e.g., getUserById, searchUsersByCreateTime)
Lifecycle methods: initialize, pause, stop, destroy
Common verb pairs: split/join, inject/extract, bind/separate, increase/decrease, launch/run, observe/listen, build/publish, encode/decode, submit/commit, push/pull, enter/exit, expand/collapse
Refactoring Techniques
Extract Method
When multiple methods share duplicated code, are too long, or contain statements at different abstraction levels, extract the common part into a new method to improve reuse and readability.
Intent‑Driven Programming
Separate the workflow (what needs to be done) from the implementation details (how it is done).
Replace Function with Function Object
Encapsulate a function inside an object so that temporary variables become fields, allowing the large function to be split into smaller methods.
Introduce Parameter Object
When a method has many parameters, wrap them into a single parameter object.
Separate Query from Modification
Methods that return a value should not have side effects.
Remove Unnecessary Temporary Variables
Eliminate variables that are used only once or whose computation cost is negligible.
Introduce Explaining Variable
Assign complex expressions to well‑named temporary variables to clarify intent.
Use Guard Clauses Instead of Nested Conditionals
Replace deep if‑else nesting with early returns to simplify logic.
Replace Conditional Logic with Polymorphism
When behavior varies by type, move each branch into a subclass and make the original method abstract.
Replace Error Codes with Exceptions
Use exceptions for abnormal business states instead of returning error codes.
Introduce Assertion
Use assertions to document assumptions that must always hold.
Introduce Null Object or Special Object
Replace frequent null checks with a special object that implements the required interface.
Extract Class
When a class accumulates many responsibilities, split related fields and methods into a new class.
Prefer Composition Over Inheritance
Use composition (has‑a) instead of inheritance (is‑a) to avoid fragile coupling.
Prefer Interfaces Over Abstract Classes
Interfaces allow multiple inheritance of type and are more flexible for future extensions.
Prefer Generics
Use generic types to achieve compile‑time type safety and avoid raw types.
Prefer Static Member Classes Over Non‑Static
Static nested classes do not hold an implicit reference to the outer instance, reducing memory overhead.
Prefer Template/Utility Classes
Encapsulate common logic in reusable utility or template classes to reduce duplication.
Separate Object Creation from Use
Inject dependencies via constructors or factories instead of creating them directly where they are used.
Minimize Accessibility
Make classes, fields, and methods as private or package‑private as possible; expose only what is necessary.
Minimize Mutability
Prefer immutable objects: make fields private and final, avoid setters, declare classes final, and defensively copy mutable components.
Quality Assurance
Test‑Driven Development (TDD)
TDD places tests at the center of development: write a failing test first, then write just enough code to pass it, and finally refactor.
TDD yields clean, working code and a comprehensive automated test suite that guards against regressions during future changes.
TDD Cycle
Add a test → run all tests and see failure → write code to pass → run all tests and see success → refactor to eliminate duplication and improve design.
Basic Principles
Write only enough code to make the failing test pass.
Before adding the next test, refactor existing code to remove duplication and improve structure.
Separate concerns: first achieve “working” code, then pursue “clean” code.
Layered Testing
Dao tests – verify MyBatis configuration, mappers, handlers (use in‑memory DB, assertions)
Adapter tests – verify external interactions and converters (manual verification)
Repository tests – verify internal calculations and transformations (mock dependencies, assertions)
Biz layer tests – verify business logic (isolate external dependencies, multiple scenario tests, assertions)
Application layer tests – verify entry‑parameter handling and system flow (isolate external dependencies, parameter‑driven scenarios, debugging, no deep logic verification)
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
