В некоторых моих кодах у меня есть статическая фабрика, похожая на эту:
public class SomeFactory {
// Static class
private SomeFactory() {...}
public static Foo createFoo() {...}
public static Foo createFooerFoo() {...}
}
Во время обзора кода было предложено, чтобы это был одиночный и введенный. Итак, это должно выглядеть так:
public class SomeFactory {
public SomeFactory() {}
public Foo createFoo() {...}
public Foo createFooerFoo() {...}
}
Несколько вещей, чтобы выделить:
- Обе фабрики не имеют гражданства.
- Единственное различие между методами - это их область действия (экземпляр против статического). Реализации одинаковы.
- Foo - это боб, у которого нет интерфейса.
Аргументы в пользу того, что я стою, были:
- Класс не имеет состояния, поэтому его не нужно создавать
- Кажется более естественным иметь возможность вызывать статический метод, чем создавать экземпляр фабрики
Аргументы для фабрики как синглтона были:
- Хорошо впрыскивать все
- Несмотря на заводское безгражданство, тестирование легче с помощью инъекций (легко издеваться)
- Надо издеваться при тестировании потребителя
У меня есть некоторые серьезные проблемы с одноэлементным подходом, поскольку кажется, что никакие методы никогда не должны быть статичными. Также кажется, что такие утилиты, как, StringUtils
следует обернуть и ввести, что кажется глупым. Наконец, это означает, что мне нужно будет издеваться над фабрикой в какой-то момент, что кажется неправильным. Я не могу вспомнить, когда мне нужно издеваться над фабрикой.
Что думает сообщество? Хотя мне не нравится одноэлементный подход, у меня, похоже, нет слишком сильного аргумента против этого.
DateTime
andFile
сложно тестировать по тем же причинам. Если у вас есть класс, например, который устанавливаетCreated
датуDateTime.Now
в конструкторе, как вы собираетесь создавать модульный тест с двумя из этих объектов, которые были созданы с интервалом в 5 минут? А как насчет лет? Вы действительно не можете сделать это (без большой работы).private
конструктора иgetInstance()
метода? Извините, неисправимая гнилька!Ответы:
Почему вы отделяете свою фабрику от создаваемого ею типа объекта?
Это то, что Джошуа Блох описывает как свой пункт 1 на странице 5 своей книги «Эффективная Ява». Java достаточно многословен без добавления дополнительных классов и одиночных игр и еще много чего. Есть некоторые случаи, когда отдельный класс фабрики имеет смысл, но они немногочисленны.
Перефразируя пункт 1 Джошуа Блоха, в отличие от конструкторов, статические фабричные методы могут:
Недостатки:
На самом деле, вы должны отнести книгу Блоха своему рецензенту кода и посмотреть, сможете ли вы двое согласиться со стилем Блоха.
источник
public static IFoo of(...) { return new Foo(...); }
чем проблема? Bloch-списки удовлетворяют вашу озабоченность как одно из преимуществ этого дизайна (второй пункт выше). Я не должен понимать ваш комментарий.Вот только некоторые проблемы со статикой в Java:
статические методы не играют по "правилам" ООП. Вы не можете заставить класс реализовать определенные статические методы (насколько я знаю). Таким образом, вы не можете иметь несколько классов, реализующих один и тот же набор статических методов с одинаковыми сигнатурами. Таким образом, вы застряли с одним из всего, что вы делаете. Вы также не можете создать подкласс статического класса. Так что нет полиморфизма и т. Д.
так как методы не являются объектами, статические методы нельзя передавать и их нельзя использовать (без тонны шаблонного кодирования), кроме как с помощью кода, который жестко связан с ними - что делает клиентский код чрезвычайно в сочетании. (Честно говоря, это не только вина статики).
статические поля очень похожи на глобальные
Ты упомянул:
Что заставляет меня любопытно спросить вас:
StringUtils
правильно разработан и реализован?источник
Foo f = createFoo();
а не иметь именованный экземпляр. Сам экземпляр не предоставляет никакой полезности. (2) Я так думаю. Он был разработан, чтобы преодолеть тот факт, что многие из этих методов не существуют вString
классе. Замена чего-то столь же фундаментального, какString
не вариант, так что утилиты - это путь. (продолжение)Integer
. Они очень просты, имеют очень мало логики и предназначены только для обеспечения удобства (например, нет другой ОО-причины иметь их). Основная часть моего аргумента заключается в том, что они не полагаются на состояние - нет причин изолировать их во время тестов. Люди не издеваютсяStringUtils
при тестировании - зачем им издеваться над этой простой фабрикой удобства?StringUtils
потому что есть разумная уверенность в том, что методы не имеют побочных эффектов.StringUtils
возвращает некоторый результат без изменения входных данных, моя фабрика также ничего не меняет.Я думаю, что приложение, которое вы создаете, должно быть довольно простым, иначе вы относительно рано в своем жизненном цикле. Единственный другой сценарий, который я могу придумать, когда у вас не было бы проблем с вашей статикой, это если вам удалось ограничить другие части вашего кода, которые знают о вашей Фабрике, одним или двумя местами.
Представьте, что ваше приложение развивается, и теперь в некоторых случаях вам нужно создать
FooBar
(подкласс Foo). Теперь вам нужно пройтись по всем местам в вашем коде, которые знают о вас,SomeFactory
и написать некую логику, которая просматриваетsomeCondition
и вызываетSomeFactory
илиSomeOtherFactory
. На самом деле, глядя на ваш пример кода, это хуже, чем это. Вы планируете просто добавить метод за методом для создания различных Foos, и весь ваш клиентский код должен проверить условие, прежде чем выяснить, какой Foo сделать. Это означает, что все клиенты должны иметь глубокие знания о том, как работает ваша Фабрика, и все они должны меняться всякий раз, когда новый Foo необходим (или удален!).Теперь представьте, если вы только что создали,
IFooFactory
что делаетIFoo
. Теперь создание другого подкласса или даже совершенно другой реализации - это детская игра - вы просто вводите правильный IFooFactory выше по цепочке, а затем, когда клиент получает его, он вызываетgimmeAFoo()
и получает правильный foo, потому что он получил правильную Factory ,Вы можете быть удивлены, как это работает в производственном коде. Чтобы дать вам один пример из кодовой базы, над которой я работаю, которая начинает выравниваться после чуть более года запуска проектов, у меня есть несколько фабрик, которые используются для создания объектов данных Question (они что-то вроде презентационных моделей). ). У меня есть
QuestionFactoryMap
хеш для поиска того, какую фабрику вопросов использовать, когда. Поэтому, чтобы создать приложение, которое задает разные вопросы, я поместил на карту разные фабрики.Вопросы могут быть представлены либо в Инструкции, либо в Упражнениях (которые оба реализуют
IContentBlock
), поэтому каждый из нихInstructionsFactory
илиExerciseFactory
получит копию QuestionFactoryMap. Затем различные фабрики с инструкциями и упражнениями передаются на тот,ContentBlockBuilder
который снова содержит хэш, который нужно использовать, когда. И затем, когда XML-файл загружен, ContentBlockBuilder вызывает фабрику упражнений или инструкций из хэша и запрашивает егоcreateContent()
, а затем Factory делегирует построение вопросов тому, что он извлекает из своей внутренней QuestionFactoryMap, на основе того, что находится в каждом узле XML против хеша. К сожалению , эта вещьBaseQuestion
вместоIQuestion
, но это просто говорит о том, что даже когда вы осторожны с дизайном, вы можете совершать ошибки, которые могут преследовать вас.Ваш инстинкт говорит вам, что эти статические травмы причинят вам боль, и в литературе , безусловно , есть много того, что поддерживает вашу интуицию. Вы никогда не потеряете, используя Dependency Injection, которое вы используете только для своих модульных тестов (не то, чтобы я полагал, что если вы создадите хороший код, вы не найдете его более), но статика может полностью разрушить ваши способности быстро и легко изменить ваш код. Так зачем вообще туда ехать?
источник
Integer
классе - простых вещах, таких как преобразование из строки. Вот чтоFoo
делает. МойFoo
не предназначен для расширения (моя вина в том, что я этого не сказал), поэтому у меня нет ваших полиморфных проблем. Я понимаю ценность наличия полиморфных фабрик и использовал их в прошлом ... Я просто не думаю, что они здесь уместны. Можете ли вы представить себе, нужно ли создавать экземпляр фабрики для выполнения простых операцийInteger
, которые в настоящее время запекаются через статические фабрики?Foo
, однако, намного проще, чем многие классы. Это просто простой POJO.if (this) then {makeThisFoo()} else {makeThatFoo()}
весь код. Одна вещь, которую я заметил в людях с хронически плохой архитектурой, это то, что они похожи на людей с хронической болью. Они на самом деле не знают, каково это - не чувствовать боли, поэтому боль, в которой они находятся, не кажется такой уж плохой.Foo
. По праву так - я никогда не объявлял это окончательным.Foo
это боб, который не предназначен для расширения.