У меня есть класс, который устанавливает массив узлов и соединяет их друг с другом в виде графа. Это лучше всего:
- Сохраняйте функциональность для инициализации и соединения узлов в одной функции
- Имейте функции инициализации и подключения в двух разных функциях (и имейте зависимый порядок, в котором функции должны быть вызваны - хотя имейте в виду, что эти функции являются частными.)
Метод 1: (Плохо, что одна функция делает две вещи, НО сохраняет зависимые функции сгруппированными - узлы никогда не должны соединяться без предварительной инициализации.)
init() {
setupNodes()
}
private func setupNodes() {
// 1. Create array of nodes
// 2. Go through array, connecting each node to its neighbors
// according to some predefined constants
}
Способ 2: (лучше в том смысле, что он самодокументируется, но connectNodes () никогда не следует вызывать перед setupNodes (), поэтому любой, кто работает с внутренними компонентами класса, должен знать об этом порядке.)
init() {
setupNodes()
}
private func setupNodes() {
createNodes()
connectNodes()
}
private func createNodes() {
// 1. Create array of nodes
}
private func connectNodes() {
// 2. Go through array, connecting each node to its neighbors
// according to some predefined constants
}
Рад слышать любые мысли.
Ответы:
Проблема, с которой вы имеете дело, называется временной связью
Вы правы, заботясь о том, насколько понятен этот код:
Я могу догадаться, что там происходит, но скажите мне, если это делает то, что еще происходит, немного яснее:
Это имеет дополнительное преимущество, заключающееся в том, что он менее связан с изменением переменных экземпляра, но для меня удобочитаемость - это номер один.
Это делает
connectNodes()
зависимость от узлов явной.источник
Отдельные функции по двум причинам:
1. Частные функции являются частными именно для этой ситуации.Ваша
init
функция общедоступна, а ее интерфейс, поведение и возвращаемое значение - это то, что вам нужно беспокоиться о защите и изменении. Результат, который вы ожидаете от этого метода, будет одинаковым независимо от того, какую реализацию вы используете.Поскольку остальная часть функциональности скрыта за этим приватным ключевым словом, она может быть реализована так, как вам нравится ... так что вы также можете сделать ее красивой и модульной, даже если один бит зависит от того, какой другой вызывается первым.
2. Соединение узлов друг с другом не может быть частной функциейЧто если в какой-то момент вы захотите добавить другие узлы в массив? Вы уничтожаете имеющуюся настройку и полностью инициализируете ее? Или вы добавляете узлы в существующий массив и затем запускаете
connectNodes
снова?Возможно,
connectNodes
может иметь вменяемый ответ, если массив узлов еще не был создан (сгенерировать исключение, вернуть пустой набор, вы должны решить, что имеет смысл для вашей ситуации).источник
Вы также можете обнаружить (в зависимости от сложности каждой из этих задач), что это хороший шов для разделения другого класса.
(Не уверен, что Swift работает таким образом, но псевдокод :)
Это разделяет обязанности по созданию и изменению узлов на отдельные классы:
NodeGenerator
заботится только о создании / извлечении узлов, аYourClass
заботится только о подключении тех узлов, которые ему даны.источник
В дополнение к тому, что это является точной целью частных методов, Swift дает вам возможность использовать внутренние функции.
Внутренние методы идеально подходят для функций, которые имеют только один сайт вызова, но чувствуют, что они не оправдывают отдельную частную функцию.
Например, очень часто есть общедоступная рекурсивная функция «entry», которая проверяет предварительные условия, устанавливает некоторые параметры и делегирует частную рекурсивную функцию, которая выполняет эту работу.
Вот пример того, как это может выглядеть в этом случае:
Обратите внимание на то, как я использую возвращаемые значения и параметры для передачи данных, а не изменяю общее состояние. Это делает поток данных намного более очевидным на первый взгляд, без необходимости переходить к реализации.
источник
Каждая функция, которую вы объявляете, несет в себе бремя добавления документации и ее обобщения, чтобы ее можно было использовать в других частях программы. Это также несет бремя понимания того, как другие функции в файле могут использовать его для тех, кто читает код.
Однако, если он не используется другими частями вашей программы, я бы не стал представлять его как отдельную функцию.
Если ваш язык поддерживает это, вы все равно можете иметь одно целое с помощью вложенных функций
Место объявления имеет очень большое значение, и в приведенном выше примере ясно, не требуя каких-либо дополнительных указаний, что внутренние функции предназначены для использования только в теле внешней функции.
Даже если вы объявляете их как частные функции, я предполагаю, что они все еще видны всему файлу. Таким образом, вам необходимо объявить их близко к объявлению основной функции и добавить некоторую документацию, поясняющую, что они должны использоваться только внешней функцией.
Я не думаю, что вы должны строго делать одно или другое. Лучшее, что можно сделать, зависит от конкретного случая.
Разбиение его на несколько функций, безусловно, добавляет непонимание, почему существует 3 функции и как они все работают друг с другом, но если логика сложная, то эти дополнительные издержки могут быть намного меньше, чем простота, представленная разбиванием сложной логики. на более простые части.
источник
private
разрешает доступ только внутри вложенного типа (struct / class / enum), в то время какfileprivate
разрешает доступ ко всему файлу