[G_PRO] Basis Programmeren en OO Programmeren
DigitAP
  • Welkom
  • Inleiding
    • Benodigdheden
    • Afspraken code
    • Afspraken oefeningen
    • Nuttige extras
    • Dankwoord
    • Mee helpen?
  • Semester 1: Programming Principles
    • H1: Werken met Visual Studio
      • Introductie tot C#
      • Visual Studio en .NET Core installeren
      • Een C# project maken in Visual Studio
      • Fouten in je code
      • Je eerste stappen in C#
      • Input/Output: ReadLine/WriteLine
      • Kleuren in Console
      • Oefeningen
    • H2: Variabelen en datatypes
      • De syntaxis van C#
      • Datatypes
      • Variabelen
      • Expressies en operators
      • Oefeningen
    • H3: Strings en hun methoden
      • Strings
      • Strings samenvoegen
      • Omzetten van en naar strings
      • Functionaliteit van strings
      • Oefeningen
    • H4: Beslissingen
      • Beslissingen intro
      • Enkelvoudige booleaanse expressies
      • If, else, else if
      • Samengestelde booleaanse expressies
      • Scope van variabelen
      • Switch
      • Oefeningen
    • H5: Loops
      • Loops intro
      • While en Do While
      • For
      • Debuggen
      • Oefeningen
    • H6: Arrays
      • Array principes
      • Alternatieve syntax
      • Werken met arrays
      • Defaultwaarden
      • List<T>
      • Oefeningen
    • H7: Methoden
      • Methoden intro
      • Parameters
      • Return waarden
      • Geavanceerde methoden
      • Oefeningen
    • Intermezzo: TextCell
    • H8: Numerieke data
      • De Math klasse
      • Random
      • Casting en conversie
      • Oefeningen
    • H9: Meerdimensionaal werken
      • N-dimensionale arrays
      • Geneste iteratie
      • Oefeningen
    • H10: Gevorderde tekstverwerking
      • Voorstelling van tekst
      • Interpolatie met formattering
      • Werken met arrays van strings
      • Input en output van tekstbestanden
      • Oefeningen
    • Afsluiter: TextCell2D
  • Semester 2 : OOP
    • H10: Klassen en objecten
      • OOP Intro
      • Klassen en objecten aanmaken
      • DateTime: leren werken met objecten
      • Enumeraties: nog een eigen datatype
      • Klassen en objecten weergeven deel 1
      • Attributen
      • Methoden
      • Access modifiers
      • Properties
      • Oefeningen
    • H11: Objecten (al dan niet) aanmaken
      • Constructors
      • Spelen met strings
      • Oefeningen
    • H12: Geheugenmanagement bij klassen
      • value en reference met eigen objecten
      • nullable value types
      • NullReference exception
      • Oefeningen
    • H13: Datastructuren
      • Foreach en var
      • List
      • Dictionary
      • Immutable datastructuren
      • Verdere datastructuren
      • Oefeningen
    • H14: Overerving
      • Overerving intro
      • Virtual en override
      • Abstract
      • Constructors bij overerving
      • Oefeningen
    • H15: Geavanceerde overerving
      • protected access modifier
      • Base keyword
      • System.Object
      • Oefeningen
    • H16: Exception handling
      • Werken met exceptions
      • Zelf uitzonderingen maken
      • Wanneer exceptions en handling gebruiken
      • Oefeningen
    • H17: Polymorfisme en interfaces
      • Polymorfisme
      • Polymorfisme in de praktijk
      • Interfaces
      • Losse koppeling
      • Oefeningen
    • H18: Testing
      • Intro Testing
      • Wat is Unit Testing
      • Waarom Unit Testing?
      • Wanneer Unit Testing?
      • Schrijven van een unit test: AAA methode
      • Eerste voorbeeld: Sum
      • Assert
      • Oefening even of oneven getal
      • TestInitialize en DataRows
      • Oefening BMI
      • Exception testing
      • Oefening BMI exception
      • Oefening SchoolAdmin test null-waarden en TestCleanup
      • Oefening SchoolAdmin test equals
      • Oefening SchoolAdmin test cursus zoeken met id
      • Dependencies bij Unit Testing
      • Mocking
      • Oefeningen Mocking
      • Test Driven Development
      • Class Library
      • Oefeningen TDD
    • H19: SOLID
      • Single Responsibility Principle (SRP)
      • Open/Closed Principle (OCP)
      • Liskov Substitution Principle (LSP)
      • Interface Segregation Principle (ISP)
      • Dependency Inversion Principle (DIP)
  • Appendix
    • Visual Studio Tips & Tricks
    • Ea-ict coding guidelines
    • Oefeningen kerkhof
  • Semester 1 appendix
    • Nice to know stuff
      • Out en Ref parameters
      • Jagged arrays
    • All-In-Projecten
      • Overzicht
      • Console Matrix
      • Ascii filmpjes maken met loops
      • Ascii filmpjes maken met methoden
      • Fun with methods: een verhaalgenerator
      • Tekst-gebaseerd Maze game
      • Conway game of life
  • Semester 2 appendix
    • Operator overloading
    • Object Initializer Syntax
    • Compositie en aggregatie
    • Nice to know stuff
      • Klassen herbruiken
      • Expression bodied members
    • All-In-Projecten
      • Overzicht
      • OO Textbased Game
      • War Simulator
      • Map Maker
      • Magic The Gathering API
      • SchoolAdmin
  • Pro (geen leerstof en/of in opbouw)
    • Bitwise operators
    • Generics en collections
      • Generics methoden en types
      • Generic classes en constraints
      • Collections
      • Labo-oefeningen
    • Events
      • Events
      • Chat server
    • Software engineering
      • SOLID
