C# Language
Wzory projektowe
Szukaj…
Uwagi
Wzory kreacyjne mają na celu oddzielenie systemu od sposobu tworzenia, komponowania i przedstawiania jego obiektów. Zwiększają elastyczność systemu pod względem tego, kto, jak i kiedy tworzy się obiekt. Wzorce tworzenia zawierają wiedzę o tym, z których klas korzysta system, ale ukrywają szczegóły dotyczące tego, jak instancje tych klas są tworzone i łączone. Programiści zdali sobie sprawę, że komponowanie systemów z dziedziczeniem powoduje, że systemy te są zbyt sztywne. Wzory kreacyjne zostały zaprojektowane tak, aby zerwać to ścisłe połączenie.
Singleton Pattern
Wzorzec Singleton ma na celu ograniczenie tworzenia klasy do dokładnie jednej instancji.
Ten wzorzec jest używany w scenariuszu, w którym sensowne jest posiadanie tylko jednego z nich, na przykład:
- pojedyncza klasa, która koordynuje interakcje innych obiektów, np. Klasa menedżera
- lub jedna klasa reprezentująca unikalny, pojedynczy zasób, np. Składnik rejestrujący
Jednym z najczęstszych sposobów implementacji wzorca Singleton jest użycie statycznej metody fabrycznej, takiej jak CreateInstance()
lub GetInstance()
(lub właściwość statyczna w C #, Instance
), która jest następnie zaprojektowana tak, aby zawsze zwracała tę samą instancję.
Pierwsze wywołanie metody lub właściwości tworzy i zwraca instancję Singleton. Następnie metoda zawsze zwraca tę samą instancję. W ten sposób istnieje tylko jedna instancja obiektu singletonu.
Zapobieganie tworzeniu instancji za pomocą new
można osiągnąć poprzez ustawienie konstruktora (ów) klasy w sposób private.
Oto typowy przykład kodu do implementacji wzorca Singleton w 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;
}
}
}
Aby zilustrować ten wzorzec, poniższy kod sprawdza, czy identyczna instancja Singleton jest zwracana, gdy właściwość Instancja jest wywoływana więcej niż jeden raz.
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");
}
}
}
Uwaga: ta implementacja nie jest bezpieczna dla wątków.
Aby zobaczyć więcej przykładów, w tym jak uczynić ten wątek bezpiecznym, odwiedź: Implementacja Singleton
Singletony są koncepcyjnie podobne do wartości globalnej i powodują podobne wady projektowe i obawy. Z tego powodu wzór Singleton jest powszechnie uważany za anty-wzór.
Odwiedź „Co jest tak złego w Singletons?” aby uzyskać więcej informacji na temat problemów, które powstają podczas ich używania.
W języku C # możesz uczynić klasę static
, co powoduje, że wszyscy członkowie są statyczni, a klasy nie można utworzyć instancji. Biorąc to pod uwagę, często obserwuje się stosowanie klas statycznych zamiast wzorca Singleton.
Aby zobaczyć kluczowe różnice między nimi, odwiedź C # Singleton Pattern vs. Static Class .
Wzór metody fabrycznej
Metoda fabryczna jest jednym z kreatywnych wzorców projektowych. Służy do radzenia sobie z problemem tworzenia obiektów bez określania dokładnego typu wyniku. Ten dokument nauczy Cię, jak prawidłowo używać Metody Fabrycznej DP.
Pozwól, że wyjaśnię ci ten pomysł na prostym przykładzie. Wyobraź sobie, że pracujesz w fabryce produkującej trzy typy urządzeń - amperomierz, woltomierz i miernik rezystancji. Piszesz program na centralny komputer, który utworzy wybrane urządzenie, ale nie znasz ostatecznej decyzji szefa co do tego, co wyprodukować.
IDevice
interfejs IDevice
z niektórymi typowymi funkcjami, które mają wszystkie urządzenia:
public interface IDevice
{
int Measure();
void TurnOff();
void TurnOn();
}
Teraz możemy tworzyć klasy reprezentujące nasze urządzenia. Klasy te muszą implementować interfejs IDevice
:
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..."); }
}
Teraz musimy zdefiniować metodę fabryczną. DeviceFactory
klasę DeviceFactory
pomocą metody statycznej wewnątrz:
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();
}
}
}
Świetny! Przetestuj nasz 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();
}
}
Oto przykładowe dane wyjściowe, które możesz zobaczyć po uruchomieniu tego kodu:
AmMeter włącza się ...
36
6
33
43
24
AmMeter miga lampkami żegnając się!
VoltMeter włącza się ...
102
-61
85
138
36
VoltMeter miga lampkami żegnając się!
OhmMeter włącza się ...
723828
368536
685412
800266
578595
OhmMeter miga lampkami żegnając się!
Wzór konstruktora
Oddziel konstrukcję złożonego obiektu od jego reprezentacji, aby ten sam proces budowy mógł tworzyć różne reprezentacje i zapewniał wysoki poziom kontroli nad złożeniem obiektów.
W tym przykładzie pokazano wzór konstruktora, w którym różne pojazdy są montowane krok po kroku. Sklep korzysta z VehicleBuilders do konstruowania różnych pojazdów w serii kolejnych kroków.
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"]);
}
}
}
Wynik
Typ pojazdu: rama skutera: rama skutera
Silnik: brak
# Koła: 2
#Drzwi: 0Typ pojazdu: samochód
Rama: rama samochodu
Silnik: 2500 cm3
# Koła: 4
#Drzwi: 4Typ pojazdu: motocykl
Rama: rama motocykla
Silnik: 500 cm3
# Koła: 2
#Drzwi: 0
Wzór prototypu
Określ rodzaj obiektów do utworzenia przy użyciu instancji prototypowej i utwórz nowe obiekty, kopiując ten prototyp.
W tym przykładzie demonstruje wzór prototypu, w którym tworzone są nowe obiekty kolorów poprzez kopiowanie wcześniej istniejących, zdefiniowanych przez użytkownika kolorów tego samego typu.
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); }
}
}
}
Wynik:
Kolor klonowania RGB: 255, 0, 0
Kolor klonowania RGB: 128 211,128
Kolor klonowania RGB: 211, 34, 20
Abstrakcyjny wzór fabryki
Zapewnij interfejs do tworzenia rodzin powiązanych lub zależnych obiektów bez określania ich konkretnych klas.
W tym przykładzie pokazano tworzenie różnych światów zwierząt dla gry komputerowej wykorzystującej różne fabryki. Chociaż zwierzęta stworzone przez fabryki kontynentu są różne, interakcje między nimi pozostają takie same.
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);
}
}
}
Wynik:
Lew zjada GNU
Wilk zjada Bizona