Che tipo di domande determinano se un codice simile è separato o comune?

4

Ho incontrato il seguente problema molte volte in varie forme nella mia carriera di programmatore. Come semplice esempio, considera quanto segue:

Diciamo che esiste un metodo (B) che potrebbe differire in base alle esigenze del cliente. Questo potrebbe essere per un numero qualsiasi di motivi. Il buon senso ti dice che come stanno le cose ora, dovresti scriverli come metodo comune e aggiungere il codice di diramazione in futuro per gestire eventuali casi limite futuri.

Customer 1: A-->B-->C

Customer 2: A-->B-->C

Tuttavia, la tua esperienza ti dice che è probabile che questo particolare bit di codice cambi, quindi sarebbe più facile avere metodi separati (B1 e B2) e cambiare la logica per un cliente piuttosto che aggiungere continuamente il metodo di creazione del codice derivato ( B) non generico.

Customer 1: A-->B1-->C

Customer 2: A-->B2-->C

Ci sono delle regole empiriche che possono essere applicate in questi casi o ogni caso deve essere preso nel merito?

Per inciso, ho incontrato un sacco di persone che sostengono fermamente un approccio rispetto all'altro e posso vedere i meriti di entrambi. Ma sicuramente c'è una via di mezzo che può essere raggiunta sulla base di argomenti ragionati?

    
posta Robbie Dee 11.03.2013 - 12:51
fonte

5 risposte

4

In generale, la logica aziendale tende ad essere simile per coincidenza. D'altra parte, la logica del programma (che indica la struttura e il flusso della tua applicazione) tende ad essere simile per convenzione.

Se pensi a diversi tipi di entità di business, avranno tutti un metodo fileTaxReturn ('tis the season), ma c'è ben poco motivo per loro di utilizzare lo stesso metodo perché il modo in cui un file LLC le sue tasse sono completamente diverse dal modo in cui una C Corp deposita le tasse. Questa è la logica aziendale.

D'altra parte, se si ha una sorta di comportamento (forse è un emettitore di eventi che viene agganciato per la registrazione) che fa parte della logica del programma, è molto probabile che questa sia una convenzione dell'applicazione e possa essere riutilizzata.

Ci sono due aspetti orientati alla manutenzione di questa domanda.

  1. Polimorfismo implicito - per la logica di business, spesso le volte ha molto senso per prematuramente astratto, perché altrimenti, aggiungendo altri casi di base, tutti i metodi diventeranno brutti cambiare le istruzioni. Trasformando i casi aziendali in scenari precoci, rende l'intento visibile e impedisce l'interruzione dell'istruzione.

  2. Astrazione prematura - D'altra parte, se stai dicendo a te stesso "Forse vorremmo accedere al database, ma a volte a un file piatto, e altre volte a un feed RSS, e forse Twitter , o Facebook, o una porta seriale ... ", stai facendo un'astrazione prematura - creando una complessità inutile in un sistema e una confusione totale per il programmatore di manutenzione. Il codice ben incapsulato è facile da refactoring e non soffrirà di una mancanza di astrazione.

risposta data 11.03.2013 - 14:52
fonte
5

Sì, ogni caso deve essere preso per il proprio merito, in quanto non esiste una regola generale che dice "tutti i rami devono essere nei loro stessi metodi".

La ramificazione deve essere eseguita in entrambi i casi. È solo questione di decidere se vuoi metterlo in B per il tuo primo esempio, o in A come nel tuo secondo. Decidere come fare si riduce a due cose:

  1. Leggibilità: il codice è più leggibile se si inserisce la ramificazione in A vs. B?
  2. Manutenibilità: il codice è più facile da mantenere se si inseriscono le diramazioni in A rispetto a B?

Il più delle volte, questi due vanno di pari passo. Il codice che è facile da capire tende ad essere più facile da cambiare. Inizia con il caso più semplice possibile (tutto il codice in un unico punto) e quindi estrai le informazioni finché non raggiungi un punto in cui il codice è breve e dolce, ma ancora abbastanza compatto per distinguere i dettagli.

Quando (se) hai bisogno di aggiungere B3 e B4, hai un candidato per introdurre qualcosa di un po 'più flessibile, come creare una superclasse comune e cosa no. Ma non prima.

    
risposta data 11.03.2013 - 13:17
fonte
5

Are there any rules of thumb that can be applied in such cases or does every case have to be taken on its merits?

Ci sono almeno una o due dozzine di diversi tipi di soluzioni per quel tipo di problema, e scegliere il "migliore" (qualunque cosa significhi) dipende dai dettagli della situazione specifica. Ciò non significa che non ci siano "regole empiriche" - ce ne sono molte, ma credo che sarebbe più logico tentare di elencarle qui in un unico post.

Detto questo, ecco alcune cose che potrebbero influenzare la tua decisione:

  • cosa "fa scattare" la ramificazione? Hai bisogno di un tempo di compilazione o di un runtime?
  • quanto sono grandi le differenze tra B1 e B2? Bisogna duplicare il codice in B1 e B2? Stai per duplicare il codice facendo "A - > B1 - > C" e "A - > B2 - > C" (che sarebbe una brutta cosa?)
  • B sta diventando "troppo grande" o "troppo complesso" (e perché B non si dirama semplicemente tra B1 e B2)?
  • usi OOP e parti A, B e C di una classe in cui l'applicazione di "modello metodo modello" può essere la cosa giusta da fare?
  • hai un linguaggio di programmazione a portata di mano dove invece di chiamare B1 o B2, puoi inserire una richiamata su B1 o B2 nel codice che deve chiamare B1 o B2?
  • la decisione "B1 vs. B2" deve essere presa in un solo posto nel codice, o ci sono molti posti in cui viene valutato il risultato di tale decisione (e il passaggio tra B1 e B2 deve essere fatto di nuovo)?
risposta data 11.03.2013 - 13:12
fonte
1

B è un comportamento. Quindi avere un'interfaccia per questo e introdurre nuove implementazioni, se necessario. Equipaggia diverse istanze di oggetti con differenti implementazioni quando le istanziate. In questo modo rispetti il Open Closed Principle , perché devi solo implementare un'interfaccia e configurarne l'utilizzo. Il proprietario di tale istanza di interfaccia non sarà influenzato dall'introduzione di nuove implementazioni dell'interfaccia.

In questo modo si separa anche la ramificazione, che è un problema di configurazione e l'effettiva esecuzione. Il codice diventerà molto più leggibile, perché non ci saranno rami durante l'esecuzione della logica di business.

    
risposta data 11.03.2013 - 16:04
fonte
0

KISS & YAGNI

... semplice e amp; I sistemi intuitivi sono in genere molto più facili da evolvere / adattare rispetto a sistemi complessi che cercano di coprire tutte le possibili "potenziali evoluzioni" in anticipo.

Se le differenze sono minori, vai per un metodo / oggetto / cosa comune.

Se le differenze sono grandi, implementale in metodo / oggetto / qualsiasi distinto.

I miei 2 centesimi

    
risposta data 11.03.2013 - 17:06
fonte

Leggi altre domande sui tag