C# Language
Создание шаблонов проектирования
Поиск…
замечания
Шаги создания нацелены на то, чтобы отделить систему от того, как ее объекты созданы, составлены и представлены. Они повышают гибкость системы с точки зрения того, что, кто, как и когда создает объект. Творческие шаблоны инкапсулируют знания о том, какие классы использует система, но они скрывают детали того, как создаются и объединяются экземпляры этих классов. Программисты поняли, что системы компоновки с наследованием делают эти системы слишком жесткими. Созданные шаблоны предназначены для разрыва этой тесной связи.
Шаблон Singleton
Шаблон Singleton предназначен для ограничения создания класса ровно на один экземпляр.
Этот шаблон используется в сценарии, где имеет смысл иметь только одно из следующего:
- один класс, который организует взаимодействия других объектов, например. Класс менеджера
- или один класс, который представляет собой уникальный, единственный ресурс, например. Компонент регистрации
Одним из наиболее распространенных способов реализации шаблона Singleton является статический заводский метод, такой как CreateInstance()
или GetInstance()
(или статическое свойство в C #, Instance
), который затем предназначен для возврата одного и того же экземпляра.
Первый вызов метода или свойства создает и возвращает экземпляр Singleton. После этого метод всегда возвращает один и тот же экземпляр. Таким образом, существует только один экземпляр объекта singleton.
Предотвращение создания экземпляров с помощью new
может быть выполнено путем создания private.
конструктора классов private.
Вот типичный пример кода для реализации шаблона Singleton в 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;
}
}
}
Чтобы проиллюстрировать этот шаблон далее, приведенный ниже код проверяет, возвращается ли идентичный экземпляр Singleton, когда свойство экземпляра вызывается более одного раза.
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");
}
}
}
Примечание: эта реализация не является потокобезопасной.
Чтобы увидеть больше примеров, в том числе о том, как сделать этот поточно-безопасный, посетите: Реализация Singleton
Синглтоны концептуально похожи на глобальную ценность и вызывают подобные конструктивные недостатки и проблемы. Из-за этого шаблон Singleton широко рассматривается как анти-шаблон.
Посещение «Что так плохо в синглтонах?» для получения дополнительной информации о проблемах, возникающих при их использовании.
В C # у вас есть возможность создать класс static
, который делает все члены статическими, а класс не может быть создан. Учитывая это, обычно можно увидеть статические классы, используемые вместо шаблона Singleton.
Для основных различий между ними, посетите C # Singleton Pattern Versus Static Class .
Заводская модель
Фабричный метод - один из шаблонов дизайна. Он используется для решения проблемы создания объектов без указания точного типа результата. В этом документе вы научитесь правильно использовать Factory Method DP.
Позвольте мне объяснить вам это на простом примере. Представьте, что вы работаете на заводе, производящем три типа устройств - Амперметр, вольтметр и измеритель сопротивления. Вы пишете программу для центрального компьютера, которая будет создавать выбранное устройство, но вы не знаете окончательного решения своего начальника о том, что производить.
Давайте создадим IDevice
интерфейса с некоторыми общими функциями, которые есть у всех устройств:
public interface IDevice
{
int Measure();
void TurnOff();
void TurnOn();
}
Теперь мы можем создавать классы, которые представляют наши устройства. Эти классы должны реализовывать интерфейс 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..."); }
}
Теперь мы должны определить фабричный метод. Давайте создадим класс DeviceFactory
со статическим методом внутри:
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();
}
}
}
Большой! Давайте проверим наш код:
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();
}
}
Это пример вывода, который вы можете увидеть после запуска этого кода:
AmMeter включается ...
36
6
33
43
24
AmMeter мигает огнями, прощаясь!
Вольтметр включается ...
102
-61
85
138
36
Вольтметр мигает огнями, прощаясь!
OhmMeter включается ...
723828
368536
685412
800266
578595
OhmMeter мигает огнями, прощаясь!
Шаблон Builder
Отделите построение сложного объекта от его представления, чтобы тот же процесс построения мог создавать различные представления и обеспечивать высокий уровень контроля над сборкой объектов.
В этом примере демонстрируется шаблон Builder, в котором различные машины собираются поэтапно. Магазин использует VehicleBuilders для создания различных транспортных средств в серии последовательных шагов.
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"]);
}
}
}
Выход
Тип транспортного средства: Скутер Рамка: Скутер
Двигатель: нет
#Wheels: 2
#Doors: 0Тип автомобиля: Автомобиль
Рамка: Автомобильная рамка
Двигатель: 2500 куб. См.
#Wheels: 4
#Doors: 4Тип транспортного средства: Мотоцикл
Рамка: рамка мотоцикла
Двигатель: 500 куб. См
#Wheels: 2
#Doors: 0
Шаблон прототипа
Укажите тип объектов, созданных с использованием прототипа, и создайте новые объекты, скопировав этот прототип.
В этом примере демонстрируется шаблон Prototype, в котором новые объекты Color создаются путем копирования уже существующих, определяемых пользователем цветов одного и того же типа.
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); }
}
}
}
Выход:
Цвет клонирования RGB: 255, 0, 0
Цвет клонирования RGB: 128 211 128
Цвет клонирования RGB: 211, 34, 20
Абстрактный шаблон завода
Предоставить интерфейс для создания семейств связанных или зависимых объектов без указания их конкретных классов.
В этом примере демонстрируется создание разных животных миров для компьютерной игры с использованием разных фабрик. Хотя животные, созданные фабриками Континента, различны, взаимодействие между животными остается неизменным.
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);
}
}
}
Выход:
Лев ест Wildebeest
Волк ест Bison