Паттерны C#

  1. Singleton Pattern (Одиночка): Один класс который имеет экземпляр предоставляющий глобальный доступ.

  2. Factory Pattern (Фабрика): Определяет общий интерфейс для создания объектов, но не дает подклассам решать какой экземпляр класса создавать.

  3. Observer Pattern (Наблюдатель): Определяет зависимость один ко многим между объектами, когда один объект изменяет состояние, все зависимые уведомляются автоматически.

  4. Decorator Pattern (Декоратор): Позволяет добавлять динамически новое поведение к отдельным объектам, не затрагивая поведение других объектов того же класса.

  5. Strategy Pattern (Стратегия): Определяет алгоритмы, инкапсулирует каждый из них и делает их взаимозаменяемыми. Стратегия позволяет алгоритму изменяться независимо от клиентов, которые его используют.

  6. Adapter Pattern (Адаптер): Преобразует интерфейс класса в другой интерфейс, ожидаемый клиентами. Он позволяет классам работать вместе, что иначе невозможно из-за несовместимых интерфейсов.

  7. Command Pattern: Инкапсулирует запрос как объект, тем самым параметризуя клиентов очередями, запросами и операциями.

Singleton Pattern (Одиночка)

using System;

public class Singleton
{
    private static Singleton instance;
    
    private Singleton() { }
    
    public static Singleton Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new Singleton();
            }
            return instance;
        }
    }

    public void DisplayMessage()
    {
        Console.WriteLine("This is a Singleton instance.");
    }
}

class Program
{
    static void Main()
    {
        // Attempt to create multiple instances of the Singleton class
        Singleton singleton1 = Singleton.Instance;
        Singleton singleton2 = Singleton.Instance;

        // Both instances should be the same
        Console.WriteLine("Are both instances the same? " + (singleton1 == singleton2));

        // Using the singleton instance
        singleton1.DisplayMessage();
    }
}

В методе Main мы демонстрируем создание двух экземпляров класса Singleton и показываем, что они являются одним и тем же объектом, проверяя их ссылки. Наконец, мы вызываем метод DisplayMessage для экземпляра Singleton, чтобы показать, что это действительно единственный экземпляр.

Factory Pattern

using System;

// Product interface
interface IProduct
{
    void Show();
}

// Concrete product classes
class ConcreteProduct1 : IProduct
{
    public void Show()
    {
        Console.WriteLine("Concrete Product 1");
    }
}

class ConcreteProduct2 : IProduct
{
    public void Show()
    {
        Console.WriteLine("Concrete Product 2");
    }
}

// Factory class
class ProductFactory
{
    public static IProduct CreateProduct(string productType)
    {
        switch (productType)
        {
            case "1":
                return new ConcreteProduct1();
            case "2":
                return new ConcreteProduct2();
            default:
                throw new ArgumentException("Invalid product type");
        }
    }
}

class Program
{
    static void Main()
    {
        // Using the factory to create objects
        IProduct product1 = ProductFactory.CreateProduct("1");
        product1.Show();

        IProduct product2 = ProductFactory.CreateProduct("2");
        product2.Show();
    }
}

В методе Main мы используем фабрику для создания экземпляров ConcreteProduct1 и ConcreteProduct2 без их непосредственного создания экземпляров. Таким образом, фабрика инкапсулирует логику создания объектов и предоставляет централизованное место для создания объектов на основе определенных условий или параметров.

Observer Pattern

using System;
using System.Collections.Generic;

// Subject interface
interface ISubject
{
    void RegisterObserver(IObserver observer);
    void RemoveObserver(IObserver observer);
    void NotifyObservers();
}

// Observer interface
interface IObserver
{
    void Update(string message);
}

// Concrete subject class
class Subject : ISubject
{
    private List<IObserver> observers = new List<IObserver>();
    private string message;

    public void RegisterObserver(IObserver observer)
    {
        observers.Add(observer);
    }

    public void RemoveObserver(IObserver observer)
    {
        observers.Remove(observer);
    }

    public void NotifyObservers()
    {
        foreach (var observer in observers)
        {
            observer.Update(message);
        }
    }

