Tengo una clase que configura una matriz de nodos y los conecta entre sí en una estructura similar a un gráfico. ¿Es mejor:
- Mantenga la funcionalidad para inicializar y conectar los nodos en una función
- Tenga la funcionalidad de inicialización y conexión en dos funciones diferentes (y tenga un orden dependiente en el que deben llamarse las funciones, aunque tenga en cuenta que estas funciones son privadas).
Método 1: (Malo en que una función está haciendo dos cosas, PERO mantiene la funcionalidad dependiente agrupada: los nodos nunca deben conectarse sin inicializarse primero).
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
}
Método 2: (Mejor en el sentido de que es autodocumentado, PERO connectNodes () nunca debe llamarse antes que setupNodes (), por lo que cualquier persona que trabaje con los componentes internos de la clase debe saber sobre este orden).
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
}
Emocionado de escuchar cualquier pensamiento.
Respuestas:
El problema con el que está lidiando se llama acoplamiento temporal
Tiene razón en preocuparse por lo comprensible que es este código:
Puedo adivinar lo que está sucediendo allí, pero dime si esto aclara un poco más lo que está sucediendo:
Esto tiene el beneficio adicional de estar menos acoplado a la modificación de variables de instancia, pero para mí ser legible es el número uno.
Esto hace que
connectNodes()
la dependencia de los nodos sea explícita.fuente
Funciones separadas , por dos razones:
1. Las funciones privadas son privadas para exactamente esta situación.Su
init
función es pública, y su interfaz, comportamiento y valor de retorno es lo que necesita para preocuparse por proteger y cambiar. El resultado que espera de ese método será el mismo, independientemente de la implementación que utilice.Dado que el resto de la funcionalidad está oculta detrás de esa palabra clave privada, puede implementarse como desee ... por lo que podría hacerlo agradable y modular, incluso si un bit depende del otro que se llama primero.
2. La conexión de nodos entre sí podría no ser una función privada¿Qué sucede si en algún momento desea agregar otros nodos a la matriz? ¿Destruye la configuración que tiene ahora y la reinicia completamente? ¿O agrega nodos a la matriz existente y luego se ejecuta
connectNodes
nuevamente?Posiblemente
connectNodes
puede tener una respuesta sensata si la matriz de nodos aún no se ha creado (¿lanzar una excepción? ¿Devolver un conjunto vacío? Tiene que decidir qué tiene sentido para su situación).fuente
También puede encontrar (dependiendo de lo compleja que sea cada una de estas tareas) que esta es una buena costura para dividir otra clase.
(No estoy seguro si Swift funciona de esta manera pero con un pseudocódigo :)
Esto separa las responsabilidades de crear y modificar nodos para separar las clases:
NodeGenerator
solo se preocupa por crear / recuperar nodos, mientras queYourClass
solo se preocupa por conectar los nodos que se le dan.fuente
Además de ser el propósito exacto de los métodos privados, Swift le brinda la capacidad de usar funciones internas.
Los métodos internos son perfectos para funciones que tienen un solo sitio de llamada, pero sienten que no justifican ser funciones privadas separadas.
Por ejemplo, es muy común tener una función de "entrada" recursiva pública, que verifica las condiciones previas, establece algunos parámetros y delega en una función recursiva privada que hace el trabajo.
Aquí hay un ejemplo de cómo se vería eso en este caso:
Preste atención a cómo uso los valores y parámetros de retorno para pasar datos, en lugar de mutar un estado compartido. Esto hace que el flujo de datos sea mucho más obvio a primera vista, sin necesidad de saltar a la implementación.
fuente
Cada función que declara conlleva la carga de agregar documentación y generalizarla para que otras partes del programa puedan utilizarla. También conlleva la carga de comprender cómo otras funciones en el archivo pueden estar usándolo para alguien que lee el código.
Sin embargo, si no lo utilizan otras partes de su programa, no lo expondría como una función separada.
Si su idioma lo admite, aún puede tener una función-hace-una-cosa usando funciones anidadas
El lugar de la declaración es muy importante, y en el ejemplo anterior está claro, sin necesidad de más pistas, que las funciones internas están destinadas a ser utilizadas solo dentro del cuerpo de la función externa.
Incluso si los está declarando como funciones privadas, supongo que todavía son visibles para todo el archivo. Por lo tanto, deberá declararlos cerca de la declaración de la función principal y agregar cierta documentación que aclare que solo deben ser utilizados por la función externa.
No creo que tengas que hacer estrictamente uno u otro. Lo mejor que puede hacer varía caso por caso.
Desglosarlo en múltiples funciones sin duda agrega una sobrecarga de comprensión de por qué hay 3 funciones y cómo funcionan todas juntas, pero si la lógica es compleja, entonces esta sobrecarga adicional puede ser mucho menor que la simplicidad introducida al romper la lógica compleja en partes más simples.
fuente
private
permite el acceso solo dentro del tipo de cerramiento (struct / class / enum), mientras quefileprivate
permite el acceso en todo el archivo