фабричный образец против твердых принципов - PullRequest
0 голосов
/ 13 мая 2019

Я прочитал о заводском шаблоне, и мне интересно, совместим ли он с принципом Open-Closed Solid.Во многих примерах фабричный метод принимает enum, а затем в операторе switch создает конкретный объект, см. Пример C # на вики-странице .Звучит разумно, что я могу решить, какой объект следует создать на основе enum.С другой стороны, когда enum расширяется, я также должен добавить в регистр switch новый регистр, который нарушает принцип Open-Closed Solid.

Мне также интересно, действительно ли вики-страница описывает шаблон фабричного метода или что-то, что называется простой фабрикой.Я могу создать следующий интерфейс:

public interface IPersonFactory
{
    IPerson CreatePerson();
}

и затем реализовать конкретную фабрику, например:

public class VillagerFactory : IPersonFactory
{
    public IPerson CreatePerson()
    {
        return new Villager();
    }
}

Затем, основываясь на enum, я могу решить, какую реализацию фабрики следует использовать, но сновакогда enum расширяется, я также должен добавить регистр в оператор switch где-то в коде, поэтому я не уверен, что второй подход лучше.

Не могли бы вы уточнить это?

Ответы [ 2 ]

1 голос
/ 14 мая 2019

Шаблон фабричного метода GoF не имеет ничего общего с enum и фактически очень совместим с принципом Open-closed.

Допустим, вы кодируете класс C.В некотором коде внутри C вы используете ключевое слово new для создания объектов некоторого класса X.

. Позже вам нужно где-то повторно использовать класс C, но с настройкой.Единственная необходимая настройка заключается в том, что внутри C вы хотите создать объекты некоторого производного класса X, скажем X'.

Затем вы применяете шаблон Factory Method, делая класс C moreabstract: добавьте абстрактный метод с сигнатурой, возвращающей объект типа X, замените каждый вызов new X на вызов абстрактного метода.Остальной код класса C остается нетронутым.

Мы называем этот абстрактный метод Фабричный метод .

Теперь вы можете повторно использовать класс C где-нибудь: write classC' наследует C, реализует абстрактный метод, позволяет ему создавать (обычно с ключевым словом new) и возвращает объект любого типа X, X', X'', который вы хотите.

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

Разработка программного обеспечения путем добавления большего количества классов, а не путем (или редко путем) изменения существующих классов.Это видение ООП.

0 голосов
/ 14 мая 2019

У заводской модели много применений. Я дам краткое описание их и их использования на очень простых примерах.

ПРИМЕЧАНИЕ. Примеры имеют только иллюстративное назначение, поэтому некоторые из них могут показаться излишними, а другие - не хорошим решением проблемы. Трудно дать реалистичный пример для всего, поэтому я дам себе некоторую свободу, игнорируя некоторые принципы в иллюстративных целях

Проблема: У вас есть класс, который имеет много конструкторов с разными параметрами. Это делает код трудным для чтения, записи и рассуждений.

Решение: Создайте фабрику, которая упрощает создание объектов и делает код чище благодаря наличию нескольких хорошо названных методов.

Пример: У вас есть система, которая имеет много различных типов продуктов.

public enum ProductType { Book, Paper, Pen }

public class Product {
    public ProductType Type { get; set;}
}

public class ProductFactory {
    public Product CreateBook() { return new Product(ProductType.Book); }
    public Product CreatePaper() { return new Product(ProductType.Paper); }
    public Product CreatePen() { return new Product(ProductType.Pen); }
}

Проблема: У вас есть объект, созданный из другого ввода и / или из-за необходимости обработки этого ввода. Или у вас есть объект, который имеет сложный творческий процесс.

Решение: Создайте фабрику, которая сделает это за вас с помощью хорошо названных методов.

Пример: У вас есть система, которая обрабатывает файлы из файловой системы и использует их расширения для сложных операций. Вы хотите использовать FileExtension class для обработки равенства (в Windows регистр не учитывается) и других операций, чтобы избежать появления плавающих строк, избежать ошибок при сравнении без учета регистра и максимально использовать строгую типизацию

public class FileExtension { }

public class FileExtensionFactory {

    public FileExtension Create(string extension) {
        return new FileExtension(extension);
     }

    public FileExtension FromPath(string fullPath) {
        return new FilePath(Path.GetExtension(fullPath);
     }
}

public class SomeClassThatUsesTheFactory {

    public void DoSomethingWithFileExtension(string filePath) {
        var extension = FileExtensionFactory.FromPath(filePath);
        // do something with the extension
    }
}

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

.

Проблема: Вам необходимо создать экземпляры семейства связанных объектов, которые подключаются к вашей системе.

Решение: Использовать абстрактный шаблон фабричного дизайна.

Пример: Вы создаете ORM и хотите поддерживать различные типы баз данных. Вы хотите определить интерфейс для соединений с базой данных, который будет использовать ваш код ORM. Позже вы захотите настроить ORM с разными типами соединений, чтобы вы могли подключаться к разным базам данных. Вы хотите иметь возможность добавить поддержку большего количества баз данных в ваш ORM.

public interface IDbCommand : IDiposable {
   string CommandText { get; set; }
   void Execute();
}

public interface IDbConnection : IDisposable {

   void Open();
   void Close();

   IDbCommand CreateCommand();
   IDbTransaction BeginTransaction();

   // other stuff
}

public interface IDbConnectionFactory {   
   IDbConnection CreateConnection();
}

public class MySqlCommand : IDbCommand { }
public class MySqlConnection : IDbConnection { }
public class MySqlConnectionFactory : IDbConnectionFactory { }

public class PostgreCommand : IDbCommand { }
public class PostgreConnection : IDbConnection { }
public class PostgreSqlConnectionFactory : IDbConnectionFactory { }

public class OrmSession {

   public OrmSession(IDbConnectionFactory) { }
}

Здесь мы видим принцип Open-Closed в действии. Вы ORM будете использовать абстрактную фабрику, чтобы вы могли предоставить конкретную фабрику. Вы можете сделать это из своего кода, использовать файл конфигурации или использовать ServiceLocator или DI для внедрения конкретного завода.

Заметьте также кое-что интересное здесь. IDbConnection также предоставляет CreateCommand FactoryMethod . Каждое соединение должно создавать конкретную команду, поэтому он предоставляет метод для этого, даже если это не чисто фабрика.

Есть и другие ситуации, в которых вы можете использовать фабрики. Тестирование если один из них. Возможно, вам потребуется предоставить Abstract Factory, чтобы вы могли сделать тестируемые части вашего приложения.

В заключение, шаблон Factory имеет много применений. Они могут варьироваться от простого повышения чистоты и выразительности кода до достижения принципа Open-Closed. Движущей силой здесь является проблема, которая у вас есть. У вас проблемы с созданием объекта. Может ли фабрика решить вашу проблему?

Не всегда. Иногда создание объекта настолько сложно и имеет много разных вариаций, что Фабрика не помогает и может запутать вещи. Иногда вам нужен один или два конструктора.

В случае создания сложных возражений вы можете использовать BuilderPattern с или без FluentInterface .

...