In generale preferisco composition
a inheritance
. Questo ha diversi motivi:
-
Gli esseri umani sono cattivi nell'affrontare la complessità. E occuparsi di alberi ad alto patrimonio è la complessità. Voglio strutture leggere sul mio cervello, che potrei trascurare facilmente. Lo stesso vale per dozzine di tipi anche derivati da una classe base.
-
Sei in grado di scambiare parti in movimento . Potresti avere un dispositivo semplice, che fa runOperatingSystem()
, invece di creare due diversi% di% di memoria, fai solo uno e dagli un device
. Se vuoi testare il comportamento, puoi iniettare un Operating System
e vedere, se lo fa, che cosa dovrebbe.
-
Sei in grado di estendere il comportamento, semplicemente iniettando più componenti comportamentali.
In termini di mockOperatingSystem
, stai meglio, progettando un tipo di dispositivo generico :
In Python il design sarebbe simile al seguente
class Device:
def __init__(self, OS, name):
self.OS=OS
self.name=name
def browseInterNet(self):
self.OS.browseInterNet()
class Android:
def browseInterNet(self):
print("I'm browsing")
Hai un generico abstraction
che esegue un device
. Ogni interazione utente è delegata a questo operating system
. Forse vuoi testare solo la chiamata navigazione che dipende da qualsiasi sistema operativo, puoi facilmente sostituirlo.
Il prossimo passo per questo disegno sarebbe, per creare un OS
-object, che prende i parametri comuni ( configuration
, CreationDate
e così via). Inietta questo HardwareId
e il configuration
appropriato tramite l'iniezione del costruttore e hai finito.
Definisci comportamento comune in un contratto ( OS
), che determina cosa potresti fare con un telefono e il sistema operativo si occupa dell'implementazione .
Tradotto nella lingua di tutti i giorni: se scrivi messaggi con il tuo telefono, non c'è differenza nel farlo con un iPhone, Android o Windows a tale riguardo, che tu sei texting , anche se i meccanismi del SO a OS differiscono. Il modo in cui si comportano tecnicamente non è interessante per il dispositivo. Il sistema operativo esegue interface
, che a sua volta si occupa della sua implementazione. È tutta una questione di elementi comuni di astrazione .
D'altra parte: questo è solo il modo uno di farlo. Dipende da te e dal tuo modello del dominio.
Dai commenti :
But you have to agree with me that this would require one to create a lot of wrapper methods to make the API simpler
Dipende da ciò che esattamente vuoi modellare. Per estendere l'esempio dato di texting app
:
Dire, hai semplicemente alcuni lavori di base, vuoi che il dispositivo faccia, definisci un texting
per quello; nel nostro caso, semplicemente il metodo API
. Hai quindi sendSMS(text)
, in cui viene richiamato il messaggio Device
. Il "send text"
a sua volta delega quella chiamata al sistema operativo utilizzato, che esegue l'invio effettivo.
Se vuoi modellare più di una manciata di device
delle offerte services
, devi fare un eccesso di device
.
Questo è un segno che il tuo livello di astrazione è troppo basso.
Il prossimo passo sarebbe quello di astrarre il concetto di un'app dal sistema.
In linea di principio, hai un API
che interagisce con la logica interna di device
, che esegue su un app
. Hai operating system
, che è elaborato e modifica il input
del dispositivo.
In termini di MVC , ad es. la tua tastiera è display
, il display è controller
e c'è l' view
all'interno dell'app, che viene modificata. E poiché la vista osserva il modello riflette eventuali cambiamenti.
Con un concetto così astratto, sei molto flessibile per costruire / modellare molti casi d'uso.
I also am not seeing exactly how you would handle the operations concept here. There are still many types of operations, each with differing parameters, that can or can't be applied to a given device.
Come detto sopra: è tutta una questione di astrazione. Più potenza vuoi, più il tuo modello deve essere astratto.
Where would the OS be located now after this abstraction?
Prendendo ulteriormente l'esempio, devi sviluppare diverse astrazioni / schemi, che aiutano in questo caso.
-
model
-Pattern: il sistema operativo funge da mediatore, cioè prende segnali in forma di Mediator
, lo invia all'app e prende in risposta commands
ad es. aggiorna la vista
-
commands
-Pattern: il pattern di comando è la forma dell'astrazione, che viene utilizzata per descrivere il flusso di comunicazione tra i componenti. Supponiamo che l'utente preme Command
, che potrebbe essere astratto come A
-comando con un valore di Keypressed
. Un altro comando sarebbe A
con il valore di update display
o in questo caso keypress.value
.
-
A
Il display come MVC
, la tastiera come view
e tra il modello (app-).
La tua immaginazione è il tuo limite.
Questo è il tipo di materiale, OOP è stato originariamente inventato: simulazione di componenti indipendenti e loro interazione