Позвольте мне предвосхитить это, сказав, что это не мой код или код моих коллег. Несколько лет назад, когда наша компания была меньше, у нас было несколько проектов, в которых мы нуждались, которых у нас не было, поэтому они были переданы на аутсорсинг. Теперь я ничего не имею против аутсорсинга или подрядчиков в целом, но кодовая база, которую они создали, - это масса WTF. При этом, это (в основном) работает, так что я полагаю, что он входит в топ-10% аутсорсинговых проектов, которые я видел.
По мере того, как наша компания росла, мы старались больше развивать свою деятельность. Этот конкретный проект оказался у меня на коленях, так что я перебрал его, очистил его, добавил тесты и т. Д. И т. Д.
Есть один шаблон, который я часто повторяю, и он кажется настолько ошеломляющим, что я подумал, может быть, есть причина, и я просто ее не вижу. Шаблон - это объект без открытых методов или членов, просто открытый конструктор, который выполняет всю работу над объектом.
Например, (код на Java, если это имеет значение, но я надеюсь, что это будет более общий вопрос):
public class Foo {
private int bar;
private String baz;
public Foo(File f) {
execute(f);
}
private void execute(File f) {
// FTP the file to some hardcoded location,
// or parse the file and commit to the database, or whatever
}
}
Если вам интересно, этот тип кода часто вызывается следующим образом:
for(File f : someListOfFiles) {
new Foo(f);
}
Теперь меня давно учили, что создание объектов в цикле, как правило, плохая идея, и что конструкторы должны выполнять минимум работы. Глядя на этот код, кажется, что было бы лучше отказаться от конструктора и создать execute
открытый статический метод.
Я спросил подрядчика, почему это было сделано таким образом, и я получил ответ «Мы можем изменить его, если хотите». Что было не очень полезно.
В любом случае, есть ли причина делать что-то подобное на любом языке программирования, или это просто очередная подача в Daily WTF?
public static void main(string[] args)
объектах и слышали о них, а затем пытались их объединить.Ответы:
Хорошо, идем вниз по списку:
Меня давно учили, что создание экземпляров объектов в цикле вообще плохая идея
Ни на каких языках я не пользовался.
В C это хорошая идея объявить ваши переменные заранее, но это отличается от того, что вы сказали. Это может быть немного быстрее, если вы объявляете объекты над циклом и используете их повторно, но существует множество языков, в которых это увеличение скорости будет бессмысленным (и, вероятно, некоторые компиляторы, которые выполняют оптимизацию для вас :)).
В общем, если вам нужен объект внутри цикла, создайте его.
Конструкторы должны сделать минимум работы
Конструкторы должны создавать экземпляры полей объекта и выполнять любую другую инициализацию, необходимую для готовности объекта к использованию. Обычно это означает, что конструкторы небольшие, но есть сценарии, в которых это будет значительный объем работы.
есть ли причина делать что-то подобное на любом языке программирования, или это просто очередная подача в Daily WTF?
Это представление в Daily WTF. Конечно, есть худшие вещи, которые вы можете сделать с кодом. Проблема в том, что у автора есть огромное недопонимание, что такое классы и как их использовать. В частности, вот что я вижу, что не так с этим кодом:
источник
Для меня это немного удивительно, когда вы получаете объект, выполняющий много работы в конструкторе, так что я бы рекомендовал это (принцип наименьшего удивления).
Попытка хорошего примера :
Я не уверен, является ли это использование анти-паттерном, но в C # я видел код, который был примерно таким (пример несколько вымышленный):
В этом случае
ImpersonationContext
класс выполняет всю свою работу в конструкторе и методе Dispose. Он использует преимуществоusing
оператора C # , который довольно хорошо понимается разработчиками C #, и, таким образом, гарантирует, что настройка, которая происходит в конструкторе, откатывается, как только мы находимся внеusing
оператора, даже если возникает исключение. Это, вероятно, лучшее использование, которое я видел для этого (то есть «работа» в конструкторе), хотя я все еще не уверен, что считаю это «хорошим». В этом случае это довольно очевидно, функционально и лаконично.Примером хорошего и похожего шаблона может служить шаблон команды . Вы определенно не выполняете логику в конструкторе, но она похожа в том смысле, что весь класс эквивалентен вызову метода (включая параметры, необходимые для вызова метода). Таким образом, информация о вызове метода может быть сериализована или передана для последующего использования, что очень полезно. Возможно, ваши подрядчики пытались сделать что-то подобное, хотя я в этом сильно сомневаюсь.
Комментарии к вашему примеру:
Я бы сказал, что это очень определенный анти-паттерн. Он ничего не добавляет, идет вразрез с ожидаемым и выполняет чрезмерно длительную обработку в конструкторе (FTP в конструкторе? Действительно, WTF, хотя я полагаю, что люди, как известно, вызывали веб-службы таким способом).
Я изо всех сил пытаюсь найти цитату, но в книге Грэди Буча «Object Solutions» есть анекдот о команде разработчиков C, переходящих на C ++. По-видимому, они прошли обучение, и руководство заявило им, что они должны делать что-то «объектно-ориентированное». Чтобы выяснить, что не так, автор использовал инструмент метрики кода и обнаружил, что среднее число методов в классе равняется ровно 1, и являются вариациями фразы «делай это». Должен сказать, что раньше я никогда не сталкивался с этой конкретной проблемой, но, очевидно, это может быть признаком того, что люди вынуждены создавать объектно-ориентированный код, не понимая, как это сделать и почему.
источник
Нет.
Если вся работа выполняется в конструкторе, это означает, что объект никогда не был нужен в первую очередь. Скорее всего, подрядчик просто использовал объект для модульности. В этом случае класс без создания экземпляра со статическим методом получил бы такое же преимущество, не создавая лишних экземпляров объекта.
Существует только один случай, когда выполнение всей работы в конструкторе объектов является приемлемым. Именно тогда целью объекта является именно представление долгосрочной уникальной личности, а не выполнение работы. В таком случае объект будет храниться по причинам, отличным от выполнения значимой работы. Например, одноэлементный экземпляр.
источник
Я, конечно, создал много классов, которые проделывают большую работу для вычисления чего-либо в конструкторе, но объект является неизменным, поэтому он делает результаты этих вычислений доступными через свойства, а затем никогда больше ничего не делает. Это нормальная неизменность в действии.
Я думаю, что странная вещь здесь - поместить код с побочными эффектами в конструктор. Создание объекта и его отбрасывание без доступа к свойствам или методам выглядит странно (но, по крайней мере, в этом случае довольно очевидно, что происходит что-то еще).
Это было бы лучше, если бы функция была перемещена в открытый метод, а затем внедрился экземпляр класса, а затем несколько раз вызывал бы метод цикла.
источник
Нет.
Можно разумно поставить под сомнение эти рекомендации и сказать: «Но я не следую этим правилам, и мой код работает нормально!» На это я бы ответил: «Ну, это может быть правдой, пока это не так».
источник
Мне кажется, что человек, делающий это, пытался взломать ограничение Java, которое не позволяет передавать функции как первоклассные граждане. Всякий раз, когда вам нужно передать функцию, вы должны обернуть ее внутри класса с помощью метода, подобного
apply
или подобного.Так что, я думаю, он просто использовал ярлык и использовал класс непосредственно для замены функции. Всякий раз, когда вы хотите выполнить функцию, вы просто создаете экземпляр класса.
Это определенно не очень хороший шаблон, по крайней мере, потому что мы здесь пытаемся понять это, но я думаю, что это может быть наименее подробный способ сделать что-то похожее на функциональное программирование в Java.
источник
Для меня эмпирическое правило - ничего не делать в конструкторе, кроме инициализации переменных-членов. Первая причина этого заключается в том, что это помогает следовать принципу SRP, поскольку обычно длительный процесс инициализации является показателем того, что класс делает больше, чем должен, и инициализация должна выполняться где-то за пределами класса. Вторая причина в том, что таким образом передаются только необходимые параметры, поэтому вы создаете менее связанный код. Конструктор со сложной инициализацией обычно нуждается в параметрах, которые используются только для создания другого объекта, но которые не используются исходным классом.
источник
Я собираюсь пойти против течения здесь - в языках, где все должно быть в каком-то классе, И у вас не может быть статического класса, вы можете столкнуться с ситуацией, когда у вас есть отдельные функциональные возможности, которые должны быть положить куда-нибудь и не подходит ни к чему другому. Ваш выбор - это служебный класс, который охватывает различные несвязанные элементы функциональности, или класс, который делает в основном только это.
Если у вас есть только один такой бит, то ваш статический класс - это ваш конкретный класс. Итак, у вас есть конструктор, который выполняет всю работу, или одна функция, которая выполняет всю работу. Преимущество выполнения всего в конструкторе состоит в том, что это происходит только один раз, это позволяет вам использовать закрытые поля, свойства и методы, не беспокоясь о том, что они могут быть повторно использованы или поточны. Если у вас есть конструктор / метод, у вас может возникнуть желание разрешить передачу параметров одному методу, но если вы используете частные поля или свойства, которые могут вызвать проблемы с многопоточностью.
Даже с помощью вспомогательного класса большинство людей не будут думать, что имееют вложенные статические классы (и это может быть невозможным в зависимости от языка).
По сути, это относительно понятный способ изолировать поведение от остальной системы в пределах того, что разрешено языком, и в то же время позволяет вам использовать в своих интересах как можно большую часть языка.
Честно говоря, я бы ответил так же, как и ваш подрядчик, это небольшая корректировка, чтобы превратить его в два звонка, не так уж много, чтобы рекомендовать один над другим. Какие дисбалансы существуют, вероятно, скорее гипотетически, чем актуально, зачем пытаться обосновать решение, когда оно не имеет значения и, вероятно, не так решено, как просто действие по умолчанию?
источник
Существуют шаблоны, распространенные в языках, где функции и классы во многом похожи, как в Python, где каждый использует объект в основном для функции со слишком большим количеством побочных эффектов или с возвращением нескольких именованных объектов.
В этом случае основная часть, если не вся работа, может быть выполнена в конструкторе, поскольку сам объект является просто оберткой вокруг одного пакета работ, и нет веских оснований для помещения его в отдельную функцию. Что-то вроде
действительно не лучше, чем
Вместо того, чтобы прямо указывать на побочные эффекты, вы можете вызвать объект, чтобы вызвать побочный эффект на реплике, что обеспечит вам лучшую видимость. Если у меня будет плохой побочный эффект на объект, я могу выполнить всю свою работу, а затем пройти мимо объекта, на который я плохо воздействую, и нанести ему урон. Идентификация объекта и изоляция вызова таким способом более понятны, чем одновременная передача нескольких таких объектов в функцию.
Вы можете сделать несколько возвращаемых значений через методы доступа к объекту. Вся работа выполнена, но я хочу, чтобы все было в контексте, и я не хочу передавать несколько ссылок в мою функцию.
Так что, как совместный программист на Python / C #, это не кажется мне таким уж странным.
источник
На ум приходит один случай, когда конструктор / деструктор выполняет всю работу:
Замки. Обычно вы никогда не взаимодействуете с замком в течение его жизни. Использование архитектуры C # хорошо для обработки таких случаев без явной ссылки на деструктор. Однако не все блокировки реализованы таким образом.
Я также написал то, что составляло часть кода только для конструктора, чтобы исправить ошибку библиотеки времени выполнения. (На самом деле исправление кода вызвало бы другие проблемы.) Деструктора не было, потому что не было необходимости отменять исправление.
источник
Я согласен с AndyBursh. Кажется, нехватка знаний вопроса.
Однако важно упомянуть фреймворки, такие как NHibernate, которые требуют только кодирования в конструкторе (при создании классов карты). Однако это не ограничение фреймворка, это то, что вам нужно. Когда дело доходит до рефлексии, конструктор - это единственный метод, который всегда будет существовать (хотя он может быть закрытым), и это может быть полезно, если вам нужно вызывать метод класса динамически.
источник
Да, возможно, вы вспоминаете, зачем нам нужен StringBuilder.
Есть две школы Java.
Создатель был продвинут первым , который учит нас повторному использованию (потому что разделение = идентичность для эффективности). Тем не менее, в начале 2000 года я начал читать, что создание объектов в Java настолько дешево (в отличие от C ++), а GC настолько эффективен, что повторное использование бесполезно, и единственное, что имеет значение, - это производительность программиста. Для повышения производительности он должен написать управляемый код, что означает модульность (то есть сужение области видимости). Так,
это плохо
это хорошо.
Я задал этот вопрос, когда forum.java.sun существовал, и люди согласились, что они предпочли бы объявить массив как можно более узким.
Современный Java-программист без колебаний объявляет новый класс или создает объект. Ему рекомендуется это сделать. Java дает все основания для этого, с ее идеей, что «все является объектом» и дешевым творением.
Кроме того, современный стиль программирования предприятия заключается в том, что, когда вам нужно выполнить действие, вы создаете специальный класс (или, что еще лучше, фреймворк), который будет выполнять это простое действие. Хорошие Java IDE, которые помогают здесь. Они генерируют тонны дерьма автоматически, как дыхание.
Итак, я думаю, что вашим объектам не хватает шаблона Builder или Factory. Нельзя создавать экземпляры напрямую конструктором. Рекомендуется специальный класс фабрики для каждого вашего объекта.
Теперь, когда функциональное программирование вступает в игру, создание объектов становится еще проще. Вы будете создавать объекты на каждом шагу как дыхание, даже не замечая этого. Итак, я ожидаю, что ваш код станет еще более «эффективным» в новую эру
Лично я не вижу причин в руководстве. Я считаю это просто еще одним методом. Факторинг кода необходим только тогда, когда вы можете поделиться им.
источник