The following design principles are applicable for any language, of any paradigm. While the metrics used to judge them are subjective, it’s good to aim for:
- High Modularity
- High Cohesion
- Low Coupling
Modularity
#tosee Difference between modularity and encapsulation?
Decomposing the problem to units (modules) that are easy to understand, manage and re-use.
OOP
Done via encapsulation (such as with classes).
Procedural Languages
Done using Functions.
Cohesion
Modules in a project must be related to, and focused on solving the overall problem. Simply, this means modules that achieve similar purposes should be grouped, but should not have much dependency on each other.
Cohesion is increased if:
- The functionalities embedded in a class, accessed through its methods, have much in common.
- Methods carry out a small number of related activities, by avoiding coarsely grained or unrelated sets of data.
- Related methods are in the same source file or otherwise grouped together; for example, in separate files but in the same sub-directory/folder. From Wikipedia
Cohesion should be maximised.
Coupling
The degree of association between modules. While some coupling is always necessary in a project, it should not be overused.
In OOP
- Deciding when to have an association relationships should be done carefully - avoid unnecessary associations as much as possible because it increases coupling.
- Decide when it is appropriate to pass objects as parameters (dependency relationships), to reduce coupling.
- Interfaces promote low coupling.
Coupling should be minimised.
Open-Closed Principle
A design philosophy that applies specifically to OOP, the open-closed principle states that:
Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.
- A module will be said to be open if it is still available for extension. For example, it should be possible to add fields to the data structures it contains, or new elements to the set of functions it performs.
- A module will be said to be closed if it is available for use by other modules. This assumes that the module has been given a well-defined, stable description (the interface in the sense of information hiding).
Bad Design Indicators
- Rigidity: Hard to modify the system because changes in one class/method cascade to many others
- Fragility: Changing one part of the system causes unrelated parts to break
- Immobility: Cannot decompose the system into reusable modules
- Viscosity: Writing “hacks” when adding code in order to preserve the design
- Complexity: Lots of clever code that isn’t necessary right now; premature optimisation is bad
- Repetition: Code looks like it was written by Cut and Paste
- Opacity: Lots of convoluted logic, design is hard to follow