Al crear software, es útil comprender una amplia gama de principios de diseño. Comprender cómo diseñar un sistema con el principio más apropiado puede ahorrarle incontables horas de desarrollo y dolor de cabeza.

Primero hablemos un poco de herencia. La herencia es cuando una clase hereda el estado y / o el comportamiento de una clase padre. Digamos que estamos diseñando un juego, y necesito un perro:

class Dog {
func bark(){
print("Bark")
}
}

Después de un tiempo, nos damos cuenta de que nuestro software, como todo, necesita Gatos, por lo que creamos una clase de gatos:

class Cat{
func .meow(){
print("Meow!")
}
}

Porque la naturaleza llama, añadimos .caca() para la clase de Gato y Perro:

class Dog {
func bark(){
print("Bark")
}
func poop(){
print("Poop")
}
}class cat{
func meow(){
print("Meow")
}
func poop(){
print("Poop")
}
}

En este ejemplo, tenemos dos animales que son capaces de hacer caca. Desafortunadamente, ambos proporcionan implementaciones para poop(), por lo que hay alguna duplicación de código aquí. así que levantamos .poop() en una clase de animal compartida.

Animal
.poop()Dog
.bark()Cat
.meow()

Ahora que tenemos muchos animales defecando en todas partes, necesitamos un bote de limpieza:

CleaningRobot
.drive()
.clean()

También necesitas un asesino que pueda .drive () y .matar() a los Gatos y Perros que son .caca por todo el suelo blanco:

MurderRobot
.drive()
.kill()

Desde entonces .drive () ahora está duplicado entre CleaningRobot y MurderRobot creamos una clase de Robot para colocarlo.

Robot
.drive()CleaningRobot
.clean()MurderRobot
.kill()

Así es como se ve toda la estructura:

Robot
.drive()CleaningRobot
.clean()MurderRobot
.kill()Animal
.poop()Dog
.bark()Cat
.meow()

«Nuestros clientes exigen un perro asesino. Tiene que ser capaz de hacerlo .matar(), .unidad(), .bark (), pero no puede hacer caca ().

Y ahora, estamos jodidos. Simplemente no podemos encajar bien al Perro Asesino en esta jerarquía de herencia. Podríamos crear un nuevo objeto padre, donde se pone toda la funcionalidad que se comparte:

GameObject
.bark()Robot
.drive()CleaningRobot
.clean()MurderRobot
.kill()MurderRobotDogAnimal
.poop()DogCat
.meow()

Pero eso significa que sus objetos tendrán una tonelada de funcionalidad que no usan, por lo que termina con un problema de Gorila/Plátano: solicita un plátano, pero termina con un gorila sosteniendo el plátano y toda la selva con él.

Sin embargo, podemos modelar esto con protocolos en Swift.¿Cómo pueden los protocolos proporcionar una mejor abstracción?

Un protocolo en Swift define métodos o propiedades que una clase puede adoptar. Aquí hay un ejemplo:

protocol Barker {
func bark()
}
protocol Pooper {
func poop()
}
protocol Driver {
func drive()
}
protocol Cleaner {
func clean()
}
protocol Killer {
func kill()
}

Dado que las clases pueden adoptar múltiples protocolos. La clase MurderRobotDog adopta el protocolo Barker, Killer and driver, lo que significa que la clase MurderRobotDog proporciona implementaciones para bark(), kill(), and clean().

class MurderRobotDog: Barker,Killer, Driver{
func bark() {
print("Bark!")
}
func driver() {
print("Drive!")
}
func killer() {
print("Kill!")
}}

A partir de Swift 2.0, ahora podemos eliminar la duplicación de código proporcionando una implementación predeterminada mediante una extensión de protocolo:

protocol Barker {
func bark()
}
extension Barker {
func bark() {
print("Bark!")
}
}class Dog: Barker{}
let myDog = Dog()
myDog.bark() // prints "Bark!"

Así que hemos visto un ejemplo de un árbol de herencia que se descompuso, y luego vimos cómo reestructurarlo usando el protocolo(interfaz).

La pregunta que probablemente esté en su mente ahora es: ¿cuándo usar cada uno? Bueno the la gran mayoría de los desarrolladores están de acuerdo en que debemos preferir la interfaz a la herencia. Mucha gente te dirá que si algo tiene una «es una» relación, entonces deberías usar la herencia. Por ejemplo, Matías «es un» hombre, por lo que puedo heredar al hombre. Si la relación es de una naturaleza «tiene», como un automóvil «tiene» un motor, entonces debe usar la composición.

Conclusión

Al diseñar un sistema, es importante elegir el principio de diseño correcto para su modelo. En muchas circunstancias, es mejor usar la interfaz en primer lugar. Es más flexible, potente y también muy fácil de hacer.

Deja una respuesta

Tu dirección de correo electrónico no será publicada.