Должны ли фабрики устанавливать свойства модели?
В рамках общего SOLID усилия программирования я создал интерфейс factory и абстрактный factory в базовом API-интерфейсе.
Люди уже начинают перегружать фабрики Create method. Проблема в том, что люди перегружают метод Create с помощью свойств модели (и тем самым ожидают, что factory заполнит их).
По-моему, настройка свойств не должна выполняться с помощью factory. Я не прав?
public interface IFactory
{
I Create<C, I>();
I Create<C, I>(long id); //<--- I feel doing this is incorrect
IFactoryTransformer Transformer { get; }
IFactoryDataAccessor DataAccessor { get; }
IFactoryValidator Validator { get; }
}
ОБНОВЛЕНИЕ. Для тех, кто не знаком с принципами SOLID, вот несколько из них:
Принцип единой ответственности
В нем говорится, что каждый объект должен иметь одну ответственность и что ответственность должна быть полностью инкапсулирована классом
Открытый/Закрытый Принцип
Смысл этого принципа заключается в том, что когда вы получаете запрос на функцию, которая должна быть добавлена в ваше приложение, вы должны иметь возможность обрабатывать ее без изменения старых классов, только добавляя подклассы и новые реализации.
Принцип инверсии зависимостей
В нем говорится, что вы должны разделить свои программные модули. Чтобы достичь этого, вам нужно изолировать зависимости.
В целом:
Я на 90% уверен, что знаю ответ. Тем не менее, я бы хотел, чтобы хорошие обсуждения у людей, уже использующих SOLID. Спасибо за ваши ценные мнения.
ОБНОВЛЕНИЕ. Итак, что я должен сделать, чтобы SOLID factory должен был делать?
IMHO sOLID factory обслуживает соответствующие объекты-экземпляры... но делает это таким образом, который скрывает сложность создания объекта. Например, если у вас есть модель Employee... вы бы попросили factory предоставить вам подходящую. DataAccessorFactory предоставит вам правильный объект доступа к данным, ValidatorFactory предоставит вам правильный объект проверки и т.д.
Например:
var employee = Factory.Create<ExxonMobilEmployee, IEmployee>();
var dataAccessorLdap = Factory.DataAccessor.Create<LDAP, IEmployee>();
var dataAccessorSqlServer = Factory.DataAccessor.Create<SqlServer, IEmployee>();
var validator = Factory.Validator.Create<ExxonMobilEmployee, IEmployee>();
Взяв пример далее, мы бы...
var audit = new Framework.Audit(); // Or have the factory hand it to you
var result = new Framework.Result(); // Or have the factory hand it to you
// Save your AuditInfo
audit.username = 'prisonerzero';
// Get from LDAP (example only)
employee.Id = 10;
result = dataAccessorLdap.Get(employee, audit);
employee = result.Instance; // All operations use the same Result object
// Update model
employee.FirstName = 'Scooby'
employee.LastName = 'Doo'
// Validate
result = validator.Validate(employee);
// Save to SQL
if(result.HasErrors)
dataAccessorSqlServer.Add(employee, audit);
ОБНОВЛЕНИЕ - Так почему я категорически против этого разделения?
Я чувствую, что разделяющие обязанности делают для меньших объектов, меньшие Unit Tests и повышают надежность и обслуживание. Я признаю, что он делает это за счет создания большего количества объектов... но это то, что защищает меня SOLID factory... он скрывает сложность сбора и создания экземпляров указанных объектов.
Ответы
Ответ 1
Я бы сказал, что он придерживается принципа DRY, и до тех пор, пока он прост в использовании, я не вижу, что это проблема/нарушение, Вместо того, чтобы
var model = this.factory.Create();
model.Id = 10;
model.Name = "X20";
разбросаны по всей вашей базе кода, почти всегда лучше иметь его в одном месте. Будущие изменения контракта, рефакторинг или новые требования будут намного проще обрабатывать.
Стоит отметить, что если такое создание объекта, а затем сразу настройка свойств является общей, то, что шаблон, который ваша команда развила, а разработчики, добавляющие перегрузки, являются только ответом на этот факт (особенно хороший). Представление API для упрощения этого процесса - вот что нужно сделать.
И снова, если он сужается до простых назначений (например, в вашем примере), я бы без колебаний сохранил перегрузки, особенно если это то, что вы часто замечаете. Когда ситуация усложняется, это станет признаком обнаружения новых шаблонов и, возможно, тогда вы должны прибегнуть к другим стандартным решениям (например, шаблон построителя).
Ответ 2
Предполагая, что ваш интерфейс factory используется из кода приложения (в отличие от инфраструктурного корня композиции), он фактически представляет собой локатор сервисов, который можно рассматривать как анти-шаблон по отношению к инъекции зависимостей. См. Также Сервис-локатор: роли против механики.
Обратите внимание, что код выглядит следующим образом:
var employee = Factory.Create<ExxonMobilEmployee, IEmployee>();
- это просто синтаксический сахар. Он не устраняет зависимость от конкретной реализации ExxonMobilEmployee
.
Вы также можете быть заинтересованы в Слабо типизированные или сильно типизированные каналы сообщений и Сообщение диспетчеризации без местоположения службы (это иллюстрирует, как такие интерфейсы нарушают SRP) и другие публикации Марка Семана.
Ответ 3
Примерно через 6 месяцев опыта в Injection Dependency я обнаружил лишь несколько случаев, когда фабрики должны устанавливать свойства:
-
Если установщик отмечен как internal
, и ожидается, что свойства будут установлены один раз только с помощью factory. Обычно это происходит на интерфейсах с только свойствами get
ter, реализация которых должна быть создана через factory.
-
Когда модель использует инъекцию свойств. Я редко вижу классы, которые используют инъекцию свойств (я также стараюсь избегать их создания), но когда я вижу его, и необходимый сервис доступен только в другом месте, это случай, когда у вас нет выбора.
В нижней строке оставьте public set
от фабрик. Только установите свойства, отмеченные как internal
. Пусть клиенты решат, какие свойства им нужно установить, если им разрешено это делать. Это позволит очистить ваши заводы от ненужных функций.