S.O.L.I.D Principles

S.O.L.I.D Principles

Home \ Journal \ S.O.L.I.D Principles
Published: 12/08/2019

Solid Principles C#

SRP - Single Responsibility Principle
OCP - Open/Close Principle 
LSP - Liskov Substitute Principle 
ISP - Interface Segregation Principle 
DIP - Dependency Inversion Principle

PPD - Pain Driven Development

Single Responsibility Principle | SRP

  • Each software module should have one and only one reason to change (A module is a class or function).
  • Tight Coupling - binds two or more details together in a way that is difficult to change.
  • Loose coupling - offers a modular way to choose which details are involved in a particular operation.
  • Separation of concerns - programs should be separated into distinct sections, each addressing a separate concern or set of information that affects the program.
  • Using SRP can help simplify writing unit tests.
  • Each class should have one responsibility or reason to change.
  • Keep your classes small and focussed.

Open/ Close Principle | OCP

  • Software entities (classes, modules, functions etc.) should be open for extension, but closed for modification.
  • It should be possible to chan ge the behaviour of a method without editing its source code.
  • You should think about OCP so that you're not having to continually apply surgery on the same function every time a new requirement arises.
  • Why should code be closed to modification!?
  • You're then less likely to introduce bugs in code we don't touch or re-deploy. Also less likely to break dependent code. Remember the phrase 'new is glue'

How to predict future changes?

  1. Start Concrete.
  2. Modify the code 1 or 2 times.
  3. By the 3rd modification, consider making the code 'open' to extension.

Typical approaches to OCP:

  1. Using parameters () and adding arguments.
  2. Inheritance.
  3. Composition and Injection.

Liskov Substitute Principle | LSP

Sub types must be Substitutes for their base types.

Types of Inheritance

  • IS-A - (something is-a something else) eg: an eagle IS-A bird.
  • HAS-A - (something has-a property) eg: An address HAS-A city.
  • LSP states that the IS-A relationship is insufficient and should be replaced with IS-SUBSTITUTABLE-FOR.

EXAMPLE:

  • A rectangle has four sides and four right angles.
  • A square has four equal sides and four right angles.
  • Per geometry a square IS-A rectangle as a square has an invariant (its sides must be equal).
  • Rectangle has an invariant its sides are independent. This design breaks rectangles invariants and thus violates LSP.

Fixing LSP violations:

  • Follow the "tell, don't ask" Principle.
  • Minimise null checks and try to use: C# alternatives, Guard Clauses and null object design pattern.
  • Follow ISP and be sure to fully implement interfaces.

Interface Segregation Principle

Clients should not be forced to depend on methods they do not use.

What does interface mean?

  1. C# interface type/ keyword.
  2. public (or accessible) interface of a class.
  3. A types interface in this context is whatever can be accessed by client code working with an instance of that type.

What's a client?

In this context the client is the code that is interacting with an instance of the interface. It's the calling code.

Detecting ISP violations in your code:

  1. Large interfaces
  2. NotImplimentedException Code uses just a small subset of a larger interface.

Fixing ISP violations:

  1. Break up large interfaces into smaller ones.
  2. To address large interfaces you don't control
  3. Create a small, cohesive interface.

Dependency Inversion Principle

  • High-Level modules should not depend on low-level modules. Both should depend on abstractions.
  • Abstractions should not depend on details.
  • Details should depend on Abstractions.

Dependencies in C#

  • References required to compile.
  • References required to run.

HIGH-LEVEL:

  • More Abstract
  • Business rules
  • Process orientated
  • Further From input/output (i/o)

LOW-LEVEL:

  • Closer to input/output (i/o)
  • "Plumbing code"
  • Interacts with specific external systems and hardware.

LOW-LEVEL DEPENDENCIES

  • Database
  • File systems
  • Email
  • Web API's
  • Configuration
  • Clock

Separation of concerns

  • Abstractions in C# are: interfaces and abstract base classes.
  • Types you can't instantiate
  • Abstractions shouldn't be coupled to details
  • Abstractions describe what: send a message | store a customer record
  • Details specify how: send an SMTP email over port 25 | serialize customer to JSON and store in txt file.