    public void SetMessage(string message)
    {
        this.message = message;
        NotifyObservers();
    }
}

// Concrete observer class
class ConcreteObserver : IObserver
{
    private string name;

    public ConcreteObserver(string name)
    {
        this.name = name;
    }

    public void Update(string message)
    {
        Console.WriteLine($"{name} received message: {message}");
    }
}

class Program
{
    static void Main()
    {
        Subject subject = new Subject();

        ConcreteObserver observer1 = new ConcreteObserver("Observer 1");
        ConcreteObserver observer2 = new ConcreteObserver("Observer 2");

        subject.RegisterObserver(observer1);
        subject.RegisterObserver(observer2);

        subject.SetMessage("Hello, Observers!");
    }
}

В методе Main мы создаем экземпляр subject и два экземпляра ConcreteObserver. Регистрируем обоих наблюдателей с субъектом, а затем устанавливаем сообщение на субъекте. Это заставляет субъект уведомлять всех зарегистрированных наблюдателей об изменении сообщения, и каждый наблюдатель распечатывает полученное сообщение.

Decorator Pattern

using System;

// Component interface
public interface ICar
{
    string GetDescription();
    double GetCost();
}

// Concrete component
public class EconomyCar : ICar
{
    public string GetDescription()
    {
        return "Economy Car";
    }

    public double GetCost()
    {
        return 20000.0;
    }
}

// Decorator
public abstract class CarDecorator : ICar
{
    protected ICar _car;

    public CarDecorator(ICar car)
    {
        _car = car;
    }

    public virtual string GetDescription()
    {
        return _car.GetDescription();
    }

    public virtual double GetCost()
    {
        return _car.GetCost();
    }
}

// Concrete decorator
public class SportsPackage : CarDecorator
{
    public SportsPackage(ICar car) : base(car)
    {
    }

    public override string GetDescription()
    {
        return $"{base.GetDescription()}, Sports Package";
    }

    public override double GetCost()
    {
        return base.GetCost() + 5000.0;
    }
}

class Program
{
    static void Main()
    {
        // Create an economy car
        ICar economyCar = new EconomyCar();
        Console.WriteLine($"Description: {economyCar.GetDescription()}, Cost: {economyCar.GetCost()}");

        // Add sports package to the economy car using decorator
        ICar sportsCar = new SportsPackage(economyCar);
        Console.WriteLine($"Description: {sportsCar.GetDescription()}, Cost: {sportsCar.GetCost()}");
    }
}

В методе Main мы создаем экземпляр автомобиля эконом-класса, затем украшаем его спортивной комплектацией с помощью шаблона декоратора.

Strategy Pattern

using System;

// Strategy interface
public interface IStrategy
{
    void Execute();
}

// Concrete strategies
public class ConcreteStrategyA : IStrategy
{
    public void Execute()
    {
        Console.WriteLine("Executing strategy A");
    }
}

public class ConcreteStrategyB : IStrategy
{
    public void Execute()
    {
        Console.WriteLine("Executing strategy B");
    }
}

// Context class
public class Context
{
    private IStrategy _strategy;

    public Context(IStrategy strategy)
    {
        _strategy = strategy;
    }

    public void SetStrategy(IStrategy strategy)
    {
        _strategy = strategy;
    }

    public void ExecuteStrategy()
    {
        _strategy.Execute();
    }
}

class Program
{
    static void Main()
    {
        // Create context with a default strategy
        Context context = new Context(new ConcreteStrategyA());

        // Execute the default strategy
        context.ExecuteStrategy();

        // Change the strategy at runtime
        context.SetStrategy(new ConcreteStrategyB());

        // Execute the new strategy
        context.ExecuteStrategy();
    }
}

В методе Main мы создаем объект Context со стратегией по умолчанию ConcreteStrategyA, выполняем эту стратегию по умолчанию, затем во время выполнения меняем стратегию на ConcreteStrategyB и выполняем ее. Это демонстрирует, как шаблон стратегии позволяет изменять поведение объекта во время выполнения, изменяя его стратегию.

