How do professional programmers make judgement call on whether to go
for OOP or not? It would be really helpful for me.
Per me, ci sono due punti decisionali. In primo luogo, a volte sarà ovvio all'inizio. Ci saranno molti tipi simili che condividono tutti i metodi comuni che differiscono ampiamente nei loro dettagli di implementazione. Ad esempio, stavo creando un sistema di flusso di lavoro e avevo bisogno della capacità di implementare attività arbitrarie. Per eseguire l'attività, ho implementato una classe base da cui ogni attività è stata ereditata, con un metodo astratto Execute()
. Le classi ereditanti hanno fornito l'implementazione, ma il sistema del flusso di lavoro potrebbe iniziare l'esecuzione senza sapere nulla sul tipo di attività in esecuzione.
La maggior parte dei progetti non è così chiara. Il secondo punto di decisione è quando un sottoinsieme del progetto è cresciuto in un enorme groviglio di istruzioni if-then o di istruzioni switch-case, e specialmente quando quelle istruzioni if-then richiedono un sacco di codice di installazione per funzionare correttamente. Sento che sto iniziando a perdere la logica di quello che sto cercando di realizzare, e il codice inizia a sentirsi fragile. A quel punto, di solito è un segno che è il momento di rifattorizzare il codice in classi base con implementazioni specifiche.
Gran parte del passaggio a uno stile orientato agli oggetti rispetto a uno stile funzionale è la conversione delle istruzioni if-then in istruzioni "esegui questa azione". Invece di un enorme insieme di istruzioni if-then, devi solo 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 a qualcosa di simile:
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'è un codice molto piccolo dentro l'if-then, questo sembra un sacco di lavoro per non avere alcun beneficio. E quando c'è un codice molto piccolo nell'e-then, è corretto: è molto lavoro per il codice che è più difficile da capire.
Ma lo stile orientato agli oggetti brilla davvero quando hai più di un'azione rispetto al solo metodo Confirm()
che deve essere eseguito. Forse hai una routine di inizializzazione, tre o più metodi di azione che possono essere eseguiti e un metodo Cleanup()
. L'algoritmo di base è identico, tranne che fa le sue chiamate negli oggetti appropriati che implementano una classe base comune. Ora si inizia 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 modo.