Software Design Principles


Design principles lay your design foundation. Design patterns are built on top of design principles. Design principles come first and are more fundamental than design patterns. When you follow proven design principles, your codebase becomes more flexible and maintainable.

Common Design Principles

There are a number of common design principles that, like design patterns, have become best practice over the years and helped to form a foundation onto which enterprise-level and maintainable software can be built.

Keep It Simple (KIS)

An all-too-common issue in software programming is the need to overcomplicate a solution. The goal of the KISS principle is concerned with the need to keep code simple but not simplistic, thus avoiding any unnecessary complexities. For example, sometimes using recursion results in a much more elegant solution that not using recursion.

Don’t Repeat Yourself (DRY)

The DRY principle aims to avoid repetition of any part of a system by abstracting out things that are common and placing those things in a single location. This principle is not only concerned with code but any logic that is duplicated in a system; ultimately there should only be one representation for every piece of knowledge in a system. For example, you may create a function that your code can call many times.

Tell, Don’t Ask

The Tell, Don’t Ask principle is closely aligned with encapsulation and the assigning of responsibilities to their correct classes. The principle states that you should tell objects what actions you want them to perform rather than asking questions about the state of the object and then making a decision yourself on what action you want to perform. This helps to align the responsibilities and avoid tight coupling between classes.

You Ain’t Gonna Need It (YAGNI)

The YAGNI principle refers to the need to only include functionality that is necessary for the application and put off any temptation to add other features that you may think you need. A design methodology that adheres to YAGNI is test-driven development (TDD). TDD is all about writing tests that prove the functionality of a system and then writing only the code to get the test to pass. TDD is discussed a little later in this chapter.

Separation of Concerns (SoC)

SoC is the process of dissecting a piece of software into distinct features that encapsulate unique behavior and data that can be used by other classes. Generally, a concern represents a feature or behavior. The act of separating a program into discrete responsibilities significantly increases code reuse, maintenance, and testability.

Functional Programming’s Top-Down and Bottom-up Strategies

Wikipedia says: “Top-down is a programming style, the mainstay of traditional procedural languages, in which design begins by specifying complex pieces and then dividing them into successively smaller pieces. The technique for writing a program using top–down methods is to write a main procedure that names all the major functions it will need”. “Top-down” is also known as stepwise design and stepwise refinement and it imposes a hierarchical structure on the design of the program. Wiki later says: “In a bottom-up approach, the individual base elements of the system are first specified in great detail”. Top-down and bottom-up are both valid strategies of information processing and knowledge ordering.

S.O.L.I.D.

The next group of design principles are S.O.L.I.D. design principles. The principles are a subset of many principles promoted by American software engineer and instructor Robert C. Martin.

Single Responsibility Principle (SRP)

The principle of SRP is closely aligned with SoC. It states that every object should only have one reason to change and a single focus of responsibility.

Open-Closed Principle (OCP)

The OCP states that classes should be open for extension and closed for modification, in that you should be able to add new features and extend a class without changing its internal behavior. How? We have a base class and a bunch of subclasses. We just need to add a new subclass when changes to the code are needed. No modifications are needed.

Liskov Substitution Principle (LS P)

The LSP dictates that you should be able to use any derived class in place of a parent class and have it behave in the same manner without modification.

Interface Segregation Principle (ISP)

The ISP is all about splitting the methods of a contract into groups of responsibility and assigning interfaces to these groups to prevent a client from needing to implement one large interface and a host of methods that they do not use.

Dependency Inversion Principle (DIP)

The DIP is all about isolating your classes from concrete implementations and having them depend on abstract classes or interfaces. It promotes the mantra of coding to an interface rather than an implementation, which increases flexibility within a system by ensuring you are not tightly coupled to one implementation.

Dependency Injection (DI) and Inversion of Control (IoC)

Closely linked to the DIP are the DI principle and the IOC principle, DI is the act of supplying a low level or dependent class via a constructor, method, or property. Used in conjunction with DI, these dependent classes can be inverted to interfaces or abstract classes that will lead to loosely coupled systems that are highly testable and easy to change.