In che modo i programmatori professionisti fanno appello al giudizio sull'opportunità o meno di OOP? Sarebbe davvero utile per me.
Per me ci sono due punti decisionali. Innanzitutto, a volte sarà ovvio all'inizio. Ci saranno molti tipi simili che condividono tutti metodi comuni che differiscono ampiamente nei dettagli di implementazione. Ad esempio, stavo costruendo un sistema di flusso di lavoro e avevo bisogno della capacità di implementare attività arbitrarie. Per eseguire l'attività, ho implementato una classe di base da cui ogni attività ereditata, con un Execute()
metodo astratto. Le classi ereditarie hanno fornito l'implementazione, ma il sistema del flusso di lavoro ha potuto iniziare l'esecuzione senza sapere nulla sul tipo di attività in esecuzione.
La maggior parte dei progetti non è così chiara. Il secondo punto decisionale è quando un sottoinsieme del progetto si è trasformato in un enorme groviglio di istruzioni if-then o istruzioni switch-case, e specialmente quando quelle istruzioni if-then richiedono molto codice di installazione per funzionare correttamente. Mi sento iniziando a perdere la logica di ciò che sto cercando di realizzare e il codice inizia a sembrare fragile. A quel punto, di solito è un segno che è tempo di refactoring del codice in classi base con implementazioni specifiche.
Una grande parte del passaggio a uno stile orientato agli oggetti invece di uno stile funzionale sta convertendo le istruzioni if-then in istruzioni "esegui questa azione". Invece di un enorme set di istruzioni if-then, basta dire al codice di eseguire la sua azione. L'azione effettivamente eseguita dipende dall'implementazione fornita.
Ad esempio, ecco lo stile funzionale nello pseudocodice in stile C #:
if ( action == "email" ) {
callEmailFunction(userInfo);
}
else if ( action == "sms" ) {
callSmsFunction(userInfo);
}
else if ( action == "web" ) {
endpoint = "http://127.0.0.1/confirm";
confirmWeb(endpoint, userinfo);
}
...
Ma forse potresti riscriverlo in qualcosa del genere:
interface IConfirmable {
void Confirm(UserInfo userinfo);
}
public class ConfirmEmail : IConfirmable {
public void Confirm(UserInfo userinfo) {
// do the appropriate thing to confirm via email
}
}
public class ConfirmSms : IConfirmable {
public void Confirm(UserInfo userinfo) {
// do the appropriate thing to confirm via email
}
}
public class ConfirmWeb : IConfirmable {
// this is a constructor
public ConfirmWeb(string endpoint) {
...
}
public void Confirm(UserInfo userinfo) {
// do the appropriate thing to confirm via web
}
}
E poi il codice stesso:
// An implementation that decides which implementation of the base class to use
// This replaces the if-then statements in the functional programmming.
IConfirmable confirmer = ConfirmerFactory.GetConfirmer();
// get the userinfo however you get it,
// which would be the same way you get it in the functional example.
UserInfo userinfo = ...;
// perform the action.
confirmer.Confirm(userinfo);
Ora, quando c'è molto poco codice all'interno dell'if-allora, questo sembra un sacco di lavoro per non trarne alcun vantaggio. E quando c'è molto poco codice nell'if-allora, è corretto: c'è molto lavoro per il codice che è più difficile da capire.
Ma lo stile orientato agli oggetti brilla davvero quando hai più di un'azione che solo il Confirm()
metodo che deve essere eseguito. Forse hai una routine di inizializzazione, tre o più metodi di azione che possono essere eseguiti e un Cleanup()
metodo. L'algoritmo di base è identico, tranne per il fatto che effettua le chiamate negli oggetti appropriati che implementano una classe base comune. Ora inizi a vedere un vero vantaggio per lo stile orientato agli oggetti: l'algoritmo di base è molto più facile da leggere rispetto a se stesse controllando le istruzioni if-then in ogni fase del processo.