Features of Good Design 1 - Design Principles
date
Mar 5, 2022
slug
features-of-good-design
status
Published
tags
coding
programming
Design
architecture
clean code
solid
summary
Software design principles for clean code/architecture
type
Post
The code reuse idea looks great on paper, but it turns out that making existing code work in a new context usually takes extra effort. Tight coupling between components, dependencies on concrete classes instead of interfaces, hardcoded operations—all of this reduces the flexibility of the code and makes it harder to reuse it.
Change is the only constant thing in a programmer’s life.
That’s why all seasoned developers try to provide for possible future changes when designing an application’s architecture.
Design Principles
Encapsulate What Varies
Identify the aspects of your application that vary and separate them from what stays the same.
- Encapsulation on a method level;
- You can extract the logic that varies into a separate method, hiding it from the original method.
- Encapsulation on a class level
- Over time you might add more and more responsibilities to a method that used to do a simple thing. These added behaviors often come with their own helper fields and methods that eventually blur the primary responsibility of the containing class. Extracting everything to a new class might make things much clearer and simpler.
Program to an Interface, not an Implementation
Depend on abstractions, not on concrete classes.
Favor Composition Over Inheritance
Inheritance comes with caveats that often become apparent only after your program already has tons of classes and changing anything is pretty hard. Here’s a list of those problems;
- A subclass can’t reduce the interface of the superclass. You have to implement all abstract methods of the parent class even if you won’t be using them.
- When overriding methods you need to make sure that the new behavior is compatible with the base one. It’s important because objects of the subclass may be passed to any code that expects objects of the superclass and you don’t want that code to break.
- Inheritance breaks the encapsulation of the superclass because the internal details of the parent class become available to the subclass. There might be an opposite situation where a programmer makes a superclass aware of some details of subclasses for the sake of making further extension easier.
- Subclasses are tightly coupled to superclasses. Any change in a superclass may break the functionality of subclasses.
- Trying to reuse code through inheritance can lead to creating parallel inheritance hierarchies. Inheritance usually takes place in a single dimension. But whenever there are two or more dimensions, you have to create lots of class combinations, bloating the class hierarchy to a ridiculous size.
There’s an alternative to inheritance called composition. Whereas inheritance represents the “is a” relationship between classes (a car is a transport), composition represents the “has a” relationship (a car has an engine).
Aggregation—a more relaxed variant of composition where one object may have a reference to the other one but doesn’t manage its lifecycle. Here’s an example: a car has a driver, but he or she may use another car or just walk without the car.