Patters Flashcards
Singleton
Beschrijving: Het Singleton-patroon zorgt ervoor dat er slechts één instantie van een klasse bestaat en biedt een globale toegangspunt tot deze instantie. Dit patroon wordt vaak gebruikt voor gedeelde bronnen zoals configuratie-instellingen, logboekregistratie, of een databaseverbinding.
public class Singleton
{
// Privé statisch veld om de enkele instantie op te slaan.
private static Singleton _instance;
// Lock object voor thread-safe toegang. private static readonly object _lock = new object(); // Privé constructor om directe instantiatie te voorkomen. private Singleton() { } // Publieke eigenschap om de enkele instantie te verkrijgen. public static Singleton Instance { get { // Dubbele controle voor efficiëntie. if (_instance == null) { lock (_lock) { if (_instance == null) { _instance = new Singleton(); } } } return _instance; } } // Voorbeeldmethode binnen de Singleton-klasse. public void DoSomething() { Console.WriteLine("Singleton instance is doing something!"); } }"
Factory method
Beschrijving: Het Factory Method-patroon definieert een interface of abstracte klasse voor het maken van objecten, maar laat subklassen de daadwerkelijke implementatie bepalen. Het helpt om objectcreatie te scheiden van de implementatiedetails, waardoor de code flexibeler en uitbreidbaar wordt.
// Stap 1: Maak een productinterface
public interface IProduct
{
string Operation();
}
// Stap 2: Implementeer concrete producten
public class ConcreteProductA : IProduct
{
public string Operation()
{
return “Resultaat van ConcreteProductA”;
}
}
public class ConcreteProductB : IProduct
{
public string Operation()
{
return “Resultaat van ConcreteProductB”;
}
}
// Stap 3: Maak een Creator-klasse
public abstract class Creator
{
// Factory Method
public abstract IProduct FactoryMethod();
// Algemeen gedrag dat het product gebruikt public string SomeOperation() { var product = FactoryMethod(); return $"Creator: Werkt met {product.Operation()}"; } }
// Stap 4: Implementeer concrete creators
public class ConcreteCreatorA : Creator
{
public override IProduct FactoryMethod()
{
return new ConcreteProductA();
}
}
public class ConcreteCreatorB : Creator
{
public override IProduct FactoryMethod()
{
return new ConcreteProductB();
}
}
// Stap 5: Gebruik het Factory Method-patroon
class Program
{
static void Main(string[] args)
{
Console.WriteLine(“App: Gelanceerd met ConcreteCreatorA.”);
ClientCode(new ConcreteCreatorA());
Console.WriteLine("\nApp: Gelanceerd met ConcreteCreatorB."); ClientCode(new ConcreteCreatorB()); } static void ClientCode(Creator creator) { Console.WriteLine(creator.SomeOperation()); } }"
Abstract factory
Beschrijving: Het Abstract Factory-patroon biedt een interface om families van gerelateerde of afhankelijke objecten te maken zonder de specifieke klassen te benoemen. Het is nuttig als je verschillende productfamilies hebt die consistent moeten worden gebruikt.
// Stap 1: Definieer productinterfaces
public interface IProductA
{
string FunctionA();
}
public interface IProductB
{
string FunctionB(IProductA collaborator);
}
// Stap 2: Implementeer concrete producten
public class ConcreteProductA1 : IProductA
{
public string FunctionA()
{
return “Resultaat van ConcreteProductA1”;
}
}
public class ConcreteProductA2 : IProductA
{
public string FunctionA()
{
return “Resultaat van ConcreteProductA2”;
}
}
public class ConcreteProductB1 : IProductB
{
public string FunctionB(IProductA collaborator)
{
return $”ConcreteProductB1 werkt samen met ({collaborator.FunctionA()})”;
}
}
public class ConcreteProductB2 : IProductB
{
public string FunctionB(IProductA collaborator)
{
return $”ConcreteProductB2 werkt samen met ({collaborator.FunctionA()})”;
}
}
// Stap 3: Definieer de Abstract Factory interface
public interface IAbstractFactory
{
IProductA CreateProductA();
IProductB CreateProductB();
}
// Stap 4: Implementeer concrete factories
public class ConcreteFactory1 : IAbstractFactory
{
public IProductA CreateProductA()
{
return new ConcreteProductA1();
}
public IProductB CreateProductB() { return new ConcreteProductB1(); } }
public class ConcreteFactory2 : IAbstractFactory
{
public IProductA CreateProductA()
{
return new ConcreteProductA2();
}
public IProductB CreateProductB() { return new ConcreteProductB2(); } }
// Stap 5: Gebruik Abstract Factory in de clientcode
class Program
{
static void Main(string[] args)
{
Console.WriteLine(“Client: Werkt met de eerste productfamilie:”);
ClientMethod(new ConcreteFactory1());
Console.WriteLine("\nClient: Werkt met de tweede productfamilie:"); ClientMethod(new ConcreteFactory2()); } static void ClientMethod(IAbstractFactory factory) { var productA = factory.CreateProductA(); var productB = factory.CreateProductB(); Console.WriteLine(productB.FunctionB(productA)); } } "
Adapter
Beschrijving: Het Adapter-patroon dient als een brug tussen twee incompatibele interfaces. Het wordt gebruikt om een bestaande klasse compatibel te maken met een andere interface zonder de oorspronkelijke klasse aan te passen. Dit patroon is handig als je een bestaande klasse wilt hergebruiken in een systeem met een andere interface.
// Stap 1: Definieer de doelinterface (Target)
public interface ITarget
{
string GetRequest();
}
// Stap 2: Implementeer een bestaande klasse met een incompatibele interface (Adaptee)
public class Adaptee
{
public string GetSpecificRequest()
{
return “Specifieke aanvraag van Adaptee”;
}
}
// Stap 3: Maak een Adapter-klasse die de Adaptee compatibel maakt met de Target
public class Adapter : ITarget
{
private readonly Adaptee _adaptee;
// Constructor injecteert de Adaptee public Adapter(Adaptee adaptee) { _adaptee = adaptee; } // Pas de specifieke aanvraag van de Adaptee aan naar de Target-interface public string GetRequest() { return $"Adapter: (Aangepast) {_adaptee.GetSpecificRequest()}"; } }
// Stap 4: Gebruik het Adapter-patroon in de clientcode
class Program
{
static void Main(string[] args)
{
// Clientcode werkt met de Target-interface
Console.WriteLine(“Client: Ik kan werken met de Target-interface.”);
ITarget target = new Adapter(new Adaptee());
Console.WriteLine(target.GetRequest());
}
}
Composite
Beschrijving: Het Composite-patroon wordt gebruikt om objecten te structureren in een boomstructuur, zodat individuele objecten en groepen van objecten op dezelfde manier behandeld kunnen worden. Het is ideaal voor hiërarchieën zoals bestandsstructuren, organisatieschema’s of grafische UI-componenten.
// Stap 1: Definieer de Component-interface
public interface IComponent
{
void Operation();
}
// Stap 2: Implementeer een Leaf-klasse (blad in de boom)
public class Leaf : IComponent
{
private readonly string _name;
public Leaf(string name) { _name = name; } public void Operation() { Console.WriteLine($"Leaf: {_name} uitgevoerd."); } }
// Stap 3: Implementeer de Composite-klasse (knoop in de boom)
public class Composite : IComponent
{
private readonly List<IComponent> _children = new List<IComponent>();
private readonly string _name;</IComponent></IComponent>
public Composite(string name) { _name = name; } public void Add(IComponent component) { _children.Add(component); } public void Remove(IComponent component) { _children.Remove(component); } public void Operation() { Console.WriteLine($"Composite: {_name} voert operaties uit op kinderen."); foreach (var child in _children) { child.Operation(); } } }
// Stap 4: Gebruik het Composite-patroon in de clientcode
class Program
{
static void Main(string[] args)
{
// Maak bladeren
var leaf1 = new Leaf(“Blad 1”);
var leaf2 = new Leaf(“Blad 2”);
var leaf3 = new Leaf(“Blad 3”);
// Maak een composite en voeg bladeren toe var composite1 = new Composite("Composite 1"); composite1.Add(leaf1); composite1.Add(leaf2); // Maak een andere composite var composite2 = new Composite("Composite 2"); composite2.Add(leaf3); // Voeg composites samen in een grotere composite var rootComposite = new Composite("Root Composite"); rootComposite.Add(composite1); rootComposite.Add(composite2); // Voer de operaties uit Console.WriteLine("Client: Voer operaties uit op de volledige boomstructuur."); rootComposite.Operation(); } }
Flyweight
Beschrijving: Het Flyweight-patroon minimaliseert geheugenverbruik door zoveel mogelijk gedeelde objecten te hergebruiken. Dit is vooral nuttig bij toepassingen waarin veel objecten worden gemaakt die dezelfde gegevens of eigenschappen delen, zoals grafische objecten in een game of tekens in een teksteditor.
Het patroon verdeelt objectgegevens in:
Intrinsieke toestand: Gedeelde gegevens die voor alle objecten hetzelfde zijn.
Extrinsieke toestand: Specifieke gegevens die buiten het gedeelde object worden opgeslagen en dynamisch worden aangeleverd.
// Stap 1: Definieer de Flyweight-interface
public interface IFlyweight
{
void Operation(string extrinsicState);
}
// Stap 2: Implementeer een Concrete Flyweight
public class ConcreteFlyweight : IFlyweight
{
private readonly string _intrinsicState;
public ConcreteFlyweight(string intrinsicState) { _intrinsicState = intrinsicState; } public void Operation(string extrinsicState) { Console.WriteLine($"Flyweight: Intrinsieke toestand = {_intrinsicState}, Extrinsieke toestand = {extrinsicState}"); } }
// Stap 3: Maak een Flyweight Factory
public class FlyweightFactory
{
private readonly Dictionary<string, IFlyweight> _flyweights = new();
public IFlyweight GetFlyweight(string key) { if (!_flyweights.ContainsKey(key)) { Console.WriteLine($"FlyweightFactory: Maak een nieuwe Flyweight voor sleutel '{key}'."); _flyweights[key] = new ConcreteFlyweight(key); } else { Console.WriteLine($"FlyweightFactory: Gebruik bestaande Flyweight voor sleutel '{key}'."); } return _flyweights[key]; } public void ListFlyweights() { Console.WriteLine($"FlyweightFactory: Er zijn {_flyweights.Count} flyweights opgeslagen:"); foreach (var key in _flyweights.Keys) { Console.WriteLine($"- {key}"); } } }
// Stap 4: Gebruik het Flyweight-patroon in de clientcode
class Program
{
static void Main(string[] args)
{
var factory = new FlyweightFactory();
// Maak of verkrijg Flyweights var flyweight1 = factory.GetFlyweight("A"); var flyweight2 = factory.GetFlyweight("B"); var flyweight3 = factory.GetFlyweight("A"); // Hergebruikt Flyweight "A" factory.ListFlyweights(); // Gebruik Flyweights met extrinsieke toestand flyweight1.Operation("Extrinsiek 1"); flyweight2.Operation("Extrinsiek 2"); flyweight3.Operation("Extrinsiek 3"); } }
Decorator
Beschrijving: Het Decorator-patroon voegt dynamisch nieuwe functionaliteit toe aan een object zonder de oorspronkelijke klasse te wijzigen. Het biedt een flexibele manier om objecten te verrijken, in tegenstelling tot subclassing, dat compile-time wijzigingen vereist.
// Stap 1: Definieer de Component-interface
public interface IComponent
{
string Operation();
}
// Stap 2: Implementeer een Concrete Component
public class ConcreteComponent : IComponent
{
public string Operation()
{
return “ConcreteComponent”;
}
}
// Stap 3: Maak een abstracte Decorator-klasse
public abstract class Decorator : IComponent
{
protected readonly IComponent _component;
public Decorator(IComponent component) { _component = component; } public virtual string Operation() { return _component.Operation(); } }
// Stap 4: Implementeer specifieke Decorators
public class DecoratorA : Decorator
{
public DecoratorA(IComponent component) : base(component) { }
public override string Operation() { return $"{base.Operation()} + DecoratorA"; } }
public class DecoratorB : Decorator
{
public DecoratorB(IComponent component) : base(component) { }
public override string Operation() { return $"{base.Operation()} + DecoratorB"; } }
// Stap 5: Gebruik het Decorator-patroon in de clientcode
class Program
{
static void Main(string[] args)
{
// Basiscomponent
IComponent component = new ConcreteComponent();
Console.WriteLine(“Basiscomponent:”);
Console.WriteLine(component.Operation());
// Voeg DecoratorA toe IComponent decoratorA = new DecoratorA(component); Console.WriteLine("\nMet DecoratorA:"); Console.WriteLine(decoratorA.Operation()); // Voeg DecoratorB toe IComponent decoratorB = new DecoratorB(decoratorA); Console.WriteLine("\nMet DecoratorA en DecoratorB:"); Console.WriteLine(decoratorB.Operation()); } }
Proxy
Beschrijving: Het Proxy-patroon voorziet een surrogaat of plaatsvervanger voor een ander object om controle te bieden over toegang, prestaties of beveiliging. Een Proxy kan gedrag toevoegen zoals lazy instantiation, caching, of toegangscontrole zonder de daadwerkelijke klasse te veranderen.
Typen Proxies:
Remote Proxy: Maakt een lokaal object dat communiceert met een object op afstand.
Virtual Proxy: Stelt dure objecten pas uit wanneer ze echt nodig zijn.
Protection Proxy: Beheert toegangsrechten voor gevoelige objecten.
// Stap 1: Definieer een gemeenschappelijke interface
public interface ISubject
{
void Request();
}
// Stap 2: Implementeer de RealSubject
public class RealSubject : ISubject
{
public void Request()
{
Console.WriteLine(“RealSubject: Verwerkt het verzoek.”);
}
}
// Stap 3: Implementeer de Proxy
public class Proxy : ISubject
{
private RealSubject _realSubject;
public void Request() { if (_realSubject == null) { Console.WriteLine("Proxy: Initialiseer het RealSubject."); _realSubject = new RealSubject(); } Console.WriteLine("Proxy: Doorsturen naar RealSubject."); _realSubject.Request(); } }
// Stap 4: Gebruik het Proxy-patroon in de clientcode
class Program
{
static void Main(string[] args)
{
ISubject proxy = new Proxy();
Console.WriteLine("Client: Doet het eerste verzoek via Proxy."); proxy.Request(); Console.WriteLine("\nClient: Doet een tweede verzoek via Proxy."); proxy.Request(); } }
Facade
Beschrijving: Het Facade-patroon biedt een eenvoudige interface voor een complex subsysteem. Het helpt gebruikers om een subsysteem te gebruiken zonder de details van de onderliggende complexiteit te hoeven begrijpen. Dit patroon verhoogt de gebruiksvriendelijkheid en vereenvoudigt de interactie met meerdere klassen.
// Stap 1: Stel subsystemen op
public class SubsystemA
{
public void OperationA()
{
Console.WriteLine(“SubsystemA: OperationA uitgevoerd.”);
}
}
public class SubsystemB
{
public void OperationB()
{
Console.WriteLine(“SubsystemB: OperationB uitgevoerd.”);
}
}
public class SubsystemC
{
public void OperationC()
{
Console.WriteLine(“SubsystemC: OperationC uitgevoerd.”);
}
}
// Stap 2: Implementeer de Facade
public class Facade
{
private readonly SubsystemA _subsystemA;
private readonly SubsystemB _subsystemB;
private readonly SubsystemC _subsystemC;
public Facade() { _subsystemA = new SubsystemA(); _subsystemB = new SubsystemB(); _subsystemC = new SubsystemC(); } public void PerformOperation() { Console.WriteLine("Facade: Start geavanceerde operatie."); _subsystemA.OperationA(); _subsystemB.OperationB(); _subsystemC.OperationC(); Console.WriteLine("Facade: Operatie voltooid."); } }
// Stap 3: Gebruik het Facade-patroon in de clientcode
class Program
{
static void Main(string[] args)
{
Facade facade = new Facade();
Console.WriteLine("Client: Roept de Facade aan."); facade.PerformOperation(); } }
Strategy
Beschrijving: Het Strategy-patroon definieert een familie van algoritmen, kapselt ze in afzonderlijke klassen in, en maakt ze uitwisselbaar. Dit patroon maakt het mogelijk om het gedrag van een klasse te wijzigen zonder de klasse zelf te veranderen. Het is ideaal wanneer je meerdere manieren hebt om een taak uit te voeren en je de implementatie dynamisch wilt kunnen kiezen.
// Stap 1: Definieer de Strategy-interface
public interface IStrategy
{
void Execute();
}
// Stap 2: Implementeer Concrete Strategies
public class ConcreteStrategyA : IStrategy
{
public void Execute()
{
Console.WriteLine(“Uitvoeren van strategie A.”);
}
}
public class ConcreteStrategyB : IStrategy
{
public void Execute()
{
Console.WriteLine(“Uitvoeren van strategie B.”);
}
}
public class ConcreteStrategyC : IStrategy
{
public void Execute()
{
Console.WriteLine(“Uitvoeren van strategie C.”);
}
}
// Stap 3: Implementeer de Context-klasse
public class Context
{
private IStrategy _strategy;
// Constructor accepteert een Strategy public Context(IStrategy strategy) { _strategy = strategy; } // Methode om de Strategy dynamisch in te stellen public void SetStrategy(IStrategy strategy) { _strategy = strategy; } // Methode die de huidige Strategy uitvoert public void ExecuteStrategy() { _strategy.Execute(); } }
// Stap 4: Gebruik het Strategy-patroon in de clientcode
class Program
{
static void Main(string[] args)
{
// Kies een Strategy
IStrategy strategyA = new ConcreteStrategyA();
Context context = new Context(strategyA);
Console.WriteLine("Client: Gebruikt Strategy A."); context.ExecuteStrategy(); // Dynamisch overschakelen naar een andere Strategy IStrategy strategyB = new ConcreteStrategyB(); Console.WriteLine("\nClient: Wisselt naar Strategy B."); context.SetStrategy(strategyB); context.ExecuteStrategy(); // Wissel naar een derde Strategy IStrategy strategyC = new ConcreteStrategyC(); Console.WriteLine("\nClient: Wisselt naar Strategy C."); context.SetStrategy(strategyC); context.ExecuteStrategy(); } }
Command
Beschrijving: Het Command-patroon verandert de manier waarop verzoeken worden gedaan aan objecten. In plaats van directe methodes aan te roepen, worden verzoeken omgezet in command-objecten die de acties representeren. Dit patroon maakt het mogelijk om verzoeken te parameteriseren, acties te bewaren, te annuleren, of op een later tijdstip opnieuw uit te voeren.
// Stap 1: Definieer de Command-interface
public interface ICommand
{
void Execute();
void Undo();
}
// Stap 2: Concrete Command-klasse (Bijvoorbeeld voor een lamp)
public class LightOnCommand : ICommand
{
private Light _light;
public LightOnCommand(Light light) { _light = light; } public void Execute() { _light.TurnOn(); } public void Undo() { _light.TurnOff(); } }
// Concrete Command voor de lamp uitzetten
public class LightOffCommand : ICommand
{
private Light _light;
public LightOffCommand(Light light) { _light = light; } public void Execute() { _light.TurnOff(); } public void Undo() { _light.TurnOn(); } }
// Stap 3: Ontvanger (Receiver)
public class Light
{
public void TurnOn()
{
Console.WriteLine(“Lamp is aan.”);
}
public void TurnOff() { Console.WriteLine("Lamp is uit."); } }
// Stap 4: Invoker die de Command’s uitvoert
public class RemoteControl
{
private ICommand _command;
public void SetCommand(ICommand command) { _command = command; } public void PressButton() { _command.Execute(); } public void PressUndoButton() { _command.Undo(); } }
// Stap 5: Gebruik het Command-patroon in de clientcode
class Program
{
static void Main(string[] args)
{
Light light = new Light();
// Creëer de concrete command-objecten ICommand lightOn = new LightOnCommand(light); ICommand lightOff = new LightOffCommand(light); // Creëer de Invoker (de RemoteControl) RemoteControl remote = new RemoteControl(); // Zet de lamp aan via de command remote.SetCommand(lightOn); Console.WriteLine("Client: Druk op de knop om de lamp aan te zetten."); remote.PressButton(); // Zet de lamp uit via de command remote.SetCommand(lightOff); Console.WriteLine("\nClient: Druk op de knop om de lamp uit te zetten."); remote.PressButton(); // Undo de laatste actie Console.WriteLine("\nClient: Druk op de Undo-knop."); remote.PressUndoButton(); } }
Observer
Beschrijving: Het Observer-patroon is een gedragsmatig ontwerp patroon waarbij een object (subject) zijn afhankelijkheden (observers) op de hoogte stelt van veranderingen, zonder dat het object zelf direct hoeft te weten wie de afhankelijkheden zijn. Dit patroon is handig voor het implementeren van event-driven systemen, zoals notificaties of het bijhouden van wijzigingen in een object.
Voordelen:
Verlies van koppeling: Het subject weet niet wie de observers zijn, en vice versa, wat de losser koppeling tussen objecten vergroot.
Flexibiliteit: Het toevoegen of verwijderen van observers kan op elk moment zonder het subject te beïnvloeden.
Event-driven architecturen: Geschikt voor situaties waar meerdere objecten moeten reageren op veranderingen van een ander object.
// Stap 1: Definieer de Observer-interface
public interface IObserver
{
void Update(string message);
}
// Stap 2: Definieer de Subject-interface
public interface ISubject
{
void Attach(IObserver observer);
void Detach(IObserver observer);
void Notify();
}
// Stap 3: Implementeer de ConcreteSubject-klasse (Het Subject dat gevolgd wordt)
public class ConcreteSubject : ISubject
{
private List<IObserver> _observers = new List<IObserver>();
private string _state;</IObserver></IObserver>
// Eigenschap die de staat van het subject bijwerkt en de observers verwittigt public string State { get { return _state; } set { _state = value; Notify(); // Notificeer alle observers bij verandering van de staat } } // Voeg een observer toe public void Attach(IObserver observer) { _observers.Add(observer); } // Verwijder een observer public void Detach(IObserver observer) { _observers.Remove(observer); } // Waarschuw alle observers public void Notify() { foreach (var observer in _observers) { observer.Update(_state); } } }
// Stap 4: Implementeer de ConcreteObserver-klasse (De Observer die reageert op veranderingen)
public class ConcreteObserver : IObserver
{
private string _name;
private ISubject _subject;
public ConcreteObserver(string name, ISubject subject) { _name = name; _subject = subject; _subject.Attach(this); // Registreer de observer bij het subject } public void Update(string message) { Console.WriteLine($"{_name} heeft de status update ontvangen: {message}"); } }
// Stap 5: Gebruik het Observer-patroon in de clientcode
class Program
{
static void Main(string[] args)
{
// Maak het concrete subject
ConcreteSubject subject = new ConcreteSubject();
// Maak observers en registreer ze bij het subject ConcreteObserver observer1 = new ConcreteObserver("Observer 1", subject); ConcreteObserver observer2 = new ConcreteObserver("Observer 2", subject); // Wijzig de staat van het subject subject.State = "Nieuwe status 1"; // Dit zal alle observers notificeren // Verwijder een observer subject.Detach(observer2); // Wijzig de staat opnieuw subject.State = "Nieuwe status 2"; // Alleen observer 1 ontvangt de notificatie } }
State
Beschrijving: Het State-patroon is een gedragsmatig ontwerp patroon dat de toestand van een object beheert door de logica voor elke toestand in afzonderlijke klasse te plaatsen. Het object kan zijn gedrag dynamisch wijzigen afhankelijk van zijn interne toestand, wat het mogelijk maakt om complex gedrag op een georganiseerde manier te beheren. Dit patroon is nuttig wanneer een object verschillende gedragingen heeft die afhankelijk zijn van de toestand waarin het zich bevindt.
Voordelen:
Verlies van complexe conditionele logica: Het patroon voorkomt dat je complexe if- en switch-verklaringen in de code hebt om verschillende toestanden te beheren.
Flexibiliteit: Gedrag kan eenvoudig worden toegevoegd of gewijzigd zonder de bestaande code te breken.
Toestand-gebaseerd ontwerp: Het maakt het gemakkelijk om de logica voor verschillende toestanden te isoleren in afzonderlijke klassen.
// Stap 1: Definieer de State-interface
public interface IState
{
void HandleRequest();
}
// Stap 2: Implementeer Concrete States
public class ConcreteStateA : IState
{
public void HandleRequest()
{
Console.WriteLine(“ConcreteStateA: Het uitvoeren van de actie voor State A.”);
}
}
public class ConcreteStateB : IState
{
public void HandleRequest()
{
Console.WriteLine(“ConcreteStateB: Het uitvoeren van de actie voor State B.”);
}
}
// Stap 3: Context-klasse (Het object waarvan de toestand verandert)
public class Context
{
private IState _state;
public Context(IState state) { _state = state; } // De toestand kan op elk moment worden gewijzigd public void SetState(IState state) { _state = state; } // Het gedrag wordt bepaald door de huidige toestand public void Request() { _state.HandleRequest(); } }
// Stap 4: Gebruik het State-patroon in de clientcode
class Program
{
static void Main(string[] args)
{
// Creëer context (initieel in StateA)
Context context = new Context(new ConcreteStateA());
// Verzoek afhandelen in de huidige toestand (StateA) context.Request(); // Verander de toestand naar ConcreteStateB context.SetState(new ConcreteStateB()); // Verzoek afhandelen in de nieuwe toestand (StateB) context.Request(); } }
Iterator
Beschrijving: Het Iterator-patroon is een gedragsmatig ontwerp patroon dat een manier biedt om toegang te krijgen tot de elementen van een verzameling zonder de onderliggende implementatie van de verzameling bloot te leggen. Dit patroon maakt het mogelijk om door een object te itereren (zoals een lijst of een array) met behulp van een iterator object, dat de toegang tot de elementen beheert.
Voordelen:
Verlies van implementatiedetails: De iterator biedt een abstractie voor de onderliggende data-structuur, zodat de clientcode zich niet hoeft bezig te houden met hoe de elementen zijn opgeslagen.
Eenvoudige toegang: Het maakt het mogelijk om door een verzameling te lopen zonder dat je je zorgen hoeft te maken over de indexen of de interne structuur van de verzameling.
Flexibiliteit: Verschillende soorten verzameling kunnen worden geïmplementeerd zonder dat de clientcode wordt beïnvloed.
// Stap 1: Definieer de Iterator-interface
public interface IIterator<T>
{
bool HasNext(); // Controleer of er nog een volgend element is
T Next(); // Verkrijg het volgende element
}</T>
// Stap 2: Definieer de Aggregate-interface
public interface IAggregate<T>
{
IIterator<T> CreateIterator(); // Maak een iterator voor de collectie
}</T></T>
// Stap 3: Implementeer de ConcreteAggregate-klasse (De collectie waar doorheen geütereerd wordt)
public class ConcreteAggregate<T> : IAggregate<T>
{
private List<T> _items = new List<T>();</T></T></T></T>
// Voeg een item toe aan de lijst public void Add(T item) { _items.Add(item); } // Maak een iterator voor deze verzameling public IIterator<T> CreateIterator() { return new ConcreteIterator<T>(_items); } }
// Stap 4: Implementeer de ConcreteIterator-klasse (De iterator die door de collectie heen gaat)
public class ConcreteIterator<T> : IIterator<T>
{
private List<T> _items;
private int _currentIndex = 0;</T></T></T>
public ConcreteIterator(List<T> items) { _items = items; } // Controleer of er nog een element is public bool HasNext() { return _currentIndex < _items.Count; } // Verkrijg het volgende element public T Next() { return _items[_currentIndex++]; } }
// Stap 5: Gebruik het Iterator-patroon in de clientcode
class Program
{
static void Main(string[] args)
{
// Maak een ConcreteAggregate en voeg items toe
ConcreteAggregate<string> aggregate = new ConcreteAggregate<string>();
aggregate.Add("Item 1");
aggregate.Add("Item 2");
aggregate.Add("Item 3");</string></string>
// Maak een iterator voor de collectie IIterator<string> iterator = aggregate.CreateIterator(); // Itereer door de collectie met de iterator while (iterator.HasNext()) { Console.WriteLine(iterator.Next()); } } }
Single Responsibility Principle
Beschrijving: Het Single Responsibility Principle (SRP) is een van de vijf SOLID-principes die betrekking hebben op objectgeoriënteerd ontwerp. Het SRP stelt dat een klasse slechts één reden tot verandering mag hebben. Met andere woorden, een klasse zou maar één verantwoordelijkheid moeten hebben. Dit zorgt ervoor dat de klasse eenvoudiger te begrijpen, testen en onderhouden is, omdat wijzigingen in een bepaald aspect van de functionaliteit slechts invloed hebben op één klasse, in plaats van op meerdere plaatsen in de code.
Het SRP voorkomt dat een klasse te veel verschillende functies uitvoert (bijvoorbeeld zowel gegevens verwerken als ze presenteren), wat kan leiden tot moeilijk te onderhouden code en hogere kans op bugs wanneer de code wordt aangepast.