C# Language
Kreativa designmönster
Sök…
Anmärkningar
Skapningsmönstren syftar till att skilja ett system från hur dess objekt skapas, komponeras och representeras. De ökar systemets flexibilitet när det gäller vad, vem, hur och när objekt skapas. Skapningsmönster kapslar in kunskapen om vilka klasser ett system använder, men de döljer detaljerna om hur instanserna för dessa klasser skapas och sätts ihop. Programmerare har insett att komponering av system med arv gör dessa system för styva. De skapande mönstren är utformade för att bryta denna nära koppling.
Singletonmönster
Singleton-mönstret är utformat för att begränsa skapandet av en klass till exakt en enda instans.
Detta mönster används i ett scenario där det är meningsfullt att bara ha något av något, till exempel:
- en enda klass som orkestrerar andra föremålers interaktion, ex. Managerklass
- eller en klass som representerar en unik, enda resurs, ex. Loggningskomponent
Ett av de vanligaste sätten att implementera Singleton-mönstret är via en statisk fabriksmetod som en CreateInstance()
eller GetInstance()
(eller en statisk egenskap i C #, Instance
), som sedan är utformad för att alltid returnera samma instans.
Det första samtalet till metoden eller egenskapen skapar och returnerar Singleton-instansen. Därefter returnerar metoden alltid samma instans. På detta sätt finns det bara någonsin en instans av singletonobjektet.
Att förhindra skapande av instanser via new
kan åstadkommas genom att göra klasskonstruktören private.
Här är ett typiskt kodexempel för implementering av ett Singleton-mönster i 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;
}
}
}
För att ytterligare illustrera detta mönster kontrollerar koden nedan om en identisk instans av Singleton returneras när egenskapen Instance kallas mer än en gång.
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");
}
}
}
Obs! Den här implementeringen är inte trådsäker.
Besök: Singleton Implementation för att se fler exempel, inklusive hur du gör denna tråd-säker
Singletons liknar konceptuellt ett globalt värde och orsakar liknande designfel och problem. På grund av detta betraktas Singleton-mönstret allmänt som ett antimönster.
Besök "Vad är så dåligt med Singletons?" för mer information om de problem som uppstår med deras användning.
I C # har du förmågan att göra en klass static
, vilket gör alla medlemmar statiska, och klassen kan inte instanseras. Med tanke på detta är det vanligt att se statiska klasser som används i stället för Singleton-mönstret.
För viktiga skillnader mellan de två, besök C # Singleton Pattern Versus Static Class .
Fabriksmetodmönster
Fabriksmetoden är ett av kreativa designmönster. Det används för att hantera problemet med att skapa objekt utan att ange exakt resultattyp. Detta dokument kommer att lära dig hur du använder Factory Method DP på rätt sätt.
Låt mig förklara idén med det på ett enkelt exempel. Föreställ dig att du arbetar i en fabrik som producerar tre typer av enheter - Ammeter, Voltmeter och resistansmätare. Du skriver ett program för en central dator som skapar en utvald enhet, men du känner inte till det slutliga beslutet från din chef om vad du ska producera.
Låt oss skapa ett gränssnitt IDevice
med några vanliga funktioner som alla enheter har:
public interface IDevice
{
int Measure();
void TurnOff();
void TurnOn();
}
Nu kan vi skapa klasser som representerar våra enheter. Dessa klasser måste implementera IDevice
gränssnittet:
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 måste vi definiera fabriksmetoden. Låt oss skapa DeviceFactory
klass med statisk metod inuti:
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();
}
}
}
Bra! Låt oss testa vår kod:
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();
}
}
Detta är exemplet som du kan se efter att du har kört den här koden:
AmMeter slår på ...
36
6
33
43
24
AmMeter blinkar lampor som säger hejdå!
VoltMeter slås på ...
102
-61
85
138
36
VoltMeter blinkar lampor som säger hejdå!
OhmMeter slår på ...
723.828
368.536
685.412
800.266
578.595
OhmMeter blinkar lampor som säger adjö!
Builder mönster
Separera konstruktionen av ett komplex objekt från dess representation så att samma konstruktionsprocess kan skapa olika representationer och ger en hög nivå av kontroll över montering av föremålen.
I det här exemplet visar Builder-mönstret i vilket olika fordon monteras steg för steg. Butiken använder VehicleBuilders för att konstruera en mängd fordon i en serie sekventiella steg.
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"]);
}
}
}
Produktion
Fordonstyp: Scooter Frame: Scooter Frame
Motor: ingen
# Hjul: 2
#Doors: 0Fordonstyp: bil
Ram: Bilram
Motor: 2500 cc
# Hjul: 4
#Doors: 4Fordonstyp: Motorcykel
Ram: MotorCycle Frame
Motor: 500 cc
# Hjul: 2
#Doors: 0
Prototypmönster
Ange vilken typ av objekt som ska skapas med en prototypisk instans och skapa nya objekt genom att kopiera denna prototyp.
I det här exemplet demonstrerar prototypmönstret där nya färgobjekt skapas genom att kopiera befintliga, användardefinierade färger av samma typ.
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); }
}
}
}
Produktion:
Kloningsfärg RGB: 255, 0, 0
Kloningsfärg RGB: 128,211,128
Kloningsfärg RGB: 211, 34, 20
Abstrakt fabriksmönster
Ge ett gränssnitt för att skapa familjer av relaterade eller beroende objekt utan att ange deras konkreta klasser.
I det här exemplet demonstrerar skapandet av olika djurvärldar för ett datorspel med olika fabriker. Även om djuren som skapats av kontinentfabrikerna är olika förblir interaktionen mellan djuren densamma.
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);
}
}
}
Produktion:
Lejon äter gnuer
Wolf äter Bison