Введение
В мире профессиональной разработки на C# знание паттернов проектирования — это не просто теоретические знания из книг Гаммы и компании, а практический инструментарий для решения повседневных задач архитектора и разработчика.
Порождающие паттерны: контроль создания объектов
1.1. Singleton (Одиночка) — современные реализации
Классическая проблема: гарантировать, что у класса будет только один экземпляр, и предоставить к нему глобальную точку доступа.
1.1.1. Потокобезопасная реализация с Lazy<T>
public sealed class ConfigurationManager { // Lazy<T> гарантирует потокобезопасную ленивую инициализацию private static readonly Lazy<ConfigurationManager> _instance = new Lazy<ConfigurationManager>(() => new ConfigurationManager()); public static ConfigurationManager Instance => _instance.Value; private readonly ConcurrentDictionary<string, string> _settings; // Приватный конструктор предотвращает создание экземпляров извне private ConfigurationManager() { _settings = new ConcurrentDictionary<string, string>(); LoadConfiguration(); } public string GetSetting(string key) => _settings.GetValueOrDefault(key); private void LoadConfiguration() { // Загрузка конфигурации из файла, базы данных или среды выполнения _settings["ApiEndpoint"] = Environment.GetEnvironmentVariable("API_ENDPOINT") ?? "https://api.default.com"; _settings["RetryCount"] = "3"; } } // Использование var config = ConfigurationManager.Instance; var endpoint = config.GetSetting("ApiEndpoint");
1.1.2. Проблемы и ограничения синглтона
// АНТИПАТТЕРН: Синглтон как глобальное состояние public class GlobalState // Плохо! { public static GlobalState Instance { get; } = new GlobalState(); public string CurrentUser { get; set; } // Общее изменяемое состояние! } // ПРАВИЛЬНЫЙ ПОДХОД: Внедрение зависимостей public interface IConfigurationService { string GetSetting(string key); } public class ConfigurationService : IConfigurationService { // Регистрируем как Singleton в DI-контейнере private readonly IReadOnlyDictionary<string, string> _settings; public ConfigurationService(IConfiguration configuration) { _settings = configuration.GetSection("AppSettings") .Get<Dictionary<string, string>>() .AsReadOnly(); } public string GetSetting(string key) => _settings.GetValueOrDefault(key); } // В ASP.NET Core Startup.cs services.AddSingleton<IConfigurationService, ConfigurationService>();
1.2. Factory Method (Фабричный метод) и Abstract Factory (Абстрактная фабрика)
1.2.1. Фабричный метод с обобщениями
public abstract class Document { public abstract string Content { get; } public abstract void Render(); } public class PdfDocument : Document { public override string Content => "PDF Content"; public override void Render() => Console.WriteLine("Rendering PDF..."); } public class WordDocument : Document { public override string Content => "Word Content"; public override void Render() => Console.WriteLine("Rendering Word..."); } public abstract class DocumentCreator { // Фабричный метод public abstract Document CreateDocument(); // Бизнес-логика, использующая фабричный метод public void ProcessDocument() { var document = CreateDocument(); Console.WriteLine($"Processing: {document.Content}"); document.Render(); } } public class PdfCreator : DocumentCreator { public override Document CreateDocument() => new PdfDocument(); } public class WordCreator : DocumentCreator { public override Document CreateDocument() => new WordDocument(); } // Использование DocumentCreator creator = new PdfCreator(); creator.ProcessDocument();
1.2.2. Абстрактная фабрика для кроссплатформенных UI
public interface IButton { void Render(); void OnClick(Action action); } public interface ITextBox { void Render(); string GetText(); } // Абстрактная фабрика public interface IUIFactory { IButton CreateButton(); ITextBox CreateTextBox(); } // Конкретные фабрики для разных платформ public class WindowsUIFactory : IUIFactory { public IButton CreateButton() => new WindowsButton(); public ITextBox CreateTextBox() => new WindowsTextBox(); } public class WebUIFactory : IUIFactory { public IButton CreateButton() => new WebButton(); public ITextBox CreateTextBox() => new WebTextBox(); } public class Application { private readonly IUIFactory _uiFactory; public Application(IUIFactory uiFactory) { _uiFactory = uiFactory; } public void RenderUI() { var button = _uiFactory.CreateButton(); var textBox = _uiFactory.CreateTextBox(); button.OnClick(() => Console.WriteLine($"Button clicked: {textBox.GetText()}")); button.Render(); textBox.Render(); } } // Использование с DI var platform = Environment.OSVersion.Platform == PlatformID.Win32NT ? new WindowsUIFactory() : new WebUIFactory(); var app = new Application(platform); app.RenderUI();
Структурные паттерны: организация классов и объектов
2.1. Adapter (Адаптер) — интеграция legacy-кода
2.1.1. Адаптер для сторонней библиотеки
// Старая система (legacy code) public class LegacyLogger { public void LogMessage(string severity, string message, DateTime timestamp) { // Сложный форматированный вывод Console.WriteLine($"[{timestamp:yyyy-MM-dd HH:mm:ss}] {severity}: {message}"); } } // Новый интерфейс, принятый в проекте public interface IModernLogger { void LogInfo(string message); void LogError(string message); void LogWarning(string message); } // Адаптер public class LegacyLoggerAdapter : IModernLogger { private readonly LegacyLogger _legacyLogger; public LegacyLoggerAdapter(LegacyLogger legacyLogger) { _legacyLogger = legacyLogger; } public void LogInfo(string message) { _legacyLogger.LogMessage("INFO", message, DateTime.Now); } public void LogError(string message) { _legacyLogger.LogMessage("ERROR", message, DateTime.Now); } public void LogWarning(string message) { _legacyLogger.LogMessage("WARNING", message, DateTime.Now); } } // Использование в современном коде public class OrderService { private readonly IModernLogger _logger; public OrderService(IModernLogger logger) { _logger = logger; } public void ProcessOrder(Order order) { try { _logger.LogInfo($"Processing order {order.Id}"); // Логика обработки _logger.LogInfo($"Order {order.Id} processed successfully"); } catch (Exception ex) { _logger.LogError($"Failed to process order {order.Id}: {ex.Message}"); } } }
2.2. Decorator (Декоратор) — добавление функциональности без изменения классов
2.2.1. Декоратор для кэширования
public interface IDataService { Task<string> GetDataAsync(string key); } // Базовая реализация public class DatabaseDataService : IDataService { public async Task<string> GetDataAsync(string key) { await Task.Delay(100); // Имитация долгого запроса к БД return $"Data for {key} from database"; } } // Декоратор для логирования public class LoggingDataServiceDecorator : IDataService { private readonly IDataService _innerService; private readonly ILogger _logger; public LoggingDataServiceDecorator(IDataService innerService, ILogger logger) { _innerService = innerService; _logger = logger; } public async Task<string> GetDataAsync(string key) { _logger.LogInformation($"Getting data for key: {key}"); var stopwatch = Stopwatch.StartNew(); try { var result = await _innerService.GetDataAsync(key); stopwatch.Stop(); _logger.LogInformation($"Successfully got data for {key} in {stopwatch.ElapsedMilliseconds}ms"); return result; } catch (Exception ex) { _logger.LogError(ex, $"Failed to get data for key: {key}"); throw; } } } // Декоратор для кэширования public class CachingDataServiceDecorator : IDataService { private readonly IDataService _innerService; private readonly IMemoryCache _cache; private readonly TimeSpan _cacheDuration; public CachingDataServiceDecorator( IDataService innerService, IMemoryCache cache, TimeSpan cacheDuration) { _innerService = innerService; _cache = cache; _cacheDuration = cacheDuration; } public async Task<string> GetDataAsync(string key) { if (_cache.TryGetValue<string>(key, out var cachedData)) { return cachedData; } var data = await _innerService.GetDataAsync(key); _cache.Set(key, data, _cacheDuration); return data; } } // Регистрация цепочки декораторов в DI services.AddScoped<DatabaseDataService>(); services.Decorate<IDataService, LoggingDataServiceDecorator>(); services.Decorate<IDataService, CachingDataServiceDecorator>(); // Использование - клиент получает полностью обернутый сервис public class ReportGenerator { private readonly IDataService _dataService; public ReportGenerator(IDataService dataService) { // Внедряется цепочка: Caching -> Logging -> Database _dataService = dataService; } public async Task GenerateReportAsync() { var data = await _dataService.GetDataAsync("report_data"); // data будет залогирована и закэширована автоматически } }
2.3. Composite (Компоновщик) — древовидные структуры
2.3.1. Система управления файлами и папками
public abstract class FileSystemComponent { public string Name { get; } public virtual long Size { get; } protected FileSystemComponent(string name) { Name = name; } public abstract void Display(int indent = 0); } // Листовой элемент public class File : FileSystemComponent { public long FileSize { get; } public override long Size => FileSize; public File(string name, long size) : base(name) { FileSize = size; } public override void Display(int indent = 0) { Console.WriteLine($"{new string(' ', indent)}📄 {Name} ({Size} bytes)"); } } // Составной элемент public class Directory : FileSystemComponent { private readonly List<FileSystemComponent> _children = new(); public override long Size => _children.Sum(c => c.Size); public Directory(string name) : base(name) { } public void Add(FileSystemComponent component) { _children.Add(component); } public void Remove(FileSystemComponent component) { _children.Remove(component); } public override void Display(int indent = 0) { Console.WriteLine($"{new string(' ', indent)}📁 {Name} (Total: {Size} bytes)"); foreach (var child in _children) { child.Display(indent + 2); } } } // Использование var root = new Directory("Root"); var documents = new Directory("Documents"); var images = new Directory("Images"); documents.Add(new File("resume.pdf", 2048)); documents.Add(new File("contract.docx", 4096)); images.Add(new File("photo1.jpg", 1024 * 1024)); images.Add(new File("photo2.png", 2048 * 1024)); root.Add(documents); root.Add(images); root.Add(new File("readme.txt", 512)); root.Display(); // Вывод: // 📁 Root (Total: 3149824 bytes) // 📁 Documents (Total: 6144 bytes) // 📄 resume.pdf (2048 bytes) // 📄 contract.docx (4096 bytes) // 📁 Images (Total: 3143680 bytes) // 📄 photo1.jpg (1048576 bytes) // 📄 photo2.png (2095104 bytes) // 📄 readme.txt (512 bytes)
Поведенческие паттерны: взаимодействие объектов
3.1. Strategy (Стратегия) — выбор алгоритма во время выполнения
3.1.1. Стратегия для различных алгоритмов сортировки
public interface ISortStrategy<T> { IEnumerable<T> Sort(IEnumerable<T> collection); } // Конкретные стратегии public class QuickSortStrategy<T> : ISortStrategy<T> where T : IComparable<T> { public IEnumerable<T> Sort(IEnumerable<T> collection) { var list = collection.ToList(); QuickSort(list, 0, list.Count - 1); return list; } private void QuickSort(List<T> list, int left, int right) { if (left < right) { int pivot = Partition(list, left, right); QuickSort(list, left, pivot - 1); QuickSort(list, pivot + 1, right); } } private int Partition(List<T> list, int left, int right) { T pivot = list[right]; int i = left - 1; for (int j = left; j < right; j++) { if (list[j].CompareTo(pivot) <= 0) { i++; (list[i], list[j]) = (list[j], list[i]); } } (list[i + 1], list[right]) = (list[right], list[i + 1]); return i + 1; } } public class MergeSortStrategy<T> : ISortStrategy<T> where T : IComparable<T> { public IEnumerable<T> Sort(IEnumerable<T> collection) { var list = collection.ToList(); if (list.Count <= 1) return list; return MergeSort(list); } private List<T> MergeSort(List<T> list) { if (list.Count <= 1) return list; var middle = list.Count / 2; var left = list.Take(middle).ToList(); var right = list.Skip(middle).ToList(); return Merge(MergeSort(left), MergeSort(right)); } private List<T> Merge(List<T> left, List<T> right) { var result = new List<T>(); int leftIndex = 0, rightIndex = 0; while (leftIndex < left.Count && rightIndex < right.Count) { if (left[leftIndex].CompareTo(right[rightIndex]) <= 0) { result.Add(left[leftIndex++]); } else { result.Add(right[rightIndex++]); } } while (leftIndex < left.Count) result.Add(left[leftIndex++]); while (rightIndex < right.Count) result.Add(right[rightIndex++]); return result; } } // Контекст, использующий стратегию public class Sorter<T> where T : IComparable<T> { private ISortStrategy<T> _strategy; public Sorter(ISortStrategy<T> strategy) { _strategy = strategy; } public void SetStrategy(ISortStrategy<T> strategy) { _strategy = strategy; } public IEnumerable<T> ExecuteSort(IEnumerable<T> collection) { Console.WriteLine($"Using {_strategy.GetType().Name}"); return _strategy.Sort(collection); } } // Использование с лямбда-выражениями (альтернатива интерфейсу) public class FlexibleSorter<T> { private Func<IEnumerable<T>, IEnumerable<T>> _sortAlgorithm; public FlexibleSorter(Func<IEnumerable<T>, IEnumerable<T>> sortAlgorithm) { _sortAlgorithm = sortAlgorithm; } public IEnumerable<T> Sort(IEnumerable<T> collection) => _sortAlgorithm(collection); } // Регистрация стратегий в DI services.AddTransient<ISortStrategy<int>, QuickSortStrategy<int>>(); services.AddTransient<ISortStrategy<int>, MergeSortStrategy<int>>(); services.AddTransient<ISortStrategy<string>, QuickSortStrategy<string>>(); // Использование var data = new[] { 5, 3, 8, 1, 9, 2 }; var sorter = new Sorter<int>(new QuickSortStrategy<int>()); var sorted1 = sorter.ExecuteSort(data); sorter.SetStrategy(new MergeSortStrategy<int>()); var sorted2 = sorter.ExecuteSort(data); // Использование лямбда-выражения var lambdaSorter = new FlexibleSorter<int>(collection => collection.OrderBy(x => x).ToList()); var sorted3 = lambdaSorter.Sort(data);
3.2. Observer (Наблюдатель) — современная реализация через события
3.2.1. Система уведомлений с событиями C#
// Событийная модель (классическая для C#) public class StockMarket { // Событие с использованием EventHandler<T> public event EventHandler<StockPriceChangedEventArgs> StockPriceChanged; private readonly Dictionary<string, decimal> _stockPrices = new(); public void UpdateStockPrice(string symbol, decimal price) { var oldPrice = _stockPrices.GetValueOrDefault(symbol); _stockPrices[symbol] = price; // Генерация события OnStockPriceChanged(new StockPriceChangedEventArgs(symbol, oldPrice, price)); } protected virtual void OnStockPriceChanged(StockPriceChangedEventArgs e) { StockPriceChanged?.Invoke(this, e); } } public class StockPriceChangedEventArgs : EventArgs { public string Symbol { get; } public decimal OldPrice { get; } public decimal NewPrice { get; } public decimal ChangePercent => OldPrice != 0 ? ((NewPrice - OldPrice) / OldPrice) * 100 : 0; public StockPriceChangedEventArgs(string symbol, decimal oldPrice, decimal newPrice) { Symbol = symbol; OldPrice = oldPrice; NewPrice = newPrice; } } // Подписчики (наблюдатели) public class Trader { private readonly string _name; public Trader(string name) { _name = name; } public void Subscribe(StockMarket market) { market.StockPriceChanged += OnStockPriceChanged; } public void Unsubscribe(StockMarket market) { market.StockPriceChanged -= OnStockPriceChanged; } private void OnStockPriceChanged(object sender, StockPriceChangedEventArgs e) { Console.WriteLine($"[{_name}] {e.Symbol}: {e.OldPrice:C} -> {e.NewPrice:C} " + $"({e.ChangePercent:+#.##;-#.##}%)"); if (Math.Abs(e.ChangePercent) > 5) { Console.WriteLine($"[{_name}] ALERT: Significant change in {e.Symbol}!"); } } } public class StockAnalyzer { private readonly Dictionary<string, List<decimal>> _priceHistory = new(); public void Subscribe(StockMarket market) { market.StockPriceChanged += OnStockPriceChanged; } private void OnStockPriceChanged(object sender, StockPriceChangedEventArgs e) { if (!_priceHistory.ContainsKey(e.Symbol)) { _priceHistory[e.Symbol] = new List<decimal>(); } _priceHistory[e.Symbol].Add(e.NewPrice); if (_priceHistory[e.Symbol].Count >= 10) { var recentPrices = _priceHistory[e.Symbol].TakeLast(10); var average = recentPrices.Average(); var volatility = CalculateVolatility(recentPrices); Console.WriteLine($"[Analyzer] {e.Symbol}: " + $"10-period avg: {average:C}, volatility: {volatility:P2}"); } } private decimal CalculateVolatility(IEnumerable<decimal> prices) { var priceList = prices.ToList(); var mean = priceList.Average(); var variance = priceList.Select(p => Math.Pow((double)(p - mean), 2)).Average(); return (decimal)Math.Sqrt(variance) / (mean != 0 ? mean : 1); } } // Использование var market = new StockMarket(); var trader1 = new Trader("John"); var trader2 = new Trader("Alice"); var analyzer = new StockAnalyzer(); trader1.Subscribe(market); trader2.Subscribe(market); analyzer.Subscribe(market); market.UpdateStockPrice("AAPL", 150.00m); market.UpdateStockPrice("AAPL", 155.50m); market.UpdateStockPrice("GOOGL", 2800.00m); market.UpdateStockPrice("AAPL", 145.00m); // Падение более 5% // Результат: // [John] AAPL: $0.00 -> $150.00 (+0%) // [Alice] AAPL: $0.00 -> $150.00 (+0%) // [John] AAPL: $150.00 -> $155.50 (+3.67%) // [Alice] AAPL: $150.00 -> $155.50 (+3.67%) // [John] GOOGL: $0.00 -> $2,800.00 (+0%) // [Alice] GOOGL: $0.00 -> $2,800.00 (+0%) // [John] AAPL: $155.50 -> $145.00 (-6.75%) // [John] ALERT: Significant change in AAPL! // [Alice] AAPL: $155.50 -> $145.00 (-6.75%) // [Alice] ALERT: Significant change in AAPL!
3.3. Command (Команда) — отмена и повтор операций
3.3.1. Система отмены действий в графическом редакторе
public interface ICommand { void Execute(); void Undo(); } // Конкретные команды public class AddShapeCommand : ICommand { private readonly Canvas _canvas; private readonly Shape _shape; private bool _isExecuted = false; public AddShapeCommand(Canvas canvas, Shape shape) { _canvas = canvas; _shape = shape; } public void Execute() { if (!_isExecuted) { _canvas.AddShape(_shape); _isExecuted = true; } } public void Undo() { if (_isExecuted) { _canvas.RemoveShape(_shape); _isExecuted = false; } } } public class MoveShapeCommand : ICommand { private readonly Canvas _canvas; private readonly Shape _shape; private readonly (int X, int Y) _oldPosition; private readonly (int X, int Y) _newPosition; private bool _isExecuted = false; public MoveShapeCommand(Canvas canvas, Shape shape, int newX, int newY) { _canvas = canvas; _shape = shape; _oldPosition = (shape.X, shape.Y); _newPosition = (newX, newY); } public void Execute() { if (!_isExecuted) { _shape.Move(_newPosition.X, _newPosition.Y); _isExecuted = true; } } public void Undo() { if (_isExecuted) { _shape.Move(_oldPosition.X, _oldPosition.Y); _isExecuted = false; } } } // Примитивы public class Shape { public int X { get; private set; } public int Y { get; private set; } public string Color { get; set; } public Shape(int x, int y, string color) { X = x; Y = y; Color = color; } public void Move(int x, int y) { X = x; Y = y; Console.WriteLine($"Moved shape to ({X}, {Y})"); } public void Draw() { Console.WriteLine($"Drawing {Color} shape at ({X}, {Y})"); } } public class Canvas { private readonly List<Shape> _shapes = new(); public void AddShape(Shape shape) { _shapes.Add(shape); Console.WriteLine($"Added shape at ({shape.X}, {shape.Y})"); } public void RemoveShape(Shape shape) { _shapes.Remove(shape); Console.WriteLine($"Removed shape from ({shape.X}, {shape.Y})"); } public void Render() { Console.WriteLine("\n--- Canvas ---"); foreach (var shape in _shapes) { shape.Draw(); } Console.WriteLine("--------------\n"); } } // Инвокер (вызывающий объект) с историей команд public class CommandManager { private readonly Stack<ICommand> _undoStack = new(); private readonly Stack<ICommand> _redoStack = new(); public void ExecuteCommand(ICommand command) { command.Execute(); _undoStack.Push(command); _redoStack.Clear(); // Новая команда очищает историю повторов } public void Undo() { if (_undoStack.Count > 0) { var command = _undoStack.Pop(); command.Undo(); _redoStack.Push(command); } else { Console.WriteLine("Nothing to undo"); } } public void Redo() { if (_redoStack.Count > 0) { var command = _redoStack.Pop(); command.Execute(); _undoStack.Push(command); } else { Console.WriteLine("Nothing to redo"); } } public void PrintHistory() { Console.WriteLine($"Undo stack: {_undoStack.Count} commands"); Console.WriteLine($"Redo stack: {_redoStack.Count} commands"); } } // Использование var canvas = new Canvas(); var commandManager = new CommandManager(); var shape1 = new Shape(10, 10, "Red"); var shape2 = new Shape(50, 50, "Blue"); // Выполнение команд commandManager.ExecuteCommand(new AddShapeCommand(canvas, shape1)); canvas.Render(); commandManager.ExecuteCommand(new AddShapeCommand(canvas, shape2)); canvas.Render(); commandManager.ExecuteCommand(new MoveShapeCommand(canvas, shape1, 20, 20)); canvas.Render(); // Отмена Console.WriteLine("\n--- Undo last command ---"); commandManager.Undo(); canvas.Render(); Console.WriteLine("\n--- Undo another command ---"); commandManager.Undo(); canvas.Render(); // Повтор Console.WriteLine("\n--- Redo ---"); commandManager.Redo(); canvas.Render(); commandManager.PrintHistory();
Современные подходы и антипаттерны
4.1. Паттерны в функциональном стиле C#
// Функциональная реализация стратегии public static class FunctionalStrategy { public static IEnumerable<T> Sort<T>( IEnumerable<T> collection, Func<IEnumerable<T>, IEnumerable<T>> strategy) => strategy(collection); // Каррирование стратегий public static Func<IEnumerable<T>, IEnumerable<T>> QuickSortStrategy<T>() where T : IComparable<T> { return collection => { var list = collection.ToList(); QuickSort(list, 0, list.Count - 1); return list; }; void QuickSort(List<T> list, int left, int right) { /* реализация */ } } } // Использование var data = new[] { 3, 1, 4, 1, 5, 9 }; var sorted = FunctionalStrategy.Sort(data, FunctionalStrategy.QuickSortStrategy<int>());
4.2. Антипаттерны и неправильное использование
// АНТИПАТТЕРН: God Object Singleton public class ApplicationManager // Плохо! { public static ApplicationManager Instance { get; } = new ApplicationManager(); public void ProcessOrder() { /* ... */ } public void SendEmail() { /* ... */ } public void UpdateDatabase() { /* ... */ } public void GenerateReport() { /* ... */ } // Десятки несвязанных методов... } // АНТИПАТТЕРН: Переусложненный паттерн public class OverEngineeredFactory { private readonly Dictionary<Type, Func<object>> _factories = new(); public void Register<T>(Func<T> factory) => _factories[typeof(T)] = () => factory(); public T Create<T>() { if (_factories.TryGetValue(typeof(T), out var factory)) return (T)factory(); throw new InvalidOperationException($"No factory for {typeof(T)}"); } // Слишком сложно для простых случаев! } // ПРАВИЛЬНЫЙ ПОДХОД: KISS (Keep It Simple, Stupid) public class SimpleServiceFactory { public T Create<T>() where T : new() => new T(); }
4.3. Паттерны в асинхронном контексте
public interface IAsyncCommand { Task ExecuteAsync(CancellationToken cancellationToken); Task UndoAsync(CancellationToken cancellationToken); } public class AsyncSaveCommand : IAsyncCommand { private readonly IDataRepository _repository; private readonly Data _data; private Data _backup; public AsyncSaveCommand(IDataRepository repository, Data data) { _repository = repository; _data = data; } public async Task ExecuteAsync(CancellationToken cancellationToken) { _backup = await _repository.GetByIdAsync(_data.Id, cancellationToken); await _repository.SaveAsync(_data, cancellationToken); } public async Task UndoAsync(CancellationToken cancellationToken) { if (_backup != null) { await _repository.SaveAsync(_backup, cancellationToken); } } } // Асинхронная фабрика public class AsyncFactory { public async Task<T> CreateAsync<T>() where T : IInitializable, new() { var instance = new T(); await instance.InitializeAsync(); return instance; } }
Заключение
Паттерны проектирования в C# — это не догма, а гибкий инструментарий, который должен применяться осознанно и в соответствии с конкретными задачами проекта.
Для дальнейшего изучения
-
Domain-Driven Design (DDD): Паттерны предметной области — Entity, Value Object, Aggregate, Repository, Domain Service.
-
Микросервисные паттерны: Saga, CQRS, API Gateway, Circuit Breaker, Service Discovery.
-
Архитектурные стили: Clean Architecture, Hexagonal Architecture, Onion Architecture.
-
Реактивные паттерны: Reactive Extensions (Rx.NET), Message Queue patterns.
-
Оптимизация производительности: Паттерны кэширования, пулинга объектов, ленивой загрузки.
-
Тестирование: Паттерны для unit-тестов (Test Doubles, Mock, Stub, Fake).
-
Контейнеры зависимостей: Регистрация паттернов в DI-контейнерах (Scoped, Singleton, Transient).
-
Source Generators: Автоматическая генерация кода для часто используемых паттернов.
-
Records и паттерны: Использование C# 9+ records для реализации Value Objects.
-
Паттерны параллельного программирования: Producer-Consumer, Reader-Writer, Barrier, Fork-Join.

