Quando si crea software, è utile comprendere una vasta gamma di principi di progettazione. Capire come progettare un sistema con il principio più appropriato può risparmiare innumerevoli ore di sviluppo e mal di testa.

Parliamo prima dell’ereditarietà per un po’. L’ereditarietà è quando una classe eredita lo stato e / o il comportamento da una classe genitore. Diciamo che stiamo progettando un gioco, e ho bisogno di un cane:

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

Dopo un po’, ci rendiamo conto che il nostro software, come tutto, ha bisogno di Gatti, quindi creiamo una classe di gatti:

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

Perché la natura chiama, aggiungiamo .cacca () per il gatto e la classe cane:

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

In questo esempio, abbiamo due animali che sono in grado di fare la cacca. Sfortunatamente, entrambi forniscono implementazioni per poop(), quindi qui c’è qualche duplicazione di codice. quindi solleviamo .poop () in una classe Animale condivisa.

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

Ora che abbiamo un sacco di animali che cagano ovunque, abbiamo bisogno di un cleaningrobot:

CleaningRobot
.drive()
.clean()

Hai anche bisogno di un MurderRobot che può.guidare () e .uccidere() i cani e gatti che sono .cacca () ing su tutti i pavimenti bianchi:

MurderRobot
.drive()
.kill()

Da allora .drive () è ora duplicato tra CleaningRobot e MurderRobot creiamo una classe di robot per metterlo in.

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

Questo è l’aspetto dell’intera struttura:

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

“I nostri clienti richiedono un MurderRobotDog. Deve essere in grado di farlo .uccidere(), .Drive(), .bark (), ma non può fare la cacca ().

E ora, siamo fottuti. Semplicemente non possiamo adattarsi bene al MurderRobotDog in questa gerarchia di ereditarietà. Potremmo creare un nuovo oggetto genitore, in cui si inseriscono tutte le funzionalità condivise:

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

Ma ciò significa che i tuoi oggetti avranno una tonnellata di funzionalità che non usano, quindi finisci con un problema Gorilla/Banana — richiedi una banana, ma finisci con un gorilla che tiene la banana e l’intera giungla con essa.

Possiamo, tuttavia, modellarlo con i protocolli in Swift.In che modo i protocolli possono fornire un’astrazione migliore?

Un protocollo in Swift definisce metodi o proprietà che una classe può quindi adottare. Ecco un esempio:

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

Poiché le classi possono adottare più protocolli. La classe MurderRobotDog adotta il protocollo Barker, Killer and driver, il che significa che la classe MurderRobotDog fornisce implementazioni per bark(), kill(), and clean().

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

A partire da Swift 2.0, ora possiamo rimuovere la duplicazione del codice fornendo un’implementazione predefinita utilizzando un’estensione del protocollo:

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

Quindi abbiamo esaminato un esempio di un albero di ereditarietà che si è rotto, e poi abbiamo esaminato come ristrutturarlo usando il protocollo(interfaccia).

La domanda che probabilmente hai in mente ora è: quando usare ognuno? Bene the la stragrande maggioranza degli sviluppatori concorda sul fatto che dovremmo favorire l’interfaccia rispetto all’ereditarietà. Molte persone ti diranno che se qualcosa ha una relazione “è a”, allora dovresti usare l’ereditarietà. Ad esempio, Mattias “è un” uomo, quindi posso ereditare l’uomo. Se la relazione è di natura” ha una”, come una macchina” ha un ” motore, allora dovresti usare la composizione.

Conclusione

Quando si progetta un sistema, è importante scegliere il principio di progettazione giusto per il modello. In molte circostanze, è meglio usare l’interfaccia in primo luogo. È più flessibile, potente ed è anche molto facile da fare.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.