Il sovraccarico è un esempio del principio aperto / chiuso?


12

Dice Wikipedia

"le entità software (classi, moduli, funzioni, ecc.) dovrebbero essere aperte per l'estensione, ma chiuse per modifica"

La parola funzioni ha attirato la mia attenzione, e ora mi chiedo se possiamo supporre che la creazione di un sovraccarico per un metodo possa essere considerata o meno un esempio del principio aperto / chiuso?

Lasciami spiegare un esempio. Considera che hai un metodo nel tuo livello di servizio, che viene utilizzato in quasi 1000 posti. Il metodo ottiene userId e determina se l'utente è admin o meno:

bool IsAdmin(userId)

Ora considera che da qualche parte è necessario determinare se l'utente è admin o meno, in base al nome utente, non userId. Se cambiamo la firma del metodo sopra menzionato, allora abbiamo rotto il codice in 1000 punti (le funzioni dovrebbero essere chiuse per la modifica). Quindi possiamo creare un sovraccarico per ottenere il nome utente, trovare userId in base al nome utente e il metodo originale:

public bool IsAdmin(string username)
{
    int userId = UserManager.GetUser(username).Id;
    return IsAdmin(userId);
}

In questo modo, abbiamo esteso la nostra funzione creando un sovraccarico (le funzioni dovrebbero essere aperte all'estensione).

È un esempio di principio aperto / chiuso?

Risposte:


5

Interpreterei personalmente l'istruzione wiki in questo modo:

  • per una classe: sentiti libero di ereditare la classe e sostituirla o estenderne la funzionalità, ma non hackerare la classe originale cambiando così ciò che fa.
  • per un modulo (forse una libreria per esempio): sentiti libero di scrivere un nuovo modulo / libreria che avvolge l'originale, unisce le funzioni in versioni più facili da usare o estende l'originale con funzionalità extra, ma non modificare il modulo originale.
  • per una funzione (cioè una funzione statica, non un metodo di classe): il tuo esempio è ragionevole per me; riutilizzare la funzione IsAdmin originale (int) all'interno del nuovo IsAdmin (stringa). l'originale non cambia, la nuova funzione estende la sua funzionalità.

il colpo nel piede tuttavia è che se il tuo codice utilizza la classe 'cUserInfo' in cui risiede IsAdmin (int), stai essenzialmente infrangendo la regola e cambiando la classe. la regola verrebbe purtroppo mantenuta solo se si creasse una nuova classe cUserWithNameInfo: classe cUserInfo pubblica e si inserisse il proprio IsAdmin (stringa). Se possedessi la base di codici non obbedirei mai alla regola. Direi bollocks e apporterò semplicemente la modifica che suggerisci.


3

Prima di tutto, il tuo esempio è sfortunatamente inventato. Non lo faresti mai nel mondo reale, perché provoca un inutile double-get. O peggio, perché userid e username potrebbero diventare lo stesso tipo ad un certo punto. Piuttosto che

public bool IsAdmin(int userid)
{
    User user = UserManager.GetUser(userid);
    return user.IsAdmin();
}

public bool IsAdmin(string username)
{
    int userId = UserManager.GetUser(username).Id;
    return IsAdmin(userId);
}

Dovresti davvero estendere quella classe.

public bool IsAdmin(int userid)
{
    User user = UserManager.GetUserById(userid);
    return user.IsAdmin();
}

public bool IsUsernameAdmin(string username)
{
    User userId = UserManager.GetUserByName(username);
    return user.IsAdmin();
}

È possibile effettuare il refactoring e modificare il nome del primo metodo in IsUserIdAdmin, per coerenza, ma non è necessario. Il punto è che, aggiungendo il nuovo metodo e garantendo che non venga violato alcun codice chiamante, hai trovato la tua classe estensibile. O in altre parole, aperto all'estensione .

Ed ecco la cosa con il principio aperto-chiuso. Non sai mai davvero quanto il tuo codice sia conforme fino a quando non provi a estenderne una parte e ti ritrovi a dover invece modificare (con il rischio maggiore che ne deriva). Ma, con l'esperienza, impari a prevedere.

Come dice lo zio Bob (enfatizzare il mio):

Poiché la chiusura non può essere completa, deve essere strategica. Cioè, il designer deve scegliere i tipi di modifiche rispetto alle quali chiudere il suo design. Ciò richiede una certa quantità di presenze derivata dall'esperienza . Il progettista esperto conosce abbastanza bene gli utenti e l'industria per giudicare la probabilità di diversi tipi di modifiche. Si assicura quindi che il principio aperto-chiuso sia invocato per le modifiche più probabili.


Grazie per la tua buona spiegazione Ma avere 1000 viaggi di andata e ritorno o di andata e ritorno non è il punto principale qui. Quindi dimentichiamoci delle prestazioni al momento. Tuttavia, quello che ho fatto è stato anche quello di estendere la classe, perché ho aggiunto un altro metodo, sebbene con lo stesso nome, ma con parametri di input diversi. Ma ho davvero apprezzato la tua citazione di zio Bob . +1. ;)
Saeed Neamati,

