SOLID
The Principles of Clean Object-Oriented Programming
Attila Bertók
C# technical lead
bertok.atti@gmail.com
https://www.linkedin.com/in/bertokattila/
SOLID
 Five principles of agile object-oriented software design
 Robert C. Martin: Agile Software Development: Principles, Patterns, and
Practices (2002)
 Robert C. Martin: Agile Software Development: Principles, Patterns, and
Practices in C# (2003)
 Robert C. Martin: Clean Code (2008)
 Robert C. Martin: Clean Architecture (2017)
SOLID – SRP
Single Responsibility Principle
SOLID - SRP
 The least well understood principle of SOLID. 
 It is not about the module (method/class/component) doing exactly one
thing.
 Correct phrasing:
“A module should be responsible to one, and only one, user or stakeholder – and
thus have one, and only one, reason to change.”
 This, in fact, does nevertheless contain the above definition of a module
doing exactly one thing.
Cohesion & Coupling
 We will discuss these in more detail when discussing Static Code Analysis
Metrics
 Coupling: interdependency between modules
 Cohesion: relatedness of functionality within one module
 Low Coupling and High Cohesion should be the goal
 SRP is basically stating that “Cohesion should be as high as possible”
SOLID - SRP
 “A class should have only one reason to change”
 As an example, consider a module that compiles and prints a report.
 Such a module can be changed for two reasons.
 The content of the report could change.
 The format of the report could change.
 These two things change for very different causes; one substantive, and one
cosmetic.
 The single responsibility principle says that these two aspects of the problem
are really two separate responsibilities, and should therefore be in separate
classes or modules. It would be a bad design to couple two things that change
for different reasons at different times.
Another Example: The Modem
public interface Modem
{
public void Dial(string phoneNumber);
public void Hangup();
public void Send(char c);
public char Receive();
}
Modem Example, Part 2
<<I>> Data Channel
+ Send(char)
+ Receive : char
<<I>> Connection
+ Dial(string)
+ Hangup()
Modem
// with two responsibilities
SOLID - SRP
 One “thing” should do exactly one “thing”
 What is a “thing”?
 Usually a class
 Sometimes a function
 Trivially a variable
 Would you write code like this?
bool isLowerCase = string.Equals(input, input.ToLower());
if (isLowerCase) {
isLowerCase = input.Length < 50;
// why declare another bool, we no longer use the previous one, lets repurpose!
}
SOLID - SRP
 Why would you then do something like this?
public void DoStuff(bool flag) {
if (flag) {
// do something
} else {
// do something else
}
}
 The above is only correct, if DoStuff() is a wrapper function, that you use to
dynamically invoke either DoSomething() or DoSomethingElse()
SOLID - SRP
 The same holds true for classes
 One class should only be responsible for one single thing
 So, increase cohesion!
 Use small classes
 Reduce the number of instance variables!
 Move functionality on different level of abstraction to a separate class
 Methods that do not manipulate instance variables might not be part of the class (static
methods)
 Use composition
An Example of Strong Cohesion
 Demo
SOLID – O/CP
Open/Closed Principle
SOLID – O/CP
 Classes should be open for extension but closed for modification
 Changes should be localizable – a single change should not cascade down and
require dependent modules to change as well
 Changes should be achieved by adding new code instead of modified existing
code
SOLID – O/CP
 Open for extension: This means that the behavior of the module can be
extended. As the requirements of the application change, we can extend the
module with new behaviors that satisfy those changes. In other words, we are
able to change what the module does.
 Closed for modification: Extending the behavior of a module does not result
in changes to the source, or binary, code of the module. The binary
executable version of the module whether in a linkable library, a DLL, or a
.EXE file remains untouched.
 Is it possible to satisfy both criteria at once?
SOLID – O/CP
 Use abstraction!
 An interface or an abstract base class can be fixed, thus closed for
modification. However, the implementation is an extension point, so the
abstract module remains open for extension.
 Some common design patterns to satisfy OCP are:
 Strategy
 Template Method
 Visitor
SOLID – LSP
Liskov Substitution Principle
SOLID – LSP
 Derived classes can be used instead of the classes they derive of
