Я занимаюсь своими делами дома, и моя жена приходит ко мне и говорит
Дорогая .. Можешь ли ты распечатать все консоли Day Light Savings по всему миру на 2018 год в консоли? Мне нужно кое-что проверить.
И я очень счастлив, потому что именно этого я ждал всю свою жизнь с опытом Java и придумал:
import java.time.*;
import java.util.Set;
class App {
void dayLightSavings() {
final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
availableZoneIds.forEach(
zoneId -> {
LocalDateTime dateTime = LocalDateTime.of(
LocalDate.of(2018, 1, 1),
LocalTime.of(0, 0, 0)
);
ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
while (2018 == now.getYear()) {
int hour = now.getHour();
now = now.plusHours(1);
if (now.getHour() == hour) {
System.out.println(now);
}
}
}
);
}
}
Но затем она говорит, что просто проверяет меня, был ли я инженером-программистом с этической точки зрения, и говорит, что, похоже, я не с тех пор (взято отсюда ) ..
Следует отметить, что ни один инженер, прошедший этическую подготовку, никогда не согласится написать процедуру DestroyBaghdad. Вместо этого базовая профессиональная этика потребовала бы от него написания процедуры DestroyCity, которой Багдад мог бы быть указан в качестве параметра.
И я, как, хорошо, хорошо, вы меня поймали. Пройдите любой год, когда захотите, вот и вы:
import java.time.*;
import java.util.Set;
class App {
void dayLightSavings(int year) {
final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
availableZoneIds.forEach(
zoneId -> {
LocalDateTime dateTime = LocalDateTime.of(
LocalDate.of(year, 1, 1),
LocalTime.of(0, 0, 0)
);
ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
while (year == now.getYear()) {
// rest is same..
Но как мне узнать, сколько (и что) параметризировать? В конце концов, она могла бы сказать ..
- она хочет передать пользовательский форматировщик строки, возможно, ей не нравится формат, в котором я уже печатаю:
2018-10-28T02:00+01:00[Arctic/Longyearbyen]
void dayLightSavings(int year, DateTimeFormatter dtf)
- она интересуется только определенными месячными периодами
void dayLightSavings(int year, DateTimeFormatter dtf, int monthStart, int monthEnd)
- она интересуется определенными часовыми периодами
void dayLightSavings(int year, DateTimeFormatter dtf, int monthStart, int monthEnd, int hourStart, int hourend)
Если вы ищете конкретный вопрос:
Если destroyCity(City city)
лучше чем destroyBaghdad()
, то takeActionOnCity(Action action, City city)
еще лучше? Почему, почему нет?
В конце концов, я могу назвать первым его Action.DESTROY
тогда Action.REBUILD
, не так ли?
Но для меня недостаточно действий в городах, как насчет takeActionOnGeographicArea(Action action, GeographicalArea GeographicalArea)
? Ведь я не хочу звонить
takeActionOnCity(Action.DESTORY, City.BAGHDAD);
тогда
takeActionOnCity(Action.DESTORY, City.ERBIL);
и так далее, когда я могу сделать:
takeActionOnGeographicArea(Action.DESTORY, Country.IRAQ);
ps я только построил свой вопрос вокруг цитаты, которую я упомянул, я ничего не имею против любой страны, религии, расы или чего-либо в мире. Я просто пытаюсь сделать точку.
источник
destroyCity(target)
это более неэтично, чемdestroyBagdad()
! Какой монстр пишет программу по уничтожению города, не говоря уже о любом городе в мире? Что если система была взломана ?! Кроме того, какое отношение время / ресурсы (вложенные усилия) имеют к этике? Пока устный / письменный договор был завершен в соответствии с согласованным.Ответы:
Это черепахи все время вниз.
Или абстракции в этом случае.
Хорошая практика кодирования - это то, что может применяться бесконечно, и в какой-то момент вы абстрагируетесь ради абстрагирования, что означает, что вы зашли слишком далеко. Найти эту строку не так-то просто, чтобы ее можно было включить в эмпирическое правило, поскольку она очень сильно зависит от вашей среды.
Например, у нас были клиенты, которые, как известно, сначала запрашивали простые приложения, а затем просили расширения. У нас также были клиенты, которые спрашивали, чего они хотят, и вообще никогда не возвращались к нам для расширения.
Ваш подход зависит от клиента. Первому клиенту придется заплатить за упреждающий абстрагирование кода, потому что вы достаточно уверены, что вам придется пересматривать этот код в будущем. Для второго клиента вы, возможно, не захотите вкладывать эти дополнительные усилия, если ожидаете, что они не захотят расширять приложение в любой момент (примечание: это не означает, что вы не следуете какой-либо хорошей практике, но просто что вы избегаете делать больше, чем необходимо в настоящее время .
Как я знаю, какие функции реализовать?
Причина, по которой я упоминаю выше, состоит в том, что вы уже попали в эту ловушку:
«Она могла бы сказать» не является текущим бизнес-требованием. Это предположение о будущих требованиях бизнеса. Как правило, не основывайтесь на догадках, а только развивайте то, что требуется в настоящее время.
Тем не менее, контекст применяется здесь. Я не знаю твою жену. Может быть, вы точно определили, что она действительно этого захочет. Но вы все равно должны подтвердить заказчику, что это действительно то, что он хочет, потому что в противном случае вы потратите время на разработку функции, которую никогда не собираетесь использовать.
Как я знаю, какую архитектуру реализовать?
Это сложнее. Клиент не заботится о внутреннем коде, поэтому вы не можете спросить его, нужен ли он ему. Их мнение по этому вопросу в основном не имеет значения.
Тем не менее, вы все еще можете подтвердить необходимость сделать это, задавая правильные вопросы клиенту. Вместо того, чтобы спрашивать об архитектуре, спросите их об их ожиданиях относительно будущего развития или расширения базы кода. Вы также можете спросить, есть ли у текущей цели крайний срок, потому что вы не сможете реализовать свою причудливую архитектуру в необходимые сроки.
Как я знаю, когда абстрагировать мой код дальше?
Я не знаю, где я читаю это (если кто-нибудь знает, дайте мне знать, и я дам кредит), но хорошее эмпирическое правило заключается в том, что разработчики должны считать как пещерный человек: один, два, много .
XKCD # 764
Другими словами, когда определенный алгоритм / шаблон используется в третий раз, его следует абстрагировать, чтобы его можно было многократно использовать (= можно использовать много раз).
Просто для ясности, я не имею в виду, что вы не должны писать повторно используемый код, когда используется только два экземпляра алгоритма. Конечно, вы также можете абстрагироваться, но правило должно состоять в том, что для трех случаев вы должны абстрагироваться.
Опять же, это влияет на ваши ожидания. Если вы уже знаете, что вам нужно три или более экземпляров, конечно, вы можете сразу же абстрагироваться. Но если вы только предполагаете, что вам захочется реализовать это больше раз, правильность реализации абстракции полностью зависит от правильности вашего предположения.
Если вы правильно догадались, вы сэкономили себе время. Если вы догадались, вы потратили немного времени и усилий и, возможно, скомпрометировали свою архитектуру, чтобы реализовать то, что вам в итоге не нужно.
Это очень зависит от нескольких вещей:
takeActionOnCity
методе.Также имейте в виду, что если вы рекурсивно абстрагируете это, вы в конечном итоге получите метод, который настолько абстрактен, что это не что иное, как контейнер для запуска другого метода, что означает, что вы сделали свой метод нерелевантным и бессмысленным.
Если все
takeActionOnCity(Action action, City city)
тело вашего метода в конечном итоге является не чем инымaction.TakeOn(city);
, вы должны задаться вопросом,takeActionOnCity
действительно ли у метода есть цель или это не просто дополнительный слой, который ничего не добавляет.Тот же вопрос всплывает здесь:
Если вы можете окончательно ответить «да» на все три вопроса, тогда абстракция оправдана.
источник
практика
Это Software Engineering SE, но создание программного обеспечения - это больше искусство, чем разработка. Не существует универсального алгоритма, который можно использовать для измерения, чтобы определить, насколько многократного использования достаточно. Как и во всем, чем больше вы будете практиковаться в разработке программ, тем лучше вы будете в этом разбираться. Вы получите лучшее представление о том, что «достаточно», потому что вы увидите, что идет не так и как это происходит, когда вы параметрируете слишком много или слишком мало.
Это не очень полезно , в настоящее время , хотя, так как о некоторых руководящих принципах?
Оглянись на свой вопрос. Там много "она могла бы сказать" и "я мог". Много утверждений, теоретизирующих о некоторой будущей потребности. Люди дерьмо предсказывают будущее. А ты (скорее всего) человек. Подавляющая проблема разработки программного обеспечения - попытаться объяснить будущее, которого вы не знаете.
Принцип 1: Вам это не нужно
Шутки в сторону. Просто перестань. Чаще всего эта воображаемая проблема будущего не проявляется - и, конечно, она не будет отображаться так, как вы ее себе представляли.
Руководящий принцип 2: Стоимость / Выгода
Круто, эта маленькая программа заняла у вас несколько часов, чтобы написать? Так что , если ваша жена действительно вернется и попросить те вещи? В худшем случае, вы тратите еще несколько часов, чтобы собрать другую программу для этого. В этом случае не так уж много времени, чтобы сделать эту программу более гибкой. И это не собирается сильно увеличивать скорость выполнения или использование памяти. Но нетривиальные программы имеют разные ответы. Разные сценарии имеют разные ответы. В какой-то момент затраты явно не стоят выгоды даже при несовершенных навыках рассказывания в будущем.
Руководящий принцип 3: Сосредоточьтесь на константах
Оглянись на вопрос. В вашем исходном коде много постоянных целых.
2018
,1
. Постоянные целые, постоянные строки ... Скорее всего, они должны быть непостоянными . Более того, они параметризуются (или, по крайней мере, определяют как фактические константы) лишь немного времени. Но другой вещью, которой следует опасаться, является постоянное поведение .System.out.println
например. Подобные предположения об использовании имеют тенденцию изменяться в будущем и исправляются очень дорого. Не только это, но и такой ввод-вывод делает функцию нечистой (наряду с извлечением часового пояса). Параметризация такого поведения может сделать функцию более чистой, что приведет к повышению гибкости и тестируемости. Большие преимущества с минимальными затратами (особенно если вы делаете перегрузку, которая используетSystem.out
по умолчанию).источник
Во-первых: ни один разработчик программного обеспечения, ориентированный на безопасность, не написал бы метод DestroyCity без передачи авторизационного токена по любой причине.
Я тоже могу написать что-либо в качестве императива, обладающего очевидной мудростью, но не применимого в другом контексте. Почему необходимо авторизовать конкатенацию строк?
Во-вторых: весь код при выполнении должен быть полностью указан .
Не имеет значения, было ли решение жестко закодировано на месте или перенесено на другой уровень. В какой-то момент есть фрагмент кода на каком-то языке, который знает как то, что должно быть уничтожено, и как его проинструктировать.
Это может быть в том же объектном файле
destroyCity(xyz)
, и это может быть в файле конфигурации:destroy {"city": "XYZ"}"
или это может быть серия щелчков и нажатий клавиш в пользовательском интерфейсе.В-третьих:
это совсем другой набор требований к:
Теперь второй набор требований, очевидно, создает более гибкий инструмент. У него более широкая целевая аудитория и более широкая сфера применения. Опасность здесь заключается в том, что наиболее гибким приложением в мире на самом деле является компилятор для машинного кода. Это буквально такая универсальная программа, что она может создать что угодно, чтобы сделать компьютер таким, каким он вам нужен (в рамках ограничений его аппаратного обеспечения).
Вообще говоря, люди, которым нужно программное обеспечение, не хотят чего-то общего; они хотят что-то конкретное. Предоставляя больше возможностей, вы фактически усложняете их жизнь. Если бы они хотели такой сложности, они бы вместо этого использовали компилятор, а не спрашивали вас.
Ваша жена спрашивала о функциональности и недостаточно указала свои требования к вам. В данном случае это было нарочно, и в целом это потому, что они не знают ничего лучшего. В противном случае они бы просто использовали компилятор. Итак, первая проблема в том, что вы не спрашивали более подробную информацию о том, что именно она хотела сделать. Она хотела запустить это в течение нескольких разных лет? Она хотела это в файле CSV? Вы не узнали, какие решения она хотела бы принять сама, и что она просила вас выяснить и решить за нее. Как только вы выяснили, какие решения необходимо отложить, вы можете выяснить, как передавать эти решения с помощью параметров (и других настраиваемых средств).
При этом большинство клиентов не сообщают, предполагают или не знают определенных деталей (то есть решений), которые они действительно хотели бы сделать сами или которые они действительно не хотели делать (но это звучит потрясающе). Вот почему такие методы работы, как PDSA (планировать-развивать-изучать-действовать), так важны. Вы спланировали работу в соответствии с требованиями, а затем разработали набор решений (код). Теперь пришло время изучить это, самостоятельно или с вашим клиентом и узнать что-то новое, и это поможет вашему мышлению двигаться вперед. Наконец, действуйте в соответствии с вашими новыми идеями - обновите требования, уточните процесс, получите новые инструменты и т. Д. Затем начните планирование снова. Это выявило бы какие-то скрытые требования со временем и доказало бы успехи многих клиентов.
В заключение. Ваше время важно ; это очень реально и очень конечно. Каждое решение, которое вы принимаете, влечет за собой множество других скрытых решений, и в этом суть разработки программного обеспечения. Задержка решения в качестве аргумента может сделать текущую функцию более простой, но она усложняет другое. Это решение актуально в этом другом месте? Это более актуально здесь? Чье это решение на самом деле? Вы решаете это; это кодирование. Если вы часто повторяете наборы решений, есть реальная выгода в их кодификации внутри некоторой абстракции. XKCD имеет полезную перспективу здесь. И это актуально на уровне системы, будь то функция, модуль, программа и т. Д.
Совет с самого начала подразумевает, что решения, которые ваша функция не имеет права принимать, должны передаваться в качестве аргумента. Проблема в том, что
DestroyBaghdad
функция на самом деле может быть той функцией, которая имеет это право.источник
Здесь много длинных ответов, но, честно говоря, я думаю, что это очень просто
так в твоей функции
У вас есть:
Поэтому я бы переместил все это в параметры в той или иной форме. Вы можете утверждать, что zoneIds неявно указаны в имени функции, может быть, вы захотите сделать это еще более, изменив его на DaylightSavingsAroundTheWorld или что-то в этом роде.
У вас нет строки форматирования, поэтому добавление ее - это запрос функции, и вам следует отослать свою жену в экземпляр семейства Jira. Это может быть отложено и расставлено по приоритетам на соответствующем заседании комитета по управлению проектом.
источник
Короче говоря, не проектируйте свое программное обеспечение для повторного использования, потому что конечному пользователю нет дела до возможности повторного использования ваших функций. Вместо этого, инженер для понимания дизайна - легко ли мой код для кого-то другого или мое будущее забывчивое я для понимания? - и гибкость дизайна- когда мне неизбежно придется исправлять ошибки, добавлять функции или иным образом изменять функциональность, насколько мой код будет противостоять изменениям? Единственное, о чем заботится ваш клиент, - это как быстро вы сможете ответить, когда она сообщит об ошибке или попросит внести изменения. Когда вы задаете эти вопросы о своем дизайне, это, как правило, приводит к тому, что код можно использовать повторно, но этот подход позволяет вам сосредоточиться на том, чтобы избежать реальных проблем, с которыми вы столкнетесь в течение жизни этого кода, чтобы вы могли лучше обслуживать конечного пользователя, а не преследовать высокий, непрактичный «инженерные» идеалы, чтобы радовать шею бороды.
Для чего-то столь же простого, как пример, который вы предоставили, ваша первоначальная реализация хороша из-за ее малости, но этот простой дизайн станет трудным для понимания и ломким, если вы попытаетесь вложить слишком много функциональной гибкости (в отличие от гибкости дизайна) в одна процедура. Ниже мое объяснение моего предпочтительного подхода к проектированию сложных систем для понимания и гибкости, которое, я надеюсь, продемонстрирует, что я имею в виду под ними. Я бы не использовал эту стратегию для чего-то, что можно было бы написать менее чем в 20 строках за одну процедуру, потому что что-то настолько маленькое, что уже соответствует моим критериям понятности и гибкости.
Объекты, а не процедуры
Вместо того чтобы использовать классы, такие как модули старой школы, с набором подпрограмм, которые вы вызываете для выполнения того, что должно делать ваше программное обеспечение, рассмотрите возможность моделирования домена как объектов, которые взаимодействуют и взаимодействуют для выполнения поставленной задачи. Методы в объектно-ориентированной парадигме были изначально созданы для того, чтобы быть сигналами между объектами, чтобы они
Object1
могли сказать,Object2
что делать, что бы то ни было, и, возможно, получить ответный сигнал. Это связано с тем, что объектно-ориентированная парадигма по своей сути предназначена для моделирования ваших доменных объектов и их взаимодействий, а не для причудливого способа организации тех же самых старых функций и процедур императивной парадигмы. В случае сvoid destroyBaghdad
Например, вместо того, чтобы пытаться написать не зависящий от контекста универсальный метод для обработки разрушения Багдада или любой другой вещи (которая может быстро стать сложной, трудной для понимания и ломкой), каждая вещь, которая может быть уничтожена, должна отвечать за понимание того, как уничтожить себя. Например, у вас есть интерфейс, который описывает поведение вещей, которые могут быть уничтожены:Тогда у вас есть город, который реализует этот интерфейс:
Ничто из того, что требует уничтожения экземпляра
City
, никогда не будет заботиться о том, как это происходит, поэтому нет никаких оснований для того, чтобы этот код существовал где-либо за пределамиCity::destroy
, и, действительно, глубокое знание внутренней работыCity
вне себя было бы тесной связью, которая уменьшает гибкость, так как вы должны учитывать эти внешние элементы, если вам когда-либо потребуется изменить поведениеCity
. Это истинная цель инкапсуляции. Думайте об этом, как будто у каждого объекта есть свой собственный API, который должен позволять вам делать с ним все, что вам нужно, чтобы вы могли беспокоиться о выполнении ваших запросов.Делегация, а не «Контроль»
Теперь, является ли ваш реализующий класс
City
илиBaghdad
зависит от того, насколько универсальным оказывается процесс разрушения города. По всей вероятности,City
воля будет состоять из более мелких частей, которые нужно будет уничтожить по отдельности, чтобы выполнить полное разрушение города, поэтому в этом случае каждая из этих частей также будет реализованаDestroyable
, и каждая из них получит указаниеCity
уничтожить Сами же так же кто-то извне просил себяCity
уничтожить.Если вы хотите по-настоящему сойти с ума и реализовать идею о
Bomb
том, что объект отбрасывается на локацию и уничтожает все в пределах определенного радиуса, это может выглядеть примерно так:ObjectsByRadius
представляет набор объектов, который рассчитывается дляBomb
входных данных, потому чтоBomb
не имеет значения, как выполняется этот расчет, если он может работать с объектами. Кстати, это можно многократно использовать, но главная цель - изолировать вычисления от процессов отбрасыванияBomb
и разрушения объектов, чтобы вы могли понять каждую часть и то, как они сочетаются друг с другом, и изменить поведение отдельной части, не изменяя весь алгоритм ,Взаимодействия, а не алгоритмы
Вместо того, чтобы пытаться угадать правильное количество параметров для сложного алгоритма, имеет смысл моделировать процесс как набор взаимодействующих объектов, каждый из которых имеет чрезвычайно узкие роли, поскольку это даст вам возможность моделировать сложность вашего процесс через взаимодействия между этими четко определенными, простыми для понимания и почти неизменными объектами. Если все сделано правильно, это делает даже некоторые из самых сложных модификаций такими же тривиальными, как реализация одного или двух интерфейсов и переработка тех объектов, которые создаются в вашем
main()
методе.Я бы дал вам кое-что для вашего оригинального примера, но я, честно говоря, не могу понять, что значит «печатать… Дневные сбережения». Что я могу сказать об этой категории проблем, так это то, что каждый раз, когда вы выполняете вычисление, результат которого можно отформатировать несколькими способами, мой предпочтительный способ разбить это так:
Поскольку в вашем примере используются классы из библиотеки Java, которые не поддерживают этот дизайн, вы можете просто использовать API
ZonedDateTime
напрямую. Идея здесь заключается в том, что каждый расчет заключен в свой собственный объект. Он не делает никаких предположений о том, сколько раз он должен выполняться или как он должен отформатировать результат. Это исключительно связано с выполнением простейшей формы расчета. Это облегчает понимание и гибкость в изменениях. Точно так же,Result
он связан исключительно с инкапсуляцией результата вычисления, аFormattedResult
исключительно с взаимодействием с нимResult
для его форматирования в соответствии с определенными нами правилами. Таким образом,мы можем найти идеальное количество аргументов для каждого из наших методов, поскольку каждый из них имеет четко определенную задачу . Также гораздо проще изменить движение вперед, если интерфейсы не меняются (что они вряд ли будут делать, если вы должным образом минимизировали ответственность ваших объектов). Нашmain()
метод может выглядеть так:Фактически, объектно-ориентированное программирование было придумано специально как решение проблемы сложности / гибкости парадигмы императива, потому что просто нет хорошего ответа (что каждый может договориться или прийти независимо, так или иначе), как оптимально указать императивные функции и процедуры в рамках идиомы.
источник
Опыт , знание предметной области и обзоры кода.
И независимо от того, сколько у вас опыта , знаний в области или команды , вы не можете избежать необходимости рефакторинга по мере необходимости.
С помощью опыта вы начнете распознавать шаблоны в не зависящих от домена методах (и классах), которые вы пишете. И, если вас вообще интересует DRY-код, вы будете испытывать дурные чувства, когда будете писать метод, который, как вы инстинктивно знаете , будут писать варианты в будущем. Таким образом, вместо этого вы интуитивно напишите параметризованный наименьший общий знаменатель.
(Этот опыт может инстинктивно переноситься на некоторые ваши доменные объекты и методы тоже.)
С Знанием предметной области вы будете иметь представление о том, какие бизнес-концепции тесно связаны, какие концепции имеют переменные, которые довольно статичны и т. Д.
С помощью Code Reviews недопустимая и чрезмерная параметризация, скорее всего, будет обнаружена до того, как она станет рабочим кодом, потому что ваши коллеги (будем надеяться) будут иметь уникальный опыт и перспективы, как в области, так и в кодировании в целом.
Тем не менее, новые разработчики, как правило, не будут иметь эти Spidey Senses или опытную группу сверстников, чтобы опираться сразу. И даже опытные разработчики извлекают выгоду из базовой дисциплины, чтобы направлять их через новые требования - или через туманные дни. Итак, вот что я бы предложил для начала :
(Включите любые параметры , которые вы уже знаете , вам нужно, очевидно , ...)
Эти шаги не обязательно происходят в указанном порядке. Если вы сядете, чтобы написать метод, который, как вы уже знаете, сильно избыточен по сравнению с существующим , перейдите прямо к рефакторингу, если это удобно. (Если на рефакторинг не потребуется значительно больше времени, чем на написание, тестирование и поддержку двух методов.)
Но помимо большого опыта и т. Д., Я советую довольно минималистичный СУХОЙ код. Нетрудно рефакторировать очевидные нарушения. И, если вы слишком усердны , вы можете получить «чрезмерно сухой» код, который еще сложнее читать, понимать и поддерживать, чем «мокрый» эквивалент.
источник
If destoryCity(City city) is better than destoryBaghdad(), is takeActionOnCity(Action action, City city) even better?
? Это вопрос да / нет, но у него нет ответа, верно? Или первоначальное предположение неверно,destroyCity(City)
не обязательно должно быть лучше, и это на самом деле зависит ? Значит, это не значит, что я не являюсь инженером -программистом, не прошедшим этическую подготовку, потому что на первом месте я реализовал напрямую без каких-либо параметров? Я имею в виду, каков ответ на конкретный вопрос, который я задаю?destroyBaghdad()
метода. Какой контекст? Это видеоигра, в которой конец игры приводит к разрушению Багдада ??? Если это так ...destroyBaghdad()
может быть вполне разумное имя метода / подпись ...It should be noted that no ethically-trained software engineer would ever consent to write a DestroyBaghdad procedure.
Если бы вы были в комнате с Натаниэлем Боренштейном, вы бы поспорили с ним, что это на самом деле зависит, а его утверждение неверно? Я имею в виду, что это прекрасно, что многие люди отвечают, тратят свое время и энергию, но я не вижу ни одного конкретного ответа нигде. Spidey-чувства, обзоры кода .. Но каков ответis takeActionOnCity(Action action, City city) better?
null
?Тот же ответ, что и с качеством, удобством использования, техническим долгом и т. Д.
Как многоразовые , как вам, пользователю, 1 нужно им быть
Это в основном суждение - будет ли стоимость проектирования и обслуживания абстракции возмещена стоимостью (= время и усилия), которая спасет вас в будущем.
1 Это кодовая конструкция, поэтому вы «пользователь» в данном случае - пользователь исходного кода
источник
Есть четкий процесс, которому вы можете следовать:
Это происходит с - по крайней мере, по мнению некоторых людей - в значительной степени оптимальным кодом, так как он настолько мал, насколько это возможно, каждая законченная функция занимает как можно меньше времени (что может быть или не быть правдой, если вы посмотрите на законченный продукт после рефакторинга) и имеет очень хорошее тестовое покрытие. Это также заметно избегает чрезмерно сконструированных слишком общих методов или классов.
Это также дает вам очень четкие инструкции, когда делать вещи универсальными, а когда специализироваться.
Я нахожу пример вашего города странным; Скорее всего, я никогда не буду жестко задавать название города. Это так очевидно, что дополнительные города будут включены позже, что бы вы ни делали. Но другим примером могут быть цвета. В некоторых случаях возможно жесткое кодирование «красного» или «зеленого». Например, светофор такой повсеместный цвет, что вы можете просто сойти с него (и вы всегда можете рефакторинг). Разница в том, что «красный» и «зеленый» имеют универсальное, «жестко закодированное» значение в нашем мире, невероятно маловероятно, что оно когда-либо изменится, и альтернативы на самом деле тоже нет.
Ваш первый метод перехода на летнее время просто сломан. Несмотря на то, что он соответствует спецификациям, жестко заданный 2018 год особенно плох, поскольку а) он не упоминается в техническом «контракте» (в данном случае в названии метода) и б) он скоро устареет, поэтому поломка включен с самого начала. Для вещей, которые связаны со временем / датой, очень редко имеет смысл жестко задавать конкретное значение, поскольку время течет. Но кроме этого все остальное подлежит обсуждению. Если вы даете ему простой год, а затем всегда рассчитываете полный год, продолжайте. Большинство из перечисленных вами вещей (форматирование, выбор меньшего диапазона и т. Д.) Кричит о том, что ваш метод делает слишком много, и вместо этого он, вероятно, должен вернуть список / массив значений, чтобы вызывающая сторона могла самостоятельно выполнить форматирование / фильтрацию.
Но, в конце концов, в основном это мнение, вкус, опыт и личная предвзятость, так что не слишком переживайте по этому поводу.
источник
Я пришел к мнению, что существует два вида кода многократного использования:
Первый вид повторного использования часто является хорошей идеей. Это относится к таким вещам, как списки, хеш-карты, хранилища ключей / значений, сопоставления строк (например, регулярное выражение, глобус, ...), кортежи, унификация, деревья поиска (сначала глубина, сначала ширина, итеративное углубление, ...) комбинаторы синтаксического анализатора, кэши / памятные записки, устройства чтения / записи форматов данных (s-выражения, XML, JSON, protobuf, ...), очереди задач и т. д.
Эти вещи настолько общие, очень абстрактным образом, что они повторно используются повсеместно в повседневном программировании. Если вы пишете специальный код, который был бы проще, если бы он был более абстрактным / общим (например, если у нас есть «список заказов клиентов», мы могли бы выбросить материал «заказ клиентов», чтобы получить «список»). ) тогда было бы неплохо вытащить это. Даже если он не используется повторно, он позволяет нам отделить несвязанную функциональность.
Второй тип - это когда у нас есть конкретный код, который решает реальную проблему, но делает это путем принятия целого ряда решений. Мы можем сделать его более общим / многократно используемым путем «мягкого кодирования» этих решений, например, превращая их в параметры, усложняя реализацию и выполняя даже более конкретные детали (т. Е. Знание, какие хуки нам могут потребоваться для переопределений). Ваш пример, кажется, такого рода. Проблема такого рода повторного использования заключается в том, что мы можем попытаться угадать варианты использования других людей или наши будущие "я". В конечном итоге мы можем получить столько параметров, что наш код не будет использоватьсяне говоря уже о многоразовости! Другими словами, при вызове требуется больше усилий, чем просто написание собственной версии. Вот где важен YAGNI (он тебе не нужен). Во многих случаях такие попытки «многократно используемого» кода заканчиваются тем, что его не используют повторно, поскольку он может быть несовместим с этими вариантами использования более фундаментально, чем могут учитывать параметры, или эти потенциальные пользователи скорее свернут свои собственные (чёрт, посмотрите на все стандарты и библиотеки там, чьи авторы имеют префикс со словом «простой», чтобы отличать их от предшественников!).
Эта вторая форма «повторного использования» должна в основном выполняться по мере необходимости. Конечно, вы можете вставить некоторые «очевидные» параметры, но не пытайтесь предсказать будущее. YAGNI.
источник
destroyBaghdad
это одноразовый сценарий (или, по крайней мере, он идемпотентный). Может быть, уничтожение любого города было бы улучшением, но что, если онdestroyBaghdad
работает, затопив Тигр? Это может быть повторно использовано для Мосула и Басры, но не для Мекки или Атланты.static
методов), которые являются чисто функциональными и низкоуровневыми, и, в отличие от этого, решение о «настройке параметров и хуков» обычно заключается в том, где Вы должны построить некоторые структуры, которые требуют оправдания.Там уже есть много отличных и сложных ответов. Некоторые из них углубляются в конкретные детали, излагают определенные точки зрения на методологии разработки программного обеспечения в целом, а некоторые из них, безусловно, содержат в себе спорные элементы или «мнения».
Ответ на Warbo уже отмечали различные виды повторного использования. А именно, является ли что-то повторно используемым, потому что оно является фундаментальным строительным блоком, или является ли что-то повторно используемым, потому что это «универсально» в некотором роде. Ссылаясь на последнее, есть кое-что, что я бы посчитал своего рода мерой для повторного использования:
Может ли один метод подражать другому.
Что касается примера из вопроса: представьте, что метод
была реализация функциональности, которая была запрошена клиентом. Так что это будет то, что другие программисты должны использовать , и, таким образом, будет публичным методом, как в
public
void dayLightSavings()
Это может быть реализовано, как вы показали в своем ответе. Теперь кто-то хочет параметризовать его по году. Таким образом, вы можете добавить метод
public
void dayLightSavings(int year)
и изменить исходную реализацию, чтобы просто быть
Следующие «запросы возможностей» и обобщения следуют той же схеме. Поэтому, если и только если есть потребность в самой общей форме, вы можете реализовать ее, зная, что эта самая общая форма допускает тривиальные реализации более конкретных:
Если вы ожидали будущих расширений и запросов на функции, и у вас было некоторое время, и вы хотели провести скучные выходные с (потенциально бесполезными) обобщениями, вы могли бы начать с самого общего с самого начала. Но только как частный метод. Пока вы только выставили простой метод, который был запрошен клиентом, как публичный метод, вы в безопасности.
тл; др :
Вопрос на самом деле не столько в том, «насколько многоразовым должен быть метод». Вопрос в том, насколько уязвима эта возможность повторного использования и как выглядит API. Создание надежного API, способного выдержать испытание временем (даже если в дальнейшем возникнут дополнительные требования), является искусством и ремеслом, и тема слишком сложна, чтобы ее здесь охватить. Для начала ознакомьтесь с этой презентацией Джошуа Блоха или вики-книгой по дизайну API .
источник
dayLightSavings()
звонитьdayLightSavings(2018)
мне не кажется хорошей идеей.dayLightSavings(computeCurrentYear());
. ...Хорошее эмпирическое правило: ваш метод должен быть как многоразовым, так и ... многоразовым.
Если вы ожидаете, что будете вызывать ваш метод только в одном месте, он должен иметь только те параметры, которые известны сайту вызова и недоступны для этого метода.
Если у вас есть больше абонентов, вы можете ввести новые параметры, если другие абоненты могут передавать эти параметры; в противном случае вам нужен новый метод.
Поскольку количество абонентов может со временем расти, вам необходимо быть готовым к рефакторингу или перегрузке. Во многих случаях это означает, что вы должны чувствовать себя в безопасности, выбирая выражение и выполняя действие «extract parameter» вашей IDE.
источник
Сверх краткий ответ: чем меньше связи или зависимости от другого кода у вашего универсального модуля, тем больше его можно использовать повторно.
Ваш пример зависит только от
так что в теории это может быть многоразовым.
На практике я не думаю, что у вас когда-нибудь будет второй сценарий использования, которому нужен этот код, поэтому, следуя принципу Ягни, я бы не использовал его повторно, если бы не было более 3 разных проектов, которым нужен этот код.
Другими аспектами возможности повторного использования являются простота использования и возможность документирования, которые связаны с разработкой на основе тестирования : полезно, если у вас есть простой модульный тест, который демонстрирует / документирует простое использование вашего универсального модуля в качестве примера кодирования для пользователей вашей библиотеки.
источник
Это хорошая возможность сформулировать правило, которое я недавно придумал:
Быть хорошим программистом означает быть способным предсказывать будущее.
Конечно, это абсолютно невозможно! В конце концов, вы никогда не знаете наверняка, какие обобщения вы найдете полезными позже, какие связанные задачи вы хотите выполнить, какие новые функции будут нужны вашим пользователям, & c. Но опыт иногда дает вам приблизительное представление о том, что может пригодиться.
Другими факторами, с которыми вы должны уравновешивать это, является то, сколько дополнительного времени и усилий потребуется, и насколько сложнее это сделает ваш код. Иногда вам везет, и решение более общей проблемы на самом деле проще! (По крайней мере, концептуально, если не в объеме кода.) Но чаще это сложность, а также затраты времени и усилий.
Поэтому, если вы считаете, что обобщение, скорее всего, понадобится, его часто стоит делать (если только это не добавляет много работы или сложности); но если это кажется гораздо менее вероятным, то, вероятно, это не так (если только это не очень просто и / или не упрощает код).
(Для недавнего примера: на прошлой неделе мне дали спецификацию для действий, которые система должна предпринять ровно через 2 дня после того, как что-то истекло. Поэтому, конечно, я сделал двухдневный период параметром. На этой неделе бизнесмены были в восторге, так как они собирались просить об этом улучшении! Мне повезло: это было легкое изменение, и я догадался, что его вполне можно было бы найти. Часто это сложнее судить. Но все же стоит попытаться предсказать, и опыт часто является хорошим руководством .)
источник
Во-первых, лучший ответ на вопрос «Как я узнаю, какими должны быть мои методы многократного использования?» - это «опыт». Сделайте это несколько тысяч раз, и вы, как правило, получите правильный ответ. Но в качестве тизера я могу дать вам последнее Строка этого ответа: ваш клиент скажет вам, сколько гибкости и сколько уровней обобщения вы должны искать.
Многие из этих ответов имеют конкретные советы. Я хотел дать что-то более общее ... потому что ирония слишком весела, чтобы от нее отказаться!
Как отмечалось в некоторых ответах, общность стоит дорого. Тем не менее, это действительно не так. Не всегда. Понимание расходов важно для игры с возможностью повторного использования.
Я сосредотачиваюсь на том, чтобы поставить вещи в масштабе от «необратимого» до «обратимого». Это гладкая шкала. Единственная действительно необратимая вещь - это «время, потраченное на проект». Вы никогда не получите эти ресурсы обратно. Чуть менее обратимыми могут быть ситуации «золотых наручников», такие как Windows API. Устаревшие функции остаются в этом API в течение десятилетий, потому что бизнес-модель Microsoft требует этого. Если у вас есть клиенты, чьи отношения будут навсегда повреждены из-за удаления какой-либо функции API, это следует рассматривать как необратимое. Глядя на другой конец шкалы, у вас есть такие вещи, как прототип кода. Если вам не нравится, куда он идет, вы можете просто выбросить его. Чуть менее обратимыми могут быть API внутреннего использования. Они могут быть реорганизованы, не беспокоя клиентавремя (самый необратимый ресурс из всех!)
Так что ставьте их в масштабе. Теперь вы можете применять эвристику: чем более обратимым является что-то, тем больше вы можете использовать его для будущих действий. Если что-то необратимо, используйте его только для конкретных задач, ориентированных на клиента. Вот почему вы видите принципы, подобные тем, которые используются в экстремальных программах, которые предлагают делать только то, что просит клиент, и ничего более. Эти принципы помогают убедиться, что вы не делаете то, о чем сожалеете.
Такие вещи, как принцип СУХОЙ, предлагают способ изменить этот баланс. Если вы обнаружите, что повторяетесь, это возможность создать внутренний API. Ни один клиент не видит это, поэтому вы всегда можете изменить его. Если у вас есть этот внутренний API, теперь вы можете начать играть с вещами, ориентированными на будущее. Как ты думаешь, сколько заданий по часовому поясу тебе даст твоя жена? Есть ли у вас другие клиенты, которые могли бы хотеть задачи на основе часового пояса? Ваша гибкость здесь определяется конкретными требованиями ваших нынешних клиентов и поддерживает потенциальные будущие потребности будущих клиентов.
Этот многоуровневый подход к мышлению, который естественно исходит от DRY, естественно, обеспечивает желаемое обобщение без потерь. Но есть ли предел? Есть конечно. Но чтобы увидеть это, нужно увидеть лес за деревьями.
Если у вас много уровней гибкости, они часто приводят к отсутствию прямого контроля над уровнями, с которыми сталкиваются ваши клиенты. У меня было программное обеспечение, где у меня была жестокая задача объяснить клиенту, почему он не может получить то, что хочет, из-за гибкости, встроенной в 10 слоев, которые они никогда не должны были видеть. Мы написали себя в угол. Мы связали себя узами брака со всей гибкостью, которая, как нам казалось, нам была нужна.
Поэтому, когда вы делаете этот трюк обобщения / сушки, всегда держите руку на пульсе своего клиента . Как вы думаете, что ваша жена собирается попросить дальше? Вы ставите себя в положение для удовлетворения этих потребностей? Если у вас есть умение, клиент эффективно расскажет вам о своих будущих потребностях. Если у вас нет ловкости, большинство из нас просто полагаются на догадки! (особенно с супругами!) Некоторым клиентам нужна большая гибкость, и они будут готовы принять на себя дополнительные расходы, связанные с разработкой всех этих уровней, поскольку они напрямую извлекают выгоду из гибкости этих уровней. У других заказчиков довольно постоянные требования, и они предпочитают, чтобы разработка была более прямой. Ваш клиент скажет вам, сколько гибкости и сколько уровней обобщения вы должны искать.
источник
Your customer will tell you how much flexibility and how many layers of generalization you should seek.
что это за мир?Это то, что известно в кругах разработчиков программного обеспечения как «шутка». Шутки не должны быть тем, что мы называем «правдой», хотя, чтобы быть смешными, они, как правило, намекают на что-то правдивое.
В данном конкретном случае «шутка» не является «правдой». Можно с уверенностью предположить, что работа, связанная с написанием общей процедуры уничтожения любого города, на несколько порядков больше, чем требуется для уничтожения одного конкретного города.город. В противном случае любой, кто разрушил один или несколько городов (библейский Иисус Навин, или, скажем, президент Трумэн), мог тривиально обобщить то, что он сделал, и был способен уничтожить абсолютно любой город по своему желанию. Это на самом деле не так. Методы, которые эти два человека, как известно, использовали для уничтожения небольшого числа конкретных городов, не обязательно будут работать в любой момент в любом городе. Другой город, чьи стены имели различную резонансную частоту или высотные средства воздушной обороны были лучше, потребовал бы незначительных или фундаментальных изменений в подходе (труба или ракета различной высоты).
Это также приводит к тому, что код будет постоянно защищаться от изменений: в настоящее время существует довольно много городов, которые не подпадают ни под один из этих подходов, благодаря современным методам строительства и вездесущему радару.
Разработка и тестирование полностью общих средств, которые уничтожат любой город, прежде чем вы согласитесь уничтожить только один город, - это отчаянно неэффективный подход. Ни один инженер по этике, обученный этике, не попытался бы обобщить проблему до такой степени, которая требовала бы на порядок больше работы, чем на самом деле должен был заплатить их работодатель / клиент, без явного требования.
Так что правда? Иногда добавление общности тривиально. Должны ли мы всегда добавлять общность, когда это тривиально? Я бы по-прежнему утверждал «нет, не всегда» из-за проблемы с длительным обслуживанием. Предположим, что на момент написания статьи все города в основном одинаковы, поэтому я продолжу с DestroyCity. Как только я написал это, наряду с интеграционными тестами, которые (из-за конечно-перечислимого пространства входных данных) выполняют итерацию по каждому известному городу и проверяют, работает ли функция на каждом (не знаю, как это работает. Вероятно, вызывает City.clone () и уничтожить клона? Во всяком случае).
На практике эта функция используется исключительно для уничтожения Багдада, предположим, что кто-то строит новый город, устойчивый к моим методам (это глубоко под землей или что-то в этом роде). Теперь у меня провал интеграционного теста для варианта использования , которого даже не существует , и прежде чем я смогу продолжить свою кампанию террора против ни в чем не повинного гражданского населения Ирака, я должен выяснить, как уничтожить Субтерранию. Неважно, является ли это этичным или нет, это глупо и трата моего безумного времени .
Итак, вы действительно хотите функцию, которая может выводить летнее время для любого года, просто для вывода данных за 2018 год? Возможно, но это, безусловно, потребует небольшого количества дополнительных усилий, просто собирая тестовые наборы. Может потребоваться много усилий, чтобы получить лучшую базу данных часовых поясов, чем та, которая у вас есть на самом деле. Так, например, в 1908 году в городе Порт-Артур, Онтарио, период DST начался 1 июля. Это в базе данных часовых поясов вашей ОС? Мысль нет, поэтому ваша обобщенная функция неверна . Нет ничего особенно этичного в написании кода, который дает обещания, которые он не может выполнить.
Итак, с соответствующими предостережениями легко написать функцию, которая выполняет временные зоны в течение ряда лет, скажем, с 1970 года по настоящее время. Но так же легко взять функцию, которую вы на самом деле написали, и обобщить ее для параметризации года. Так что сейчас нет ничего более этичного / разумного обобщать сейчас, это делать то, что вы делали, а затем обобщать, если и когда вам это нужно.
Однако, если бы вы знали, почему ваша жена хотела проверить этот список летнего времени, у вас было бы информированное мнение о том, может ли она снова задать тот же вопрос в 2019 году, и если да, то сможете ли вы выйти из этого цикла, дав ей функцию, которую она может вызвать, без необходимости перекомпилировать ее. После того, как вы сделали этот анализ, ответ на вопрос «следует ли это обобщать на последние годы» может быть «да». Но вы создаете для себя еще одну проблему, которая заключается в том, что данные о часовых поясах в будущем носят только предварительный характер, и поэтому, если она запустит их на 2019 год сегодня, она может или не может понять, что это питает ее предположение. Таким образом, вам все еще нужно написать кучу документации, которая не понадобится для менее общей функции («данные поступают из базы данных часовых поясов, бла-бла, вот ссылка, чтобы увидеть их политику по продвижению обновлений, бла-бла»). Если вы отказываетесь от особого случая, то все время, пока вы это делаете, она не может выполнить задачу, для которой ей нужны данные за 2018 год, из-за какой-то ерунды о 2019 году, о которой она даже не заботится.
Не делай трудных вещей, не продумав их должным образом, только потому, что тебе пошутили. Это полезно? Это достаточно дешево для такой степени полезности?
источник
Это простая линия для рисования, потому что повторное использование, как это определено астронавтами архитектуры, является бредом.
Почти весь код, созданный разработчиками приложений, является исключительно предметно-ориентированным. Это не 1980 год. Почти все, что стоит беспокоиться, уже в рамках.
Абстракции и соглашения требуют документирования и обучения. Пожалуйста, прекратите создавать новые только ради этого. (Я смотрю на тебя , JavaScript люди!)
Давайте потворствуем неправдоподобной фантазии о том, что вы нашли то, что действительно должно быть в ваших предпочтениях. Вы не можете просто набрать код, как обычно. О нет, вам нужно тестовое покрытие не только для предполагаемого использования, но и для отклонений от предполагаемого использования, для всех известных крайних случаев, для всех мыслимых режимов отказов, тестовых случаев для диагностики, тестовых данных, технической документации, пользовательской документации, управления выпусками, поддержки скрипты, регрессионные тесты, управление изменениями ...
Ваш работодатель рад заплатить за все это? Я собираюсь сказать нет.
Абстракция - это цена, которую мы платим за гибкость. Это делает наш код более сложным и трудным для понимания. Если гибкость не служит реальной и существующей потребности, просто не надо, потому что YAGNI.
Давайте посмотрим на реальный пример, с которым мне только что пришлось иметь дело: HTMLRenderer. Он не масштабировался должным образом, когда я пытался выполнить рендеринг в контекст устройства принтера. Мне потребовался целый день, чтобы обнаружить, что по умолчанию он использует GDI (который не масштабируется), а не GDI + (который делает, но не сглаживает), потому что мне пришлось пройти через шесть уровней косвенного обращения в двух сборках, прежде чем я нашел код, который сделал что-нибудь .
В этом случае я прощу автора. Абстракция на самом деле необходимо , так как это является основой код , который нацелен на пять очень разных целей рендеринга: WinForms, WPF, DotNet Core, Mono и PDFsharp.
Но это только подчеркивает мою точку зрения: вы почти наверняка не делаете что-то чрезвычайно сложное (рендеринг HTML с таблицами стилей) для нескольких платформ с заявленной целью высокой производительности на всех платформах.
Ваш код почти наверняка является еще одной сеткой базы данных с бизнес-правилами, которые применяются только к вашему работодателю, и налоговыми правилами, которые применяются только в вашем штате, в приложении, которое не продается.
Все это косвенное решение решает проблему, которой у вас нет, и делает ваш код намного труднее для чтения, что значительно увеличивает стоимость обслуживания и наносит огромный ущерб вашему работодателю. К счастью, люди, которые должны жаловаться на это, не в состоянии понять, что вы с ними делаете.
Противоположным аргументом является то, что этот вид абстракции поддерживает разработку, основанную на тестировании, но я думаю, что TDD также является бредом, поскольку предполагает, что бизнес имеет четкое, полное и правильное понимание своих требований. TDD отлично подходит для НАСА и управляющего программного обеспечения для медицинского оборудования и автомобилей с автоматическим управлением, но слишком дорог для всех остальных.
Между прочим, невозможно предсказать всю дневную экономию в мире. В частности, в Израиле ежегодно совершается около 40 переходов, которые перепрыгивают повсюду, потому что у нас не может быть людей, молящихся в неподходящее время, и Бог не делает летних сбережений.
источник
Если вы используете хотя бы java 8, вы должны написать класс WorldTimeZones, чтобы обеспечить то, что по сути представляется коллекцией часовых поясов.
Затем добавьте метод фильтра (фильтр предиката) в класс WorldTimeZones. Это позволяет вызывающей стороне фильтровать все, что они хотят, передавая лямбда-выражение в качестве параметра.
По сути, метод одиночного фильтра поддерживает фильтрацию всего, что содержится в значении, передаваемом предикату.
Кроме того, добавьте метод stream () в ваш класс WorldTimeZones, который при вызове создает поток часовых поясов. Затем вызывающий абонент может фильтровать, отображать и уменьшать по своему усмотрению, не записывая никаких специализаций.
источник