Adapter pattern

using System;

// Target interface that the client code expects to work with
interface ITarget
{
   void Request();
}

// Adaptee class that needs to be adapted to work with the client code
class Adaptee
{
   public void SpecificRequest()
   {
       Console.WriteLine("Adaptee's specific request.");
   }
}

// Adapter class that adapts the Adaptee to the Target interface
class Adapter : ITarget
{
   private readonly Adaptee adaptee;

   public Adapter(Adaptee adaptee)
   {
       this.adaptee = adaptee;
   }

   public void Request()
   {
       adaptee.SpecificRequest();
   }
}

class Program
{
   static void Main()
   {
       // Using the Adaptee class directly
       Adaptee adaptee = new Adaptee();
       adaptee.SpecificRequest();

       // Using the Adapter to adapt the Adaptee to the ITarget interface
       ITarget adapter = new Adapter(adaptee);
       adapter.Request();
   }
}
  • ITarget — это целевой интерфейс, с которым ожидается работа клиентского кода.
  • Adaptee — это класс, имеющий другой интерфейс, который необходимо адаптировать.
  • Adapter — это класс адаптера, который реализует интерфейс ITarget и делегирует запрос Adaptee.
  • В методе Main мы демонстрируем непосредственное использование класса Adaptee, а затем его адаптацию с помощью класса Adapter для работы с интерфейсом ITarget.

Этот шаблон позволяет объектам с несовместимыми интерфейсами работать вместе. Адаптер действует как мост между клиентским кодом и Адаптируемым, позволяя им беспрепятственно работать вместе.

Command Pattern

using System;
using System.Collections.Generic;

// Command interface
interface ICommand
{
    void Execute();
}

// Receiver class
class Receiver
{
    public void Action()
    {
        Console.WriteLine("Receiver is performing an action.");
    }
}

// Concrete command classes
class ConcreteCommand1 : ICommand
{
    private Receiver receiver;

    public ConcreteCommand1(Receiver receiver)
    {
        this.receiver = receiver;
    }

    public void Execute()
    {
        receiver.Action();
        Console.WriteLine("ConcreteCommand1 executed.");
    }
}

class ConcreteCommand2 : ICommand
{
    private Receiver receiver;

    public ConcreteCommand2(Receiver receiver)
    {
        this.receiver = receiver;
    }

    public void Execute()
    {
        receiver.Action();
        Console.WriteLine("ConcreteCommand2 executed.");
    }
}

// Invoker class
class Invoker
{
    private List<ICommand> commands = new List<ICommand>();

    public void AddCommand(ICommand command)
    {
        commands.Add(command);
    }

    public void ExecuteCommands()
    {
        foreach (var command in commands)
        {
            command.Execute();
        }
    }
}

class Program
{
    static void Main()
    {
        Receiver receiver = new Receiver();
        
        ICommand command1 = new ConcreteCommand1(receiver);
        ICommand command2 = new ConcreteCommand2(receiver);

        Invoker invoker = new Invoker();
        invoker.AddCommand(command1);
        invoker.AddCommand(command2);

        invoker.ExecuteCommands();
    }
}
  • ICommand — это командный интерфейс, в котором объявляется метод выполнения команды.
  • Receiver — это класс, который выполняет фактическое действие, связанное с командой.
  • ConcreteCommand1 и ConcreteCommand2 — это конкретные классы команд, которые реализуют интерфейс ICommand и выполняют определенные действия на приемнике.
  • Invoker — это класс, который содержит и выполняет список команд.
  • В методе Main мы создаём экземпляры приёмника, конкретных команд и инициатора. Мы добавляем команды в вызывающую программу и затем выполняем их.

Этот шаблон отделяет отправителя запроса от объекта, который выполняет запрос, обеспечивая гибкость в параметризации клиентов с различными запросами, а также в организации очереди или регистрации запросов.

Оставьте комментарий

Обратите внимание, что мы не будем показывать ваш адрес электронной почты другим пользователям и не будем использовать его для отправки нежелательных электронных писем.