@SaeedNeamati: Il punto che ho sollevato è che non importa se stai aggiungendo un metodo che utilizza un metodo esistente o aggiungendo un metodo che è completamente indipendente, in entrambi i casi la tua classe era aperta all'estensione. Tuttavia, se per ottenere ciò è necessario modificare l'interfaccia del metodo esistente, non si è chiusi alla modifica.
pdr

2

I moduli conformi al principio aperto-chiuso hanno due attributi primari.

  1. Sono "Open For Extension". Ciò significa che il comportamento del modulo può essere esteso. Che possiamo fare in modo che il modulo si comporti in modi nuovi e diversi quando cambiano i requisiti dell'applicazione o per soddisfare le esigenze di nuove applicazioni.
  2. Sono "Chiusi per modifica". Il codice sorgente di tale modulo è inviolato. A nessuno è permesso apportare modifiche al codice sorgente.
  1. Sovraccaricando il tuo metodo estendi le funzionalità del modulo esistente, soddisfando così le nuove esigenze della tua applicazione

  2. Non stai apportando alcuna modifica a un metodo esistente, quindi non stai violando la seconda regola.

Sarei interessato a sentire ciò che gli altri hanno da dire sul non consentire di cambiare il metodo originale. Sì, è possibile inserire modificatori di accesso appropriati e applicare l'incapsulamento, ma cos'altro si può fare?

Rif: http://www.objectmentor.com/resources/articles/ocp.pdf


1

Il principio aperto-chiuso è un obiettivo, un caso ideale, non sempre una realtà. Soprattutto quando si cerca di refactificare il vecchio codice, il primo passo è spesso una pesante modifica per rendere possibile l'OCP. La radice del principio è che il codice di lavoro funziona già, e la modifica probabilmente introduce dei bug. Pertanto, lo scenario migliore non è modificare il codice esistente, ma solo aggiungere nuovo codice.

Ma supponiamo che tu abbia una funzione chiamata BigContrivedMethod(int1, int2, string1). BigContrivedMethodfa tre cose: cosa1, cosa2 e cosa3. A questo punto, riutilizzare BCM è probabilmente difficile, perché fa troppo. Rifattorandolo (se possibile) in ContrivedFunction1(int), ContrivedFunction2(int)e ContrivedFunction3(string)ti offre tre metodi più piccoli e più mirati che puoi combinare più facilmente.

E questa è la chiave dell'OCP per quanto riguarda metodi / funzioni: composizione. "Estendi" le funzioni chiamandole da altre funzioni.

Ricorda che OCP fa parte di altri 5 principi, le linee guida SOLID. Quella prima è la chiave, singola responsabilità. Se tutto nella tua base di codice ha fatto solo una cosa specifica che doveva fare, non avresti mai bisogno di modificare il codice. Dovresti solo aggiungere un nuovo codice o combinare il vecchio codice insieme in nuovi modi. Poiché il codice reale raramente soddisfa tale linea guida, spesso è necessario modificarlo per ottenere SRP prima di poter ottenere OCP.


Penso che qui parli del Responsabile unico responsabile, non dell'OCP.
Saeed Neamati,

1
Sto dicendo che non puoi realisticamente avere OCP senza SRP. Il codice che fa troppo non può essere esteso, non può essere riutilizzato. SOLID è quasi scritto nell'ordine di importanza, con ogni principio che fa affidamento su quelli prima di essere fattibile.
CodexArcanum,

Questa risposta dovrebbe essere più votata.
Ashish Gupta,

0

Stai seguendo il principio aperto-chiuso se stai modificando il comportamento del tuo programma scrivendo nuovo codice anziché alterare il vecchio codice.

Dal momento che è praticamente impossibile scrivere un codice che sia aperto a ogni possibile cambiamento (e non lo si desidera perché si immette la paralisi dell'analisi) si scrive codice che risponde a tutti i diversi comportamenti su cui si sta attualmente lavorando per estensione piuttosto che per modifica.

Quando ti imbatti in qualcosa che devi cambiare, invece di semplicemente alterare qualcosa per consentire il nuovo comportamento, capisci qual è il punto di cambiamento, ristrutturi il tuo programma senza alterare il suo comportamento in modo da poterlo cambiare scrivendo NUOVO codice .

Quindi, come si applica al tuo caso:

Se stai aggiungendo nuove funzioni alla tua classe, allora la tua classe non è aperta / chiusa ma lo sono i client della funzione che stai sovraccaricando.

Se stai semplicemente aggiungendo nuove funzioni che funzionano sulla tua classe, allora sia essa che i client della tua funzione sono aperti / chiusi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.