Saturday, January 31, 2015

Software Design Principles

In software design, there is a direct, strong, and undeniable correlation between principles and quality.  These principles guide us to create good designs which become good code which becomes good software.  It is absolutely worth your time to understand these principles and remember them.
There are many more principles than these, but the ones listed here are solid, proven, fairly well known, and will guide you to create strong designs.

The Single Responsibility Principle says that each class and each function should have one single, specific, and well-defined purpose, and it should never do anything that is unrelated to its purpose.  This principle is essentially just a reminder of the importance of cohesion.

The Hollywood Principle says, “Don’t call us, we’ll call you.”  What it means is that some classes should serve a high-level purpose and be the ones that drive processes, while others should simply perform the low-level tasks and nothing more.  High level classes should invoke methods of lower-level classes, but never vice versa.

The Principle of Least knowledge says, “Only talk to your immediate friends.”  When diagramming your design in a UML class diagram, each class should only invoke methods of classes that are directly connected to it.  This provides many advantages; one of the most significant being that it greatly reduces coupling.

Occam’s Razor is a well-known scientific principle that we can modify slightly to fit the purpose of software design.  It says if two different designs are both satisfactory, then the simpler one is better.

The Open/Closed Principle says that classes should be open to extension but closed to modification.  The idea is that code should be designed so that new functionality can be added without changing the existing, working code.

The Liskov Substitution Principle says that if you can design your classes to depend on abstract classes and interfaces, then any subclass of those can be substituted in later regardless of how it implements the functionality behind the scenes.

Favor composition over inheritance.  In many (but not all) cases, using composition for code reuse works just as well as using inheritance.  Composition has the advantage of looser coupling, improves reusability, simplifies testing and maintenance, and provides numerous other benefits.

Separate the aspects that vary from the aspects that stay the same.  If your design must work within several different contexts, identify what is different between those contexts and what stays the same.  If you can separate the part that stays the same, then you only need to worry about adapting the part that differs.

Saturday, January 10, 2015

Fundamental Software Design Concepts

As with all areas of formalized knowledge, the field of software design is built upon a set of fundamental concepts.  These concepts give us a list of general design goals, and when we combine them in meaningful ways, they produce a set of tried-and-true principles for creating high-quality software designs.

Abstraction means that every class should only contain the data and functionality that it needs to serve its purpose – no more and no less.  Abstraction takes different forms, but it is generally something that we want to strive for.

Coupling means there is a dependency between two or more classes.  We want to minimize this for several reasons:  It makes it difficult to reuse code and complicates testing and maintenance efforts.  We cannot eliminate coupling completely because it is necessary for classes to collaborate.  However, we should aim to minimize it whenever possible.

Cohesion is a measure of how closely the members of a class are related.  A class should have one single well-defined purpose, and everything it contains should contribute to that one purpose.

Decomposition means that large, complex things should be divided into smaller, simpler things.

Modularization means that each component should have a specific, non-redundant purpose and well-defined interfaces.  This is related to decomposition.

Encapsulation means that the details of what an entity is made of (its variables and functions) are bundled into a single unit, such as a class.

Information hiding means that the encapsulated details of a class can be hidden from external entities for the sake of simplicity as well as reliability.  This is related to encapsulation.

Separation of interface and implementation means that the interface to a class should be separate from how it actually works behind the scenes.  This is a specific type of information hiding.

Sufficiency means that a component contains enough of its essential features to be usable.

Completeness means that a component contains everything that it needs and nothing more.

Primitiveness means that the design should be based on patterns that are easy to implement.

Separation of concerns means that stakeholders can focus on just a few issues at a time rather than trying to grasp the entire system at once.