Для чего нужен паттерн Фабрика (Factory Method/Simple Factory)?

«Для чего нужен паттерн Фабрика (Factory Method/Simple Factory)?» — вопрос из категории Паттерны, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Паттерн Фабрика (в частности, Simple Factory или Factory Method) решает проблему создания объектов, когда клиентский код не должен зависеть от конкретных классов создаваемых продуктов. Он инкапсулирует логику создания в отдельном компоненте.

Зачем это нужно?

  • Соблюдение принципа открытости/закрытости (OCP): Чтобы добавить новый тип продукта, вы изменяете код фабрики, а не все места в коде, где этот продукт создается.
  • Упрощение клиентского кода: Клиент работает с абстракцией (IDocument), а не с конкретными классами (PdfDocument, WordDocument).
  • Централизация сложной логики инициализации: Если создание объекта требует сложных шагов (например, конфигурации, проверок), это скрыто внутри фабрики.

Практический пример: Фабрика документов для системы отчетов

// 1. Абстракция продукта
public interface IDocument
{
    string Name { get; }
    byte[] Generate();
}

// 2. Конкретные продукты
public class PdfDocument : IDocument
{
    public string Name => "Report.pdf";
    public byte[] Generate()
    {
        // Логика генерации PDF (например, с помощью iTextSharp)
        Console.WriteLine("Generating PDF...");
        return new byte[] { /* PDF bytes */ };
    }
}

public class ExcelDocument : IDocument
{
    public string Name => "Data.xlsx";
    public byte[] Generate()
    {
        // Логика генерации Excel (например, с помощью EPPlus)
        Console.WriteLine("Generating Excel spreadsheet...");
        return new byte[] { /* Excel bytes */ };
    }
}

// 3. Фабрика (Simple Factory)
public static class DocumentFactory
{
    public static IDocument CreateDocument(string documentType, string data)
    {
        return documentType.ToUpper() switch
        {
            "PDF" => new PdfDocument(), // Может принимать data для наполнения
            "EXCEL" => new ExcelDocument(),
            _ => throw new ArgumentException($"Unsupported document type: {documentType}")
        };
    }
}

// 4. Использование клиентом
public class ReportService
{
    public void ExportReport(string format, string data)
    {
        // Клиент НЕ знает о конкретных классах PdfDocument/ExcelDocument
        IDocument document = DocumentFactory.CreateDocument(format, data);

        byte[] fileContent = document.Generate();
        File.WriteAllBytes(document.Name, fileContent);
        Console.WriteLine($"Document saved as {document.Name}");
    }
}

Развитие паттерна: Для более сложных сценариев используются Abstract Factory (создание семейств связанных объектов) и Factory Method (делегирование создания подклассам).