Good Software Design
Contents
Good Software Design
Software will always change.
Technology will change, versions will get updated, frameworks will change, computers with better/worse hardware will be used.
With the constant change of specifications and requirements - it is important to write maintainable, reusable and performant code.
The Ideal System
- Loose coupling - Low interdependence of modules
- High cohesion - Modules are logically separated, and still work well with each other
Design Smells / Code Smells
- Rigidity - Complicated to change one small thing (causes a series of changes for dependent modules)
- e.g. Library system - Adding a new category requires changes to a lot of cde
- Fragility - Code breaks because of a single change
- e.g. 3D shape - Modifying the length of one side requires manual modification to its surface area and volume properties.
- Immobility - Non-modular code that is hard to reuse in other systems
- e.g. A class that relies on case-specific objects
- Viscosity - Hard to implement without 'hacking' it together
- Opacity - Confusing and hard to understand
Needless Complexity
Duplicate code
Long function bodies
Large class
Long parameter list
Divergent change (When one class is commonly changed in different ways for different reasons)
Shotgun surgery (When a lot of classes are changed for one reason)
Design Principles
- SOLID
- Separation of Concerns (SOC)
- Less Fragile Systems (Maintainable, Reusable and Extensible code)
- Pragmatic Programming (Don't Repeat Yourself, Keep It Simple Stupid)
- Design Patterns (GOF)
Warning - Don't over do it
These 'principles' are more like 'tools', rather than 'rules'.
Don't apply design principles if there are no code smells present.
Otherwise you will result in your code having needless complexity.
Principle of Least Knowledge / Law of Demeter
"one dot only".
(Two is okay I guess)
Only interact with immediate objects (i.e Objects contained inside the class)
In practical application - Rather than chaining functions in one line, add extra methods in neighbouring classes that can return the next object
A method in an object should only invoke methods of:
- the object itself
- objects passed in as parameters
- objects instantiated within the method
- component objects
- NOT of those returned
Liskov Substitution Principle
Favour composition (wrapper) over inheritance