C# Language
Creatieve ontwerppatronen
Zoeken…
Opmerkingen
De creatiepatronen zijn bedoeld om een systeem te scheiden van hoe de objecten worden gemaakt, samengesteld en weergegeven. Ze vergroten de flexibiliteit van het systeem wat betreft het wat, wie, hoe en wanneer objectcreatie. Creatiepatronen bevatten de kennis over welke klassen een systeem gebruikt, maar ze verbergen de details over hoe de instanties van deze klassen worden gemaakt en samengesteld. Programmeurs zijn zich gaan realiseren dat het samenstellen van systemen met overerving die systemen te rigide maakt. De creatiepatronen zijn ontworpen om deze nauwe koppeling te verbreken.
Singleton patroon
Het Singleton-patroon is ontworpen om het maken van een klasse te beperken tot precies één exemplaar.
Dit patroon wordt gebruikt in een scenario waarbij het logisch is om slechts één van iets te hebben, zoals:
- een enkele klasse die de interacties van andere objecten orkestreert, ex. Manager klasse
- of een klasse die een unieke, enkele resource vertegenwoordigt, bijv. Logboekcomponent
Een van de meest gebruikelijke manieren om het Singleton-patroon te implementeren, is via een statische fabrieksmethode zoals CreateInstance()
of GetInstance()
(of een statische eigenschap in C #, Instance
), die vervolgens is ontworpen om altijd dezelfde instantie te retourneren.
De eerste aanroep van de methode of eigenschap maakt en retourneert de instantie Singleton. Daarna retourneert de methode altijd dezelfde instantie. Op deze manier is er maar één exemplaar van het singleton-object.
Het voorkomen van het maken van instanties via new
kan worden bereikt door de klassenbouwer (s) private.
Hier is een typisch codevoorbeeld voor het implementeren van een Singleton-patroon in C #:
class Singleton
{
// Because the _instance member is made private, the only way to get the single
// instance is via the static Instance property below. This can also be similarly
// achieved with a GetInstance() method instead of the property.
private static Singleton _instance = null;
// Making the constructor private prevents other instances from being
// created via something like Singleton s = new Singleton(), protecting
// against unintentional misuse.
private Singleton()
{
}
public static Singleton Instance
{
get
{
// The first call will create the one and only instance.
if (_instance == null)
{
_instance = new Singleton();
}
// Every call afterwards will return the single instance created above.
return _instance;
}
}
}
Om dit patroon verder te illustreren, controleert de onderstaande code of een identieke instantie van de Singleton wordt geretourneerd wanneer de eigenschap Instance meer dan eens wordt aangeroepen.
class Program
{
static void Main(string[] args)
{
Singleton s1 = Singleton.Instance;
Singleton s2 = Singleton.Instance;
// Both Singleton objects above should now reference the same Singleton instance.
if (Object.ReferenceEquals(s1, s2))
{
Console.WriteLine("Singleton is working");
}
else
{
// Otherwise, the Singleton Instance property is returning something
// other than the unique, single instance when called.
Console.WriteLine("Singleton is broken");
}
}
}
Opmerking: deze implementatie is niet thread-safe.
Ga voor meer voorbeelden, waaronder hoe u deze thread veilig kunt maken, naar: Singleton Implementation
Singletons zijn conceptueel vergelijkbaar met een wereldwijde waarde en veroorzaken vergelijkbare ontwerpfouten en -problemen. Daarom wordt het Singleton-patroon algemeen beschouwd als een antipatroon.
Ga naar "Wat is er zo slecht aan Singletons?" voor meer informatie over de problemen die zich voordoen bij het gebruik ervan.
In C # kunt u een klasse static
, waardoor alle leden statisch worden en de klasse kan niet worden geïnstantieerd. Daarom is het gebruikelijk om statische klassen te zien die worden gebruikt in plaats van het Singleton-patroon.
Ga naar C # Singleton Pattern Versus Static Class voor de belangrijkste verschillen tussen de twee.
Patroon fabrieksmethode
Factory Method is een van de creatieve ontwerppatronen. Het wordt gebruikt om het probleem van het maken van objecten aan te pakken zonder het exacte resultaattype op te geven. Dit document leert u hoe u Factory Method DP op de juiste manier gebruikt.
Laat me het idee ervan aan u uitleggen aan de hand van een eenvoudig voorbeeld. Stel je voor dat je in een fabriek werkt die drie soorten apparaten produceert: ampèremeter, voltmeter en weerstandsmeter. U schrijft een programma voor een centrale computer waarmee het geselecteerde apparaat wordt gemaakt, maar u weet niet de uiteindelijke beslissing van uw baas over wat te produceren.
Laten we een interface- IDevice
met enkele algemene functies die alle apparaten hebben:
public interface IDevice
{
int Measure();
void TurnOff();
void TurnOn();
}
Nu kunnen we klassen maken die onze apparaten vertegenwoordigen. Die klassen moeten IDevice
interface implementeren:
public class AmMeter : IDevice
{
private Random r = null;
public AmMeter()
{
r = new Random();
}
public int Measure() { return r.Next(-25, 60); }
public void TurnOff() { Console.WriteLine("AmMeter flashes lights saying good bye!"); }
public void TurnOn() { Console.WriteLine("AmMeter turns on..."); }
}
public class OhmMeter : IDevice
{
private Random r = null;
public OhmMeter()
{
r = new Random();
}
public int Measure() { return r.Next(0, 1000000); }
public void TurnOff() { Console.WriteLine("OhmMeter flashes lights saying good bye!"); }
public void TurnOn() { Console.WriteLine("OhmMeter turns on..."); }
}
public class VoltMeter : IDevice
{
private Random r = null;
public VoltMeter()
{
r = new Random();
}
public int Measure() { return r.Next(-230, 230); }
public void TurnOff() { Console.WriteLine("VoltMeter flashes lights saying good bye!"); }
public void TurnOn() { Console.WriteLine("VoltMeter turns on..."); }
}
Nu moeten we de fabrieksmethode definiëren. Laten we de DeviceFactory
klasse met de statische methode erin maken:
public enum Device
{
AM,
VOLT,
OHM
}
public class DeviceFactory
{
public static IDevice CreateDevice(Device d)
{
switch(d)
{
case Device.AM: return new AmMeter();
case Device.VOLT: return new VoltMeter();
case Device.OHM: return new OhmMeter();
default: return new AmMeter();
}
}
}
Super goed! Laten we onze code testen:
public class Program
{
static void Main(string[] args)
{
IDevice device = DeviceFactory.CreateDevice(Device.AM);
device.TurnOn();
Console.WriteLine(device.Measure());
Console.WriteLine(device.Measure());
Console.WriteLine(device.Measure());
Console.WriteLine(device.Measure());
Console.WriteLine(device.Measure());
device.TurnOff();
Console.WriteLine();
device = DeviceFactory.CreateDevice(Device.VOLT);
device.TurnOn();
Console.WriteLine(device.Measure());
Console.WriteLine(device.Measure());
Console.WriteLine(device.Measure());
Console.WriteLine(device.Measure());
Console.WriteLine(device.Measure());
device.TurnOff();
Console.WriteLine();
device = DeviceFactory.CreateDevice(Device.OHM);
device.TurnOn();
Console.WriteLine(device.Measure());
Console.WriteLine(device.Measure());
Console.WriteLine(device.Measure());
Console.WriteLine(device.Measure());
Console.WriteLine(device.Measure());
device.TurnOff();
Console.WriteLine();
}
}
Dit is de voorbeelduitvoer die u mogelijk ziet na het uitvoeren van deze code:
AmMeter wordt ingeschakeld ...
36
6
33
43
24
AmMeter flitst lichten vaarwel!
VoltMeter wordt ingeschakeld ...
102
-61
85
138
36
VoltMeter flitst lichten vaarwel!
OhmMeter wordt ingeschakeld ...
723.828
368.536
685.412
800.266
578.595
OhmMeter knippert licht en zegt tot ziens!
Bouwer patroon
Scheid de constructie van een complex object van de weergave ervan, zodat hetzelfde constructieproces verschillende weergaven kan creëren en een hoog niveau van controle biedt over de assemblage van de objecten.
In dit voorbeeld wordt het Builder-patroon getoond waarin verschillende voertuigen stapsgewijs worden geassembleerd. De winkel gebruikt VehicleBuilders om verschillende voertuigen in een reeks opeenvolgende stappen te bouwen.
using System;
using System.Collections.Generic;
namespace GangOfFour.Builder
{
/// <summary>
/// MainApp startup class for Real-World
/// Builder Design Pattern.
/// </summary>
public class MainApp
{
/// <summary>
/// Entry point into console application.
/// </summary>
public static void Main()
{
VehicleBuilder builder;
// Create shop with vehicle builders
Shop shop = new Shop();
// Construct and display vehicles
builder = new ScooterBuilder();
shop.Construct(builder);
builder.Vehicle.Show();
builder = new CarBuilder();
shop.Construct(builder);
builder.Vehicle.Show();
builder = new MotorCycleBuilder();
shop.Construct(builder);
builder.Vehicle.Show();
// Wait for user
Console.ReadKey();
}
}
/// <summary>
/// The 'Director' class
/// </summary>
class Shop
{
// Builder uses a complex series of steps
public void Construct(VehicleBuilder vehicleBuilder)
{
vehicleBuilder.BuildFrame();
vehicleBuilder.BuildEngine();
vehicleBuilder.BuildWheels();
vehicleBuilder.BuildDoors();
}
}
/// <summary>
/// The 'Builder' abstract class
/// </summary>
abstract class VehicleBuilder
{
protected Vehicle vehicle;
// Gets vehicle instance
public Vehicle Vehicle
{
get { return vehicle; }
}
// Abstract build methods
public abstract void BuildFrame();
public abstract void BuildEngine();
public abstract void BuildWheels();
public abstract void BuildDoors();
}
/// <summary>
/// The 'ConcreteBuilder1' class
/// </summary>
class MotorCycleBuilder : VehicleBuilder
{
public MotorCycleBuilder()
{
vehicle = new Vehicle("MotorCycle");
}
public override void BuildFrame()
{
vehicle["frame"] = "MotorCycle Frame";
}
public override void BuildEngine()
{
vehicle["engine"] = "500 cc";
}
public override void BuildWheels()
{
vehicle["wheels"] = "2";
}
public override void BuildDoors()
{
vehicle["doors"] = "0";
}
}
/// <summary>
/// The 'ConcreteBuilder2' class
/// </summary>
class CarBuilder : VehicleBuilder
{
public CarBuilder()
{
vehicle = new Vehicle("Car");
}
public override void BuildFrame()
{
vehicle["frame"] = "Car Frame";
}
public override void BuildEngine()
{
vehicle["engine"] = "2500 cc";
}
public override void BuildWheels()
{
vehicle["wheels"] = "4";
}
public override void BuildDoors()
{
vehicle["doors"] = "4";
}
}
/// <summary>
/// The 'ConcreteBuilder3' class
/// </summary>
class ScooterBuilder : VehicleBuilder
{
public ScooterBuilder()
{
vehicle = new Vehicle("Scooter");
}
public override void BuildFrame()
{
vehicle["frame"] = "Scooter Frame";
}
public override void BuildEngine()
{
vehicle["engine"] = "50 cc";
}
public override void BuildWheels()
{
vehicle["wheels"] = "2";
}
public override void BuildDoors()
{
vehicle["doors"] = "0";
}
}
/// <summary>
/// The 'Product' class
/// </summary>
class Vehicle
{
private string _vehicleType;
private Dictionary<string,string> _parts =
new Dictionary<string,string>();
// Constructor
public Vehicle(string vehicleType)
{
this._vehicleType = vehicleType;
}
// Indexer
public string this[string key]
{
get { return _parts[key]; }
set { _parts[key] = value; }
}
public void Show()
{
Console.WriteLine("\n---------------------------");
Console.WriteLine("Vehicle Type: {0}", _vehicleType);
Console.WriteLine(" Frame : {0}", _parts["frame"]);
Console.WriteLine(" Engine : {0}", _parts["engine"]);
Console.WriteLine(" #Wheels: {0}", _parts["wheels"]);
Console.WriteLine(" #Doors : {0}", _parts["doors"]);
}
}
}
uitgang
Voertuigtype: Scooterframe: Scooterframe
Motor: geen
#Wheels: 2
#Doors: 0Voertuigtype: Auto
Frame: autoframe
Motor: 2500 cc
#Wheels: 4
#Doors: 4Voertuigtype: MotorCycle
Frame: motorframe
Motor: 500 cc
#Wheels: 2
#Doors: 0
Prototype patroon
Geef het soort objecten op dat moet worden gemaakt met een prototypische instantie en maak nieuwe objecten door dit prototype te kopiëren.
In dit voorbeeld wordt het prototype-patroon getoond waarin nieuwe kleurobjecten worden gemaakt door bestaande, door de gebruiker gedefinieerde kleuren van hetzelfde type te kopiëren.
using System;
using System.Collections.Generic;
namespace GangOfFour.Prototype
{
/// <summary>
/// MainApp startup class for Real-World
/// Prototype Design Pattern.
/// </summary>
class MainApp
{
/// <summary>
/// Entry point into console application.
/// </summary>
static void Main()
{
ColorManager colormanager = new ColorManager();
// Initialize with standard colors
colormanager["red"] = new Color(255, 0, 0);
colormanager["green"] = new Color(0, 255, 0);
colormanager["blue"] = new Color(0, 0, 255);
// User adds personalized colors
colormanager["angry"] = new Color(255, 54, 0);
colormanager["peace"] = new Color(128, 211, 128);
colormanager["flame"] = new Color(211, 34, 20);
// User clones selected colors
Color color1 = colormanager["red"].Clone() as Color;
Color color2 = colormanager["peace"].Clone() as Color;
Color color3 = colormanager["flame"].Clone() as Color;
// Wait for user
Console.ReadKey();
}
}
/// <summary>
/// The 'Prototype' abstract class
/// </summary>
abstract class ColorPrototype
{
public abstract ColorPrototype Clone();
}
/// <summary>
/// The 'ConcretePrototype' class
/// </summary>
class Color : ColorPrototype
{
private int _red;
private int _green;
private int _blue;
// Constructor
public Color(int red, int green, int blue)
{
this._red = red;
this._green = green;
this._blue = blue;
}
// Create a shallow copy
public override ColorPrototype Clone()
{
Console.WriteLine(
"Cloning color RGB: {0,3},{1,3},{2,3}",
_red, _green, _blue);
return this.MemberwiseClone() as ColorPrototype;
}
}
/// <summary>
/// Prototype manager
/// </summary>
class ColorManager
{
private Dictionary<string, ColorPrototype> _colors =
new Dictionary<string, ColorPrototype>();
// Indexer
public ColorPrototype this[string key]
{
get { return _colors[key]; }
set { _colors.Add(key, value); }
}
}
}
Output:
RGB klonen: 255, 0, 0
RGB klonen: 128,211,128
RGB klonen: 211, 34, 20
Abstract fabriekspatroon
Bieden een interface voor het maken van families van gerelateerde of afhankelijke objecten zonder hun concrete klassen op te geven.
In dit voorbeeld wordt het creëren van verschillende dierenwerelden voor een computerspel met verschillende fabrieken getoond. Hoewel de dieren gecreëerd door de Continent-fabrieken verschillend zijn, blijven de interacties tussen de dieren hetzelfde.
using System;
namespace GangOfFour.AbstractFactory
{
/// <summary>
/// MainApp startup class for Real-World
/// Abstract Factory Design Pattern.
/// </summary>
class MainApp
{
/// <summary>
/// Entry point into console application.
/// </summary>
public static void Main()
{
// Create and run the African animal world
ContinentFactory africa = new AfricaFactory();
AnimalWorld world = new AnimalWorld(africa);
world.RunFoodChain();
// Create and run the American animal world
ContinentFactory america = new AmericaFactory();
world = new AnimalWorld(america);
world.RunFoodChain();
// Wait for user input
Console.ReadKey();
}
}
/// <summary>
/// The 'AbstractFactory' abstract class
/// </summary>
abstract class ContinentFactory
{
public abstract Herbivore CreateHerbivore();
public abstract Carnivore CreateCarnivore();
}
/// <summary>
/// The 'ConcreteFactory1' class
/// </summary>
class AfricaFactory : ContinentFactory
{
public override Herbivore CreateHerbivore()
{
return new Wildebeest();
}
public override Carnivore CreateCarnivore()
{
return new Lion();
}
}
/// <summary>
/// The 'ConcreteFactory2' class
/// </summary>
class AmericaFactory : ContinentFactory
{
public override Herbivore CreateHerbivore()
{
return new Bison();
}
public override Carnivore CreateCarnivore()
{
return new Wolf();
}
}
/// <summary>
/// The 'AbstractProductA' abstract class
/// </summary>
abstract class Herbivore
{
}
/// <summary>
/// The 'AbstractProductB' abstract class
/// </summary>
abstract class Carnivore
{
public abstract void Eat(Herbivore h);
}
/// <summary>
/// The 'ProductA1' class
/// </summary>
class Wildebeest : Herbivore
{
}
/// <summary>
/// The 'ProductB1' class
/// </summary>
class Lion : Carnivore
{
public override void Eat(Herbivore h)
{
// Eat Wildebeest
Console.WriteLine(this.GetType().Name +
" eats " + h.GetType().Name);
}
}
/// <summary>
/// The 'ProductA2' class
/// </summary>
class Bison : Herbivore
{
}
/// <summary>
/// The 'ProductB2' class
/// </summary>
class Wolf : Carnivore
{
public override void Eat(Herbivore h)
{
// Eat Bison
Console.WriteLine(this.GetType().Name +
" eats " + h.GetType().Name);
}
}
/// <summary>
/// The 'Client' class
/// </summary>
class AnimalWorld
{
private Herbivore _herbivore;
private Carnivore _carnivore;
// Constructor
public AnimalWorld(ContinentFactory factory)
{
_carnivore = factory.CreateCarnivore();
_herbivore = factory.CreateHerbivore();
}
public void RunFoodChain()
{
_carnivore.Eat(_herbivore);
}
}
}
Output:
Lion eet gnoe
Wolf eet bizon