Powered by GitBook
On this page
Export as PDF
  1. Semester 2 : OOP
  2. H19: SOLID

Liskov Substitution Principle (LSP)

Het Liskov Substitution Principle (LSP) heeft betrekking op de relatie tussen klassen en hun subklassen (afgeleide klassen). Het stelt dat objecten van afgeleide klassen zich moeten kunnen gedragen als objecten van hun basisklassen zonder ongewenst gedrag te introduceren. Met andere woorden, je moet een object van een subklasse kunnen gebruiken als een object van de basisklasse, zonder dat dit leidt tot fouten of inconsistent gedrag.

In C# wordt het LSP vaak bereikt door ervoor te zorgen dat de subklasse de gedragscontracten (interfaces of overerving) van de basisklasse naleeft en niet in strijd is met de verwachtingen van de gebruikers van de basisklasse.

Laten we dit principe verder illustreren met een voorbeeld:

Stel je hebt een hiërarchie van vormen met een basisklasse Shape en twee afgeleide klassen: Rectangle en Circle. Het LSP vereist dat een object van een subklasse (bijvoorbeeld Rectangle of Circle) naadloos kan worden vervangen door een object van de basisklasse (Shape) zonder dat dit tot problemen leidt.

public class Shape
{
	public virtual double CalculateArea()
	{
    	  // Basisklasse heeft een standaardgedrag voor het berekenen van het gebied
    	  return 0;
	}
}

public class Rectangle : Shape
{
        public double Width { get; set; }
	public double Height { get; set; }

	public override double CalculateArea()
	{
    		return Width * Height;
	}
}

public class Circle : Shape
{
	public double Radius { get; set; }

	public override double CalculateArea()
	{
    		return Math.PI * Radius * Radius;
	}
}

In dit voorbeeld implementeren zowel Rectangle als Circle de CalculateArea-methode die wordt geërfd van de basisklasse Shape. Hierdoor kunnen objecten van beide subklassen naadloos worden vervangen door objecten van de basisklasse in situaties waarin het berekenen van het gebied nodig is.

Het Liskov Substitution Principle zorgt ervoor dat wanneer we een object van een subklasse gebruiken in plaats van een object van de basisklasse, het verwachte gedrag behouden blijft. Het helpt om onverwachte fouten en inconsistente gedragingen in je code te vermijden.

Laten we eens kijken naar een voorbeeld waar we zondigen tegen dit principe. Stel je voor dat je een hiërarchie van dieren hebt, met een basisklasse Animal en twee afgeleide klassen: Dog en Cat. Laten we aannemen dat elke dierklasse een methode heeft genaamd MakeSound() om het geluid van het dier weer te geven.

Zonder Liskov Substitution Principle:

public class Animal
{
	public virtual void MakeSound()
	{
    		Console.WriteLine("Geluid van een dier");
	}
}

public class Dog : Animal
{
	public override void MakeSound()
	{
    		Console.WriteLine("Woef woef!");
	}
}

public class Cat : Animal
{
	public override void MakeSound()
	{
    		Console.WriteLine("Miauw miauw!");
	}
}

Op het eerste gezicht lijkt dit correct, maar laten we een verkeerde implementatie van het Liskov Substitution Principle introduceren:

public class WrongDog : Animal
{
	public override void MakeSound()
	{
    		Console.WriteLine("Miauw miauw!"); // Fout geluid voor een hond
	}
}

In dit geval voldoet WrongDog niet aan het Liskov Substitution Principle. Als we WrongDog gebruiken waar we een Dog-object verwachten, zoals in een scenario waar honden geluiden maken, zouden we echter onverwacht gedrag krijgen, aangezien het geluid dat WrongDog maakt niet overeenkomt met een hond.

Dit kan leiden tot verwarring bij ontwikkelaars die de code gebruiken, onverwachte resultaten veroorzaken en zelfs ernstige fouten in applicaties introduceren.

Met het Liskov Substitution Principle zouden alle subklassen (zoals Dog en Cat) zich moeten gedragen op een manier die compatibel is met het verwachte gedrag van de basisklasse (Animal). In dit geval zou het de verwachting zijn dat het geluid van een hond wordt gereproduceerd wanneer de MakeSound()-methode van een Dog-object wordt aangeroepen.

Een ander bekend voorbeeld is de "wekkerklasse".

In de Wekkerklasse staan de functie maakGeluid en het veld geluid. Op het moment dat de Wekker hoort af te gaan wordt de functie maakGeluid aangeroepen en gaat het alarm af. Een analoge wekker is een wekker en zou dus Wekker moeten overerven. Ook een digitale wekker is een wekker en erft dus van de wekkerklasse. De dovenwekker is ook een wekker en zou dus moeten overerven van Wekker. Maar nu is er een probleem, deze wekker trilt in plaats van dat hij geluid maakt. De functie maakGeluid heeft dus geen duidelijke implementatie. In .net resulteert dit vaak in het throwen van een NotImplemented exception.

Dit gaat dus in tegen het substitutieprincipe van Liskov: de methode maakGeluid is een methode van Wekker, maar maakGeluid hoort niet bij DovenWekker, terwijl DovenWekker een Wekker is.

PreviousOpen/Closed Principle (OCP)NextInterface Segregation Principle (ISP)

Last updated 1 year ago