Animal
Bird Dog
Move
Fly Walk
SOLID – LSP
Bad solution
public class Bird : IAnimal {
public void Move() {
throw
NotImplementedException
(“Use fly()!”);
}
public void Fly() {
FlapWings();
}
}
Good solution
public class Bird : IAnimal {
public void Move() {
Fly();
}
public void Fly() {
FlapWings();
}
}
SOLID – LSP
public class Bird : IAnimal {
public void Move() {
Fly();
}
}
public class Dog : IAnimal {
public void Move() {
Walk();
}
}
public void MoveAllAnimals(IEnumerable<IAnimal> animals) {
foreach (animal in Animals) {
animal.Move();
}
}
SOLID – LSP
 Common example of breaking the LSP: the Square/Rectangle problem
 A square is (by mathematic definition) a rectangle.
 public class Rectangle {
public virtual int Height { get; set; }
public virtual int Width { get; set; }
}
 public class Square : Rectangle {
private int side;
public override int Height {
get { return side; }
set { side = value; }
}
public override int Width {
get { return side; }
set { width = side; }
}
}
SOLID – LSP
 Nah, that’s okay, where’s the issue?
 Well…
Rectangle r = RectangleFactory.Create();
r.Height = 2;
r.Width = 5;
r.Area.Should().Be(10);
works just fine if the RectangleFactory returns a Rectangle, but fails, if it returns a
Square – which is a valid return value, as a square is a rectangle in our
implementation.
SOLID – ISP
Interface Segregation Principle
SOLID – ISP
 Use multiple small interfaces
 Split interfaces by functional boundaries
 When a method only requires something related to the interface, use the
interface as the type of the input parameter
 Why?
 So that your client won’t do anything stupid.
 So that your client doesn’t depend on things it does not need.
SOLID – ISP
Bad solution
A solution I’ve worked on: IInstance.
 428 lines of code
 100+ methods
 8 regions:
 Configuration and Tracing
 LoginManager
 WMSManager
 ProductionManager
 …
