Patters Flashcards

1
Q

Singleton

A

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!");
} }"
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Factory method

A

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());
} }"
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Abstract factory

A

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));
} } "
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Adapter

A

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());
}
}

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Composite

A

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();
} }
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Flyweight

A

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");
} }
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Decorator

A

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());
} }
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Proxy

A

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();
} }
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Facade

A

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();
} }
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Strategy

A

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();
} }
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Command

A

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();
} }
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Observer

A

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
} }
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

State

A

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();
} }
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Iterator

A

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());
    }
} }
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Single Responsibility Principle

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Open Closed Principle

A

Beschrijving: Het Open/Closed Principle (OCP) is een van de vijf SOLID-principes voor objectgeoriënteerd ontwerp. Het stelt dat software-entiteiten (zoals klassen, modules, functies, etc.) open moeten zijn voor uitbreiding, maar gesloten voor modificatie. Dit betekent dat je de functionaliteit van een systeem kunt uitbreiden door nieuwe code toe te voegen, zonder bestaande code te wijzigen.

Het doel van OCP is om de onderhoudbaarheid en uitbreidbaarheid van software te verbeteren. In plaats van bestaande code te wijzigen wanneer nieuwe functionaliteit nodig is, kun je de bestaande code behouden en nieuwe functionaliteit toevoegen door de code uit te breiden. Dit helpt om het risico op bugs te verminderen en maakt het makkelijker om nieuwe features toe te voegen zonder dat je bestaande functionaliteit breekt.

17
Q

Liskov Substitution Principle

A

Beschrijving: Het Liskov Substitution Principle (LSP) is een van de vijf SOLID-principes en stelt dat objecten van een afgeleide klasse in plaats van objecten van de basisklasse moeten kunnen worden gebruikt zonder dat de correcte werking van het programma in gevaar komt. Met andere woorden, als een klasse een subklasse is van een andere klasse, dan moeten objecten van de subklasse zonder problemen het gedrag van de basisklasse kunnen vervangen, en de code moet nog steeds correct functioneren.

Het LSP zorgt ervoor dat het gebruik van overerving in objectgeoriënteerde programma’s logisch en effectief is, doordat de subklassen volledig compatibel moeten zijn met de basisklasse.

Samenvatting: Een subklasse moet vervangbaar zijn voor haar basisklasse zonder dat de functionaliteit van het systeem in gevaar komt.

18
Q

Interface Segregation Principle

A

Beschrijving: Het Liskov Substitution Principle (LSP) is een van de vijf SOLID-principes en stelt dat objecten van een afgeleide klasse in plaats van objecten van de basisklasse moeten kunnen worden gebruikt zonder dat de correcte werking van het programma in gevaar komt. Met andere woorden, als een klasse een subklasse is van een andere klasse, dan moeten objecten van de subklasse zonder problemen het gedrag van de basisklasse kunnen vervangen, en de code moet nog steeds correct functioneren.

Het LSP zorgt ervoor dat het gebruik van overerving in objectgeoriënteerde programma’s logisch en effectief is, doordat de subklassen volledig compatibel moeten zijn met de basisklasse.

Samenvatting: Een subklasse moet vervangbaar zijn voor haar basisklasse zonder dat de functionaliteit van het systeem in gevaar komt.

19
Q

Dependency Inversion Principle

A

Beschrijving: Het Dependency Inversion Principle (DIP) is een van de vijf SOLID-principes en stelt dat hoog-niveau modules niet afhankelijk moeten zijn van laag-niveau modules. In plaats daarvan moeten beide afhankelijk zijn van abstracties (interfaces of abstracte klassen). Daarnaast moeten abstracties niet afhankelijk zijn van details, maar details moeten afhankelijk zijn van abstracties.

Het doel van DIP is om de afhankelijkheden in een systeem te beheren zodat de hoge- en lage-niveau modules losjes gekoppeld blijven. Hierdoor wordt het systeem flexibeler en makkelijker uitbreidbaar, omdat het makkelijker wordt om nieuwe functionaliteit toe te voegen zonder bestaande code te breken.

In eenvoudiger woorden: abstracties zouden de afhankelijkheden moeten definiëren, niet de concrete implementaties.