Una buena dosis de autocrítica es fundamental para el crecimiento profesional y personal. Cuando se trata de programar, este sentido de la autocrítica requiere la capacidad de detectar patrones improductivos o contraproducentes en el diseño, el código, los procesos y el comportamiento. Por eso, el conocimiento de los antipatrones de diseño Swift es muy útil para cualquier programador.
Qué son los antipatrones de diseño Swift
Los antipatrones o trampas, son los claros ejemplos de malas soluciones a los problemas. Se estudian para poder evitarlos más adelante y, si es necesario, para poder reconocer fácilmente su presencia al investigar sistemas disfuncionales durante una auditoría.
El término antipatrón surge como el opuesto del término patrón, este se usa constantemente en programación para referirse a las buenas prácticas cuando se programa, se diseña o se gestionan sistemas. Así que podemos decir que un sistema “bien hecho” o un sistema ideal se forma con patrones y carece de antipatrones.
Entonces un antipatrón puede definirse como «una forma literaria que describe una solución recurrente que genera consecuencias decididamente negativas».
Esto puede generarse por una mala comprensión del problema, por la falta de conocimientos y la escasa experiencia en la resolución del tipo especial de problema, o simplemente por la aplicación de un patrón en un escenario que no es apropiado.
Pero desde una perspectiva más amplia, podemos ver los antipatrones de diseño Swift como una manera de detectar fallos, o un modo de descartar alternativas incorrectas para guiarnos hacia la elección de la alternativa óptima.
En el caso de los patrones, simplemente tienen un problema y una solución a ese problema. La esencia de un antipatrón es diferente, ya que tiene dos soluciones: una problemática, que genera consecuencias muy negativas; y otra refacturada, en la que se rediseña y transforma el problema, generando un escenario mucho más saludable.
Los antipatrones son métodos que permiten un mapeo eficiente entre una situación general y una clase de solución específica. Los antipatrones y los patrones están basados en un vocabulario para identificar, definir, debatir y conseguirle solución a los problemas eficientemente.
¿Por qué no usarlos?
Porque los antipatrones de diseño Swift en la arquitectura de programas son soluciones negativas, que presentan más problemas que soluciones, a pesar de que crean un camino rápido y fácil. Trata de analizarlo un poco siguiendo con esta analogía, si necesitas dinero, tienes dos opciones: el patrón (buen comportamiento) de trabajar duro o el antipatrón (rápido y con consecuencias a largo plazo) de robar un banco.
Los antipatrones más comunes
Los antipatrones de diseño Swift son muchos y son más comunes de lo que piensas, muchos programadores caen en ellos debido a que visualizan una alternativa realmente sencilla y veloz, sin embargo, las consecuencias se hacen notar al final del día. A continuación te explicamos brevemente algunos de ellos:
Force Unwrapping
Es muy conveniente forzar el desencapsulamiento de un opcional cuando crees que sabes que una variable no es nula. Este tipo de forzar el desencapsulamiento es muy tentador.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
protocol Pluralizable { func pluralize(forNumber number:Int?) -> String } class Mouse : Pluralizable { func pluralize(forNumber number:Int?) -> String { if number! == 1 { return "\(number!) mouse" } else { return "\(number!) mice" } } } |
Tienes una clase, Mouse, que quieres implementar un protocolo existente, Pluralizable por su conveniente API que incluye un método para la pluralización.
Sigues adelante e implementas este método, e intentas implementarlo con una simple sentencia if number > 1, pero obtienes un error de compilación sobre que number es opcional.
No se te ocurre ningún caso en el que number sea nulo, así que pones un signo de exclamación para forzar el desenvolvimiento, como sugiere Xcode, y sigues tu camino.
Forzar el argumento de la función definida por el protocolo es como poner una llamada a fatalError() en uno de tus métodos de la API. El protocolo definió el argumento como opcional, así que tu implementación necesita manejar ese caso.
Declarar View Controller Properties según sea necesario
Por ejemplo, considera un view Controller que muestra información sobre un animal determinado.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class AnimalInfoViewController : UIViewController { public var animalToDisplay:Animal! @IBOutlet weak var animalNameLbl: UILabel! @IBOutlet weak var animalColorLbl: UILabel! override public func viewDidLoad() { super.viewDidLoad() animalNameLbl.text = animalToDisplay.name animalColorLbl.text = animalToDisplay.color } ... } |
Debido a que este controlador de vista es parte de una aplicación iOS, y está conectado a un controlador de vista en un Storyboard, espera que animalToDisplay sea pasado desde su controlador de vista de presentación.
Esta es una buena expectativa, y una alternativa que puede llevar a otro antipatrón, el tener un singleton que almacena el animal que se muestra actualmente.
Sin embargo, hay un problema de mantenimiento aquí, ya que no hay manera de forzar al controlador de la vista de presentación para establecer animalToDisplay, por lo que viewDidLoad podría ser llamado sin animalToDisplay se establece y la aplicación se bloquearía.
Si estás usando Storyboards, no tienes la opción de usar un inicializador personalizado, así que tus dos opciones son darle a animalToDisplay un valor por defecto, o declararlo como opcional. Ambas podrían ser soluciones razonables dependiendo del caso de uso, y ambas son preferibles a tener una propiedad requerida en tu controlador de vista.
Massive View Controllers
Uno de los problemas conocidos de la arquitectura MVC en el desarrollo de iOS es Massive View Controller. ViewController es responsable de dos funciones fundamentales:
- presentar datos al usuario
- manejo de la interacción del usuario
Debido a que los controladores de vista en iOS tienen muchas responsabilidades y están estrechamente relacionados con las pantallas de la aplicación, se termina escribiendo una gran cantidad de código en ellos porque es más fácil y rápido de esa manera.
Ambiguous Naming
Es un antipatrón muy común entre los principiantes. Este antipatrón ocurre cuando se usan nombres ambiguos para las variables, funciones y clases. Es un problema muy grande. Porque algunos proyectos se hacen colectivamente (con miembros del equipo).
Entonces todos deben tener una idea tan pronto como ven el nombre de la variable, el nombre de la función o el nombre de la clase. Y puede ser difícil incluso para ellos mantener el programa cuando empleamos nombres ambiguos.
Magic String / Numbers
Comúnmente este antipatrón ocurre cuando utilizamos cadenas, números u otros datos de tipo sin inicializar. Es difícil saber qué ocurrió en esa posición del programa. Entonces podemos utilizar variables o constantes bien nombradas para esos datos para identificar mejor lo que sucedió.
A veces esos datos pueden utilizarse en diferentes lugares en el programa. Entonces es muy útil y flexible si empleamos variables bien nombradas para esos números o cadenas mágicas. Básicamente, consiste en usar números sin nombre o literales de cadena en lugar de constantes con nombre en el código.
Sin embargo, el principal problema es que la semántica del número o de la cadena literal está parcial o completamente oculta sin un nombre descriptivo u otra forma de anotación. Esto hace que la comprensión del código sea más difícil, y si es necesario cambiar la constante, la búsqueda y el reemplazo u otras herramientas de refactorización pueden introducir errores sutiles.
Los literales de cadena pueden parecer menos propensos a estos problemas, pero tener literales de cadena sin nombre en el código hace más difícil la internacionalización, y puede introducir problemas similares que tienen que ver con las instancias del mismo literal que tienen diferente semántica.
Por ejemplo, los homónimos en inglés pueden causar un problema similar con la búsqueda y el reemplazo; considera dos apariciones de «point», una en la que se refiere a un sustantivo (como en «she has a point») y la otra como un verbo (como en «to point out the differences…»).
Sustituir estos literales de cadena por un mecanismo de recuperación de cadenas que permita indicar claramente la semántica puede ayudar a distinguir estos dos casos, y también resultará útil cuando envíes las cadenas para su traducción.
Singleton
Singleton se basa en la premisa de que una clase solo debe tener una instancia, y debe imponer esta singularidad, pero la premisa es falsa porque el cliente de la clase, no la clase misma, está en posición de saber cuántas instancias se necesitan.
Además, romper la encapsulación y causar dificultades de inicialización no puede ser bueno para ningún conjunto de compensaciones de diseño. Dado el daño de diseño que inflige, singleton puede ser considerado un antipatrón(puedes leer más aqui)
Conclusión
Swift es un lenguaje potente que permite hacer mucho con las características modernas del lenguaje como los opcionales, las funciones como objetos de primera clase y las funciones de orden superior, y también incluye algunas funcionalidades relativamente novedosas como las extensiones y los valores de retorno múltiples.
Todo esto hace que el lenguaje sea gratificante de usar, pero esta riqueza de funcionalidad también puede prestarse a algunos malos hábitos, que son los antipatrones de diseño Swift, esperamos que con este breve artículo hayas aprendido un poco más sobre el tema y tengas cuidado para evitar caer en estos malos hábitos.
Comentarios recientes