Good solution
IInstance : ILoginManager,
IWmsManager, IProductionManager, …
Usage:
ProductionEngineWrapper
(IProductionManager
productionManager) {
// can only call
// production related code
// from this method
// but not WMS-related
}
Another Example
class Printer
{
public enum PaperType { A3, A4, Letter };
public PaperType Paper { get; set; }
}
class MyGraph
{
// details
}
Another Example, cont’d
static void Main()
{
MyGraph graph = new MyGraph();
graph.Load(@"C:settings.txt);
PrintAGraphInA4(graph);
}
static void PrintAGraphInA4(MyGraph
graph)
{
// Note:
// the code only needed access
// to the Print Method
// but was exposed to
// Load/Save/
// MathModel/GraphTitle/etc.
Printer printer = new Printer();
printer.Paper =
Printer.PaperType.A4;
graph.Print(printer);
}
Another Example, corrected
class Printer
{
public enum PaperType { A3, A4, Letter };
public PaperType Paper { get; set; }
}
interface IPrintable
{
void Print(Printer printerDevice);
}
class MyGraph : IPrintable
{
// details
}
Corrected example, continued
static void Main()
{
MyGraph graph = new MyGraph();
graph.Load(@"C:settings.txt”);
// because inheritance is used for
// MyGraph : Iprintable
// we can simply pass the graph
// to automatically cast
// to Iprintable
PrintInA4(graph);
}
static void PrintInA4(IPrintable
printable)
{
// we do not care
// what we are printing.
Printer printer = new Printer();
printer.Paper =
Printer.PaperType.A4_PAPER;
printable.Print(printer);
}
SOLID - DIP
Dependency Inversion Principle
SOLID - DIP
 Rely on abstractions and interfaces, not concrete implementations
 High level modules should not depend on low level modules.
 Why?
 See O/C principle! Implementations are subject to change, abstractions are usually
not!
 The level of abstraction is broken. Your object should not care about the lifetime
of the objects it uses.
 Objects should be used
 Not created and destroyed
SOLID - DIP
Bad solution
public class MainViewModel {
public IInstructionViewModel
InstructionViewModel
{ get; set; }
public IProductionViewModel
ProductionViewModel
{ get; set; }
public MainViewModel() {
InstructionViewModel = new()
ProductionViewModel = new()
}
}
Good solution
public class MainViewModel {
public IInstructionViewModel
InstructionViewModel
{ get; set; }
public IProductionViewModel
ProductionViewModel
{ get; set; }
public MainViewModel(
IInstructionViewModel ivm,
IProductionViewModel pvm) {
this.InstructionViewModel = ivm
ProductionViewModel = pvm
}
}
 Where do I get my dependencies from?
 Factory methods
 It’s not “Enterprise Code©®™” without factories!
 DI framework! (Service Locator or Dependency Container patterns)
Q&A
Questions?
Thank you!

An Introduction to the SOLID Principles

  • 1.
    SOLID The Principles ofClean Object-Oriented Programming
  • 2.
    Attila Bertók C# technicallead bertok.atti@gmail.com https://www.linkedin.com/in/bertokattila/
  • 3.
    SOLID  Five principlesof agile object-oriented software design  Robert C. Martin: Agile Software Development: Principles, Patterns, and Practices (2002)  Robert C. Martin: Agile Software Development: Principles, Patterns, and Practices in C# (2003)  Robert C. Martin: Clean Code (2008)  Robert C. Martin: Clean Architecture (2017)
  • 4.
    SOLID – SRP SingleResponsibility Principle
  • 5.
    SOLID - SRP The least well understood principle of SOLID.   It is not about the module (method/class/component) doing exactly one thing.  Correct phrasing: “A module should be responsible to one, and only one, user or stakeholder – and thus have one, and only one, reason to change.”  This, in fact, does nevertheless contain the above definition of a module doing exactly one thing.
  • 6.
    Cohesion & Coupling We will discuss these in more detail when discussing Static Code Analysis Metrics  Coupling: interdependency between modules  Cohesion: relatedness of functionality within one module  Low Coupling and High Cohesion should be the goal  SRP is basically stating that “Cohesion should be as high as possible”
  • 7.
    SOLID - SRP “A class should have only one reason to change”  As an example, consider a module that compiles and prints a report.  Such a module can be changed for two reasons.  The content of the report could change.  The format of the report could change.  These two things change for very different causes; one substantive, and one cosmetic.  The single responsibility principle says that these two aspects of the problem are really two separate responsibilities, and should therefore be in separate classes or modules. It would be a bad design to couple two things that change for different reasons at different times.
  • 8.
    Another Example: TheModem public interface Modem { public void Dial(string phoneNumber); public void Hangup(); public void Send(char c); public char Receive(); }
  • 9.
    Modem Example, Part2 <<I>> Data Channel + Send(char) + Receive : char <<I>> Connection + Dial(string) + Hangup() Modem // with two responsibilities
  • 10.
    SOLID - SRP One “thing” should do exactly one “thing”  What is a “thing”?  Usually a class  Sometimes a function  Trivially a variable  Would you write code like this? bool isLowerCase = string.Equals(input, input.ToLower()); if (isLowerCase) { isLowerCase = input.Length < 50; // why declare another bool, we no longer use the previous one, lets repurpose! }
  • 11.
    SOLID - SRP Why would you then do something like this? public void DoStuff(bool flag) { if (flag) { // do something } else { // do something else } }  The above is only correct, if DoStuff() is a wrapper function, that you use to dynamically invoke either DoSomething() or DoSomethingElse()
  • 12.
    SOLID - SRP The same holds true for classes  One class should only be responsible for one single thing  So, increase cohesion!  Use small classes  Reduce the number of instance variables!  Move functionality on different level of abstraction to a separate class  Methods that do not manipulate instance variables might not be part of the class (static methods)  Use composition
  • 13.
    An Example ofStrong Cohesion  Demo
  • 14.
  • 15.
    SOLID – O/CP Classes should be open for extension but closed for modification  Changes should be localizable – a single change should not cascade down and require dependent modules to change as well  Changes should be achieved by adding new code instead of modified existing code
  • 16.
    SOLID – O/CP Open for extension: This means that the behavior of the module can be extended. As the requirements of the application change, we can extend the module with new behaviors that satisfy those changes. In other words, we are able to change what the module does.  Closed for modification: Extending the behavior of a module does not result in changes to the source, or binary, code of the module. The binary executable version of the module whether in a linkable library, a DLL, or a .EXE file remains untouched.  Is it possible to satisfy both criteria at once?
  • 17.
    SOLID – O/CP Use abstraction!  An interface or an abstract base class can be fixed, thus closed for modification. However, the implementation is an extension point, so the abstract module remains open for extension.  Some common design patterns to satisfy OCP are:  Strategy  Template Method  Visitor
  • 18.
    SOLID – LSP LiskovSubstitution Principle
  • 19.
    SOLID – LSP Derived classes can be used instead of the classes they derive of Animal Bird Dog Move Fly Walk
  • 20.
    SOLID – LSP Badsolution public class Bird : IAnimal { public void Move() { throw NotImplementedException (“Use fly()!”); } public void Fly() { FlapWings(); } } Good solution public class Bird : IAnimal { public void Move() { Fly(); } public void Fly() { FlapWings(); } }
  • 21.
    SOLID – LSP publicclass Bird : IAnimal { public void Move() { Fly(); } } public class Dog : IAnimal { public void Move() { Walk(); } } public void MoveAllAnimals(IEnumerable<IAnimal> animals) { foreach (animal in Animals) { animal.Move(); } }
  • 22.
    SOLID – LSP Common example of breaking the LSP: the Square/Rectangle problem  A square is (by mathematic definition) a rectangle.  public class Rectangle { public virtual int Height { get; set; } public virtual int Width { get; set; } }  public class Square : Rectangle { private int side; public override int Height { get { return side; } set { side = value; } } public override int Width { get { return side; } set { width = side; } } }
  • 23.
    SOLID – LSP Nah, that’s okay, where’s the issue?  Well… Rectangle r = RectangleFactory.Create(); r.Height = 2; r.Width = 5; r.Area.Should().Be(10); works just fine if the RectangleFactory returns a Rectangle, but fails, if it returns a Square – which is a valid return value, as a square is a rectangle in our implementation.
  • 24.
    SOLID – ISP InterfaceSegregation Principle
  • 25.
    SOLID – ISP Use multiple small interfaces  Split interfaces by functional boundaries  When a method only requires something related to the interface, use the interface as the type of the input parameter  Why?  So that your client won’t do anything stupid.  So that your client doesn’t depend on things it does not need.
  • 26.
    SOLID – ISP Badsolution A solution I’ve worked on: IInstance.  428 lines of code  100+ methods  8 regions:  Configuration and Tracing  LoginManager  WMSManager  ProductionManager  … Good solution IInstance : ILoginManager, IWmsManager, IProductionManager, … Usage: ProductionEngineWrapper (IProductionManager productionManager) { // can only call // production related code // from this method // but not WMS-related }
  • 27.
    Another Example class Printer { publicenum PaperType { A3, A4, Letter }; public PaperType Paper { get; set; } } class MyGraph { // details }
  • 28.
    Another Example, cont’d staticvoid Main() { MyGraph graph = new MyGraph(); graph.Load(@"C:settings.txt); PrintAGraphInA4(graph); } static void PrintAGraphInA4(MyGraph graph) { // Note: // the code only needed access // to the Print Method // but was exposed to // Load/Save/ // MathModel/GraphTitle/etc. Printer printer = new Printer(); printer.Paper = Printer.PaperType.A4; graph.Print(printer); }
  • 29.
    Another Example, corrected classPrinter { public enum PaperType { A3, A4, Letter }; public PaperType Paper { get; set; } } interface IPrintable { void Print(Printer printerDevice); } class MyGraph : IPrintable { // details }
  • 30.
    Corrected example, continued staticvoid Main() { MyGraph graph = new MyGraph(); graph.Load(@"C:settings.txt”); // because inheritance is used for // MyGraph : Iprintable // we can simply pass the graph // to automatically cast // to Iprintable PrintInA4(graph); } static void PrintInA4(IPrintable printable) { // we do not care // what we are printing. Printer printer = new Printer(); printer.Paper = Printer.PaperType.A4_PAPER; printable.Print(printer); }
  • 31.
    SOLID - DIP DependencyInversion Principle
  • 32.
    SOLID - DIP Rely on abstractions and interfaces, not concrete implementations  High level modules should not depend on low level modules.  Why?  See O/C principle! Implementations are subject to change, abstractions are usually not!  The level of abstraction is broken. Your object should not care about the lifetime of the objects it uses.  Objects should be used  Not created and destroyed
  • 33.
    SOLID - DIP Badsolution public class MainViewModel { public IInstructionViewModel InstructionViewModel { get; set; } public IProductionViewModel ProductionViewModel { get; set; } public MainViewModel() { InstructionViewModel = new() ProductionViewModel = new() } } Good solution public class MainViewModel { public IInstructionViewModel InstructionViewModel { get; set; } public IProductionViewModel ProductionViewModel { get; set; } public MainViewModel( IInstructionViewModel ivm, IProductionViewModel pvm) { this.InstructionViewModel = ivm ProductionViewModel = pvm } }
  • 34.
     Where doI get my dependencies from?  Factory methods  It’s not “Enterprise Code©®™” without factories!  DI framework! (Service Locator or Dependency Container patterns)
  • 35.
  • 36.