Я понимаю важность хорошо документированного кода. Но я также понимаю важность самодокументируемого кода. Чем проще визуально прочитать определенную функцию, тем быстрее мы можем двигаться во время обслуживания программного обеспечения.
С учетом сказанного я люблю разделять большие функции на другие меньшие. Но я делаю это до такой степени, что класс может иметь более пяти из них только для обслуживания одного открытого метода. Теперь умножьте пять частных методов на пять открытых, и вы получите около двадцати пяти скрытых методов, которые, вероятно, будут вызываться только один раз этими открытыми.
Конечно, теперь легче читать эти общедоступные методы, но я не могу не думать, что слишком много функций - плохая практика.
[Редактировать]
Люди спрашивают меня, почему я считаю слишком много функций плохой практикой.
Простой ответ: это внутреннее чувство.
Мое убеждение не подкреплено какими-то часами опыта разработки программного обеспечения. Это просто неопределенность, которая дала мне «писательский блок», но для программиста.
В прошлом я программировал только личные проекты. Совсем недавно я перешел к групповым проектам. Теперь я хочу, чтобы другие могли читать и понимать мой код.
Я не был уверен, что улучшит разборчивость. С одной стороны, я думал о том, чтобы разделить одну большую функцию на другую с понятными именами. Но была другая сторона меня, говоря, что это просто избыточно.
Итак, я прошу это просветить себя, чтобы выбрать правильный путь.
[Редактировать]
Ниже я включил две версии того, как я мог решить мою проблему. Первый решает эту проблему, не разделяя большие куски кода. Второй делает отдельные вещи.
Первая версия:
public static int Main()
{
// Displays the menu.
Console.WriteLine("Pick your option");
Console.Writeline("[1] Input and display a polynomial");
Console.WriteLine("[2] Add two polynomials");
Console.WriteLine("[3] Subtract two polynomials");
Console.WriteLine("[4] Differentiate two polynomials");
Console.WriteLine("[0] Quit");
}
Вторая версия:
public static int Main()
{
DisplayMenu();
}
private static void DisplayMenu()
{
Console.WriteLine("Pick your option");
Console.Writeline("[1] Input and display a polynomial");
Console.WriteLine("[2] Add two polynomials");
Console.WriteLine("[3] Subtract two polynomials");
Console.WriteLine("[4] Differentiate two polynomials");
Console.WriteLine("[0] Quit");
}
В приведенных выше примерах последний вызывает функцию, которая будет использоваться только один раз в течение всего времени выполнения программы.
Примечание: приведенный выше код обобщен, но имеет ту же природу, что и моя проблема.
Теперь вот мой вопрос: какой? Я выбираю первый или второй?
Ответы:
Это то, что известно как Инкапсуляция , которая создает контрольную абстракцию на более высоком уровне. Это хорошая вещь.
Это означает , что каждый , кто читает ваш код, когда они попадают в
startTheEngine()
метод в вашем коде, может игнорировать все нижние детали уровня , такие какopenIgnitionModule()
,turnDistributorMotor()
,sendSparksToSparkPlugs()
,injectFuelIntoCylinders()
,activateStarterSolenoid()
, и все другой сложной, маленькой, функции , которые должны быть запущены, чтобы облегчить гораздо большую, более абстрактную функциюstartTheEngine()
.Если проблема, с которой вы сталкиваетесь в своем коде, не связана непосредственно с одним из этих компонентов, разработчики кода могут двигаться дальше, игнорируя изолированную, инкапсулированную функциональность.
Это также имеет дополнительное преимущество, заключающееся в упрощении тестирования кода. , Например, я могу написать контрольный пример
turnAlternatorMotor(int revolutionRate)
и проверить его функциональность, полностью независимую от других систем. Если есть проблема с этой функцией и результат не соответствует ожиданиям, я знаю, в чем проблема.Код, который не разбит на компоненты, гораздо сложнее протестировать. Внезапно ваши сопровождающие только смотрят на абстракцию, вместо того, чтобы копаться в измеримых компонентах.
Я советую продолжать делать то, что вы делаете, так как ваш код будет масштабироваться, его будет легко поддерживать, и его можно будет использовать и обновлять в течение многих лет.
источник
myString.replaceAll(pattern, originalData);
или я вставил весь метод replaceAll в свой код, включая массив вспомогательных символов, который представляет базовый строковый объект каждый раз, когда мне нужно использовать функциональность строки?И да и нет. Фундаментальный принцип: метод должен делать одно и только одно
Правильно «разбить» ваш метод на более мелкие методы. Опять же, эти методы должны быть оптимально достаточно общими, чтобы не только обслуживать этот «большой» метод, но и использоваться в других местах. В некоторых случаях метод будет следовать простой древовидной структуре, такой как метод Initialize, который вызывает InitializePlugins, InitializeInterface и т. Д.
Если у вас действительно большой метод / класс, это, как правило, признак того, что он делает много, и вам нужно провести некоторый рефакторинг, чтобы разбить «блоб». Возможно скрыть некоторую сложность в другом классе под абстракцией, и использовать внедрение зависимости
источник
Я думаю, что в целом лучше ошибиться из-за слишком большого количества функций, а не слишком мало. Два исключения из этого правила, которые я видел на практике, относятся к принципам СУХОЙ и ЯГНИ . Если у вас есть много почти идентичных функций, вы должны объединить их и использовать параметры, чтобы избежать повторения. У вас также может быть слишком много функций, если вы создали их «на всякий случай», и они не используются. Я не вижу абсолютно ничего плохого в том, чтобы иметь функцию, которая используется только один раз, если она добавляет удобочитаемость и удобство обслуживания.
источник
Отличный ответ jmort253 вдохновил меня на ответ "да, но" ...
Наличие большого количества маленьких частных методов не так уж и плохо, но обратите внимание на это чувство, которое заставляет вас задать вопрос здесь :). Если вы попадаете в точку, в которой вы пишете тесты, которые фокусируются только на частных методах (либо вызывая их напрямую, либо настраивая сценарий так, чтобы он вызывался определенным образом, чтобы вы могли проверить результат), тогда ты должен сделать шаг назад.
То, что у вас может быть, - это новый класс с собственным более сфокусированным публичным интерфейсом, пытающимся сбежать. В этот момент вы можете подумать о том, чтобы извлечь класс для инкапсуляции той части ваших существующих функций классов, которая в настоящее время живет в приватном методе. Теперь ваш исходный класс может использовать ваш новый класс в качестве зависимости, и вы можете писать более сфокусированные тесты непосредственно для этого класса.
источник
Почти каждый ОО-проект, к которому я когда-либо присоединялся, сильно страдал от слишком больших классов и слишком длинных методов.
Когда я чувствую необходимость в комментарии, объясняющем следующий раздел кода, я выделяю этот раздел в отдельный метод. В конечном итоге это делает код короче. Эти маленькие методы используются повторно больше, чем вы изначально ожидали. Вы начинаете видеть возможности собирать их в отдельный класс. Вскоре вы думаете о своей проблеме на более высоком уровне, и все усилия идут намного быстрее. Новые функции легче писать, потому что вы можете использовать те маленькие классы, которые вы создали из этих маленьких методов.
источник
Класс с 5 открытыми и 25 закрытыми методами не кажется мне таким большим. Просто убедитесь, что у ваших классов есть четко определенные обязанности, и не беспокойтесь о количестве методов.
Тем не менее, эти частные методы должны быть сосредоточены на одном конкретном аспекте всей задачи, но не способом «doSomethingPart1», «doSomethingPart2». Вы ничего не получите, если эти частные методы - просто куски произвольно разделенной длинной истории, каждый из которых не имеет смысла вне всего контекста.
источник
Выдержка из Чистого кодекса Р. Мартина (стр. 176):
источник
Некоторые варианты:
Все в порядке. Просто назовите приватные методы так же, как и ваши публичные методы. И назовите свои публичные методы хорошо.
Абстрагируйте некоторые из этих вспомогательных методов в вспомогательные классы или вспомогательные модули. И убедитесь, что эти вспомогательные классы / модули названы правильно и являются самостоятельными абстракциями.
источник
Я не думаю, что когда-либо возникает проблема «слишком большого количества частных методов», если это так, вероятно, является признаком плохой архитектуры кода.
Вот как я об этом думаю ... Если архитектура хорошо спроектирована, то вообще очевидно, где должен быть код, который делает X. Это допустимо, если есть несколько исключений, но они должны быть действительно исключительными, в противном случае реорганизовать архитектуру, чтобы обрабатывать их как стандартные.
Если проблема заключается в том, что при открытии вашего класса он полон частных методов, которые разбивают большие куски на меньшие куски кода, то, возможно, это является признаком отсутствия архитектуры. Вместо классов и архитектуры эта область кода была реализована процедурным способом внутри одного класса.
Нахождение баланса здесь зависит от проекта и его требований. Противоположным аргументом является высказывание о том, что младший программист может собрать решение в 50 строк кода, что требует от архитектора 50 классов.
источник
Да.
В Python концепция «private» (используемая в C ++, Java и C #) на самом деле не существует.
Существует «своеобразное» соглашение об именах, но это все.
Приватный может привести к сложному для тестирования коду. Это также может нарушить принцип «Открыто-закрыто», просто делая код закрытым.
Следовательно, для людей, которые использовали Python, частные функции не имеют значения. Просто сделайте все публичным и покончите с этим.
источник