Dovremmo evitare di usare modelli di design in progetti in costante evoluzione?


32

Un mio amico sta lavorando per una piccola azienda su un progetto che ogni sviluppatore odierebbe: è costretto a rilasciare il più rapidamente possibile, è l'unico a cui sembra interessare il debito tecnico, il cliente non ha esperienza tecnica, ecc.

Mi ha raccontato una storia che mi ha fatto riflettere sull'adeguatezza dei modelli di progettazione in progetti come questo. Ecco la storia.

Abbiamo dovuto mostrare prodotti in diversi punti del sito web. Ad esempio, i gestori dei contenuti possono visualizzare i prodotti, ma anche gli utenti finali o i partner tramite l'API.

A volte, i prodotti mancavano di informazioni: ad esempio, alcuni di essi non avevano alcun prezzo al momento della creazione del prodotto, ma il prezzo non era ancora stato specificato. Alcuni non avevano una descrizione (la descrizione era un oggetto complesso con storie di modifica, contenuto localizzato, ecc.). Alcuni mancavano di informazioni sulla spedizione.

Ispirato dalle mie recenti letture sui modelli di design, ho pensato che fosse un'ottima opportunità per usare il magico modello a oggetti nulli . Così l'ho fatto e tutto era liscio e pulito. Uno doveva solo chiamare product.Price.ToString("c")per visualizzare il prezzo o product.Description.Currentper mostrare la descrizione; nessuna richiesta condizionale. Fino a quando un giorno lo stakeholder ha chiesto di visualizzarlo in modo diverso nell'API, avendo unnull in JSON. E anche in modo diverso per i gestori dei contenuti mostrando "Prezzo non specificato [Modifica]". E ho dovuto uccidere il mio amato modello a oggetti nulli, perché non ce n'era più bisogno.

Allo stesso modo, ho dovuto rimuovere alcune fabbriche astratte e alcuni costruttori, ho finito per sostituire il mio bellissimo modello di facciata con chiamate dirette e brutte, perché le interfacce sottostanti sono cambiate due volte al giorno per tre mesi e anche il Singleton mi ha lasciato quando i requisiti dicevano che l'oggetto in questione doveva essere diverso a seconda del contesto.

Più di tre settimane di lavoro consistevano nell'aggiungere modelli di progettazione, poi nel separarli un mese dopo, e il mio codice divenne finalmente abbastanza spaghetti da essere impossibile da mantenere per chiunque, incluso me stesso. Non sarebbe meglio non usare mai questi schemi in primo luogo?

In effetti, ho dovuto lavorare su quei tipi di progetti in cui i requisiti cambiano costantemente e sono dettati da persone che non hanno in mente la coesione o la coerenza del prodotto. In questo contesto, non importa quanto tu sia agile, verrai con una soluzione elegante a un problema e quando finalmente lo implementerai, imparerai che i requisiti sono cambiati così drasticamente, che la tua soluzione elegante non si adatta non piu.

Quale sarebbe la soluzione in questo caso?

  • Non utilizzare alcun modello di progettazione, smettere di pensare e scrivere direttamente il codice?

    Sarebbe interessante fare un'esperienza in cui un team sta scrivendo direttamente il codice, mentre un altro ci pensa due volte prima di digitare, correndo il rischio di dover buttare via il design originale qualche giorno dopo: chissà, forse entrambi i team avrebbero il stesso debito tecnico. In assenza di tali dati, vorrei solo affermare che non è giusto digitare il codice senza pensarci prima quando si lavora su un progetto di 20 mesi al mese.

  • Mantenere lo schema di progettazione che non ha più senso e provare ad aggiungere altri schemi per la situazione appena creata?

    Neanche questo sembra giusto. I pattern sono usati per semplificare la comprensione del codice; metti troppi schemi e il codice diventerà un casino.

  • Cominciare a pensare a un nuovo design che racchiude le nuove esigenze, quindi adattare lentamente il vecchio design a quello nuovo?

    Come teorico e colui che favorisce Agile, sono totalmente coinvolto. In pratica, quando sai che dovrai tornare alla lavagna ogni settimana e ripetere gran parte del progetto precedente e che il cliente non ha abbastanza fondi per pagarti per quello, né abbastanza tempo per aspettare , questo probabilmente non funzionerà.

Quindi, qualche suggerimento?


43
Penso che questo sia un falso dilemma. Per ammissione del tuo amico, il codice è ora un piatto di spaghetti non mantenibile. Non è colpa dei modelli software; è l'incapacità del tuo amico di usare correttamente questi schemi, in modo da aumentare la manutenibilità, non diminuirla. Quando posso fornire esempi specifici, posterò una risposta adeguata.
Robert Harvey,

5
Inoltre, FWIW, qualsiasi cliente che non abbia una certa tolleranza per i costi alla deriva non dovrebbe probabilmente fare Agile, a meno che non ci siano delle quote integrate nei costi per i requisiti di spostamento.
Robert Harvey,

5
sospetto che senza gli schemi di progettazione, il codice avrebbe raggiunto uno stato non mantenibile molto prima
Steven A. Lowe,

28
Questa domanda non ha alcun senso. L'unico modo per "evitare schemi di progettazione" è di non scrivere alcun software. Cose come "XYZ Pattern" sono solo nomi assegnati a strategie di codifica comuni per consentire a noi programmatori di comunicare informazioni e consigli sulla struttura del codice e sulle scelte reciproche in modo più conveniente. A qualsiasi scelta di design nel tuo codice potrebbe essere assegnato un nome e chiamato "modello di progettazione", anche se non necessariamente ampiamente conosciuto (a meno che, suppongo, tu sia orgoglioso del tuo design unico e sufficientemente motivato per dargli un nome e un blog su o qualcosa del genere).
Jason C,

9
Cioè puoi evitare di chiamare il tuo piede "piede" ma hai ancora quel piede, è solo più difficile parlarne con qualcuno se non lo chiami "piede". Potresti pensare di "evitare modelli di design" ma se ti viene in mente un design decente che funziona, fai un passo indietro e dai un'occhiata, probabilmente scoprirai che il tuo design è appena finito per adattarsi a uno dei modelli nominati più comuni, che tu lo chiami o no.
Jason C,

Risposte:


86

Vedo alcune ipotesi errate in questa domanda:

  • il codice con schemi di progettazione, sebbene applicato correttamente, richiede più tempo per essere implementato rispetto al codice senza tali schemi.

I modelli di design non hanno fine a se stessi, dovrebbero servirti, non viceversa. Se un modello di progettazione non rende il codice più facile da implementare, o almeno meglio evolvibile (ciò significa: più facile da adattare ai mutevoli requisiti), allora il modello non ha il suo scopo. Non applicare schemi quando non rendono la "vita" più semplice per la squadra. Se il nuovo modello di oggetti Null stava servendo il tuo amico per il tempo in cui lo ha usato, allora tutto andava bene. Se dovesse essere eliminato più tardi, allora potrebbe anche essere ok. Se il modello di oggetti Null ha rallentato l'implementazione (corretta), l'utilizzo è stato errato. Nota, da questa parte della storia non si può concludere alcuna causa del "codice spaghetti" finora.

  • la colpa è del cliente perché non ha un background tecnico e non si preoccupa della coesione o della coerenza del prodotto

Non è né il suo lavoro né la sua colpa! Il tuo compito è preoccuparti della coesione e della coerenza. Quando i requisiti cambiano due volte al giorno, la soluzione non dovrebbe essere quella di sacrificare la qualità del codice. Devi solo dire al cliente quanto tempo impiega e se ritieni di aver bisogno di più tempo per ottenere il progetto "giusto", aggiungi un margine di sicurezza sufficiente a qualsiasi stima. Soprattutto quando hai un cliente che cerca di farti pressione, usa lo "Scotty Principle" . E quando si discute con un cliente non tecnico dello sforzo, evitare termini come "refactoring", "unit test", "schemi di progettazione" o "documentazione del codice" - che sono cose che non comprende e che probabilmente considerano "inutili assurdità "perché non vede alcun valore in essa. Parla sempre di cose che sono o almeno comprensibile per il cliente (funzionalità, funzioni secondarie, modifiche comportamentali, documenti utente, correzioni di errori, ottimizzazione delle prestazioni e così via).

  • la soluzione per i requisiti in rapida evoluzione è di cambiare rapidamente il codice

Onestamente, se "le interfacce sottostanti cambiano due volte al giorno per tre mesi", la soluzione non dovrebbe essere quella di reagire modificando il codice due volte al giorno. La vera soluzione è chiedere perché i requisiti cambiano così spesso e se è possibile apportare una modifica in quella parte del processo. Forse qualche ulteriore analisi iniziale aiuterà. Forse l'interfaccia è troppo ampia perché il confine tra i componenti è stato scelto in modo errato. A volte aiuta a chiedere maggiori informazioni su quale parte dei requisiti è stabile e quali sono ancora in discussione (e in realtà rimandare l'implementazione per le cose in discussione). E a volte alcune persone devono solo essere "prese a calci in culo" per non aver cambiato idea due volte al giorno.


30
+1: non è possibile risolvere i problemi delle persone con soluzioni tecniche.
Telastyn,

1
+1, ma "o almeno meglio evolvibile (ciò significa: più facile da adattare ai mutevoli requisiti)" - lo qualificherei con requisiti ragionevolmente mutevoli , giusto?
Fuhrmanator,

1
@Fuhrmanator: penso che sia difficile discutere in termini generali. IMHO è ovvio che nessun modello di progettazione ti aiuterà quando il tuo primo requisito è "abbiamo bisogno di un elaboratore di testi" e questo cambia in "abbiamo bisogno di un simulatore di volo". Per modifiche meno drastiche dei requisiti non è sempre facile decidere cosa ti aiuterà a mantenere il tuo software evolvibile. La cosa migliore è che IMHO non applica troppi modelli, ma alcuni principi - principalmente i principi SOLID e YAGNI. E sono d'accordo al 100% con Telastyn - quando i requisiti cambiano troppo spesso, probabilmente non è un problema tecnico.
Doc Brown,

4
+1 - "Onestamente, se" le interfacce sottostanti cambiano due volte al giorno per tre mesi ", la soluzione non dovrebbe essere quella di reagire modificando il codice due volte al giorno. La vera soluzione è chiedere perché i requisiti cambiano così spesso e se è possibile apportare un cambiamento in quella parte del processo. "Se ti vengono costantemente date nuove indicazioni, è meglio sedersi con tutte le parti interessate e riaffermare quali sono le aspettative. Risolvi le tue differenze e, si spera, non sprecare il tempo e il denaro di tutti dando al progetto un obiettivo più chiaro.
Krillgar,

1
@Cornelius Doc Brown ha detto che è difficile senza concretezza. I requisiti che vanno da un elaboratore di testi a un simulatore di volo non sarebbero ragionevoli; nessun modello di progettazione sarebbe d'aiuto. C'è un sacco di area grigia tra il suo esempio e, diciamo, l'aggiunta di un nuovo formato di file alla funzione Salva di un elaboratore di testi (che è molto ragionevole). Senza dettagli, è difficile da discutere. Inoltre, non è che uno non vorrebbe cambiare. È che tali cambiamenti sono difficili, se hai già fatto una scelta di progettazione in anticipo sulla base di un requisito. L'elaboratore di testi e la simulazione di volo sono un ottimo esempio di scelta anticipata.
Fuhrmanator,

43

La mia modesta opinione è che non dovresti evitare o non evitare l'uso di modelli di design.

I modelli di progettazione sono semplicemente soluzioni ben note e affidabili per problemi generali, a cui sono stati dati nomi. Non sono diversi in modo tecnico rispetto a qualsiasi altra soluzione o design che ti viene in mente.

Penso che la radice del problema potrebbe essere che il tuo amico pensa in termini di "applicazione o non applicazione di un modello di progettazione", invece di pensare in termini di "qual è la migliore soluzione che mi viene in mente, compresi ma non limitati ai modelli Lo so".

Forse questo approccio lo porta ad usare modelli in modo parzialmente artificiale o forzato, in luoghi in cui non appartengono. E questo è ciò che provoca un disastro.


13
+1 "I modelli di progettazione sono semplicemente soluzioni conosciute e affidabili per problemi generali, a cui sono stati dati nomi. Non sono diversi in modo tecnico rispetto a qualsiasi altra soluzione o progettazione che si possa pensare." Esattamente. Le persone restano così coinvolte nei modelli di design nominati che dimenticano che non sono altro che nomi assegnati a varie strategie per rendere più facile per noi programmatori comunicare tra loro sul nostro codice e sulle nostre scelte di design. Questo atteggiamento è molto spesso associato con il tentativo di forzare inappropriate "pattern" sui problemi che non necessariamente beneficiano - gran casino.
Jason C,

14

Nel tuo esempio di utilizzo del modello Null Object, credo che alla fine sia fallito perché ha soddisfatto le esigenze del programmatore e non quelle del cliente. Il cliente doveva visualizzare il prezzo in una forma adeguata al contesto. Il programmatore doveva semplificare parte del codice visualizzato.

Quindi, quando un modello di progettazione non soddisfa i requisiti, diciamo che tutti i modelli di progettazione sono una perdita di tempo o diciamo che abbiamo bisogno di un modello di progettazione diverso?


9

Sembrerebbe che l'errore fosse più quello di rimuovere gli oggetti del modello, che di usarli. Nella progettazione iniziale, l'oggetto null sembra aver fornito una soluzione a un problema. Questa potrebbe non essere stata la soluzione migliore.

Essere l'unica persona che lavora a un progetto ti dà la possibilità di sperimentare l'intero processo di sviluppo. Il grande svantaggio non è avere qualcuno come mentore. Prendersi del tempo per imparare e applicare le migliori o le migliori pratiche è probabile che ripaghi rapidamente. Il trucco sta nell'identificare quale pratica imparare quando.

Concatenare i riferimenti nel formato product.Price.toString ('c') viola la Legge di Demetra . Ho visto ogni sorta di problemi con questa pratica, molti dei quali riguardano i null. Un metodo come product.displayPrice ('c') potrebbe gestire i prezzi null internamente. Allo stesso modo product.Description.Current potrebbe essere gestito da product.displayDescription (), product.displayCurrentDescription (). o product.diplay ('Current').

La gestione del nuovo requisito per gestori e fornitori di contenuti deve essere gestita rispondendo al contesto. Esistono diversi approcci che possono essere utilizzati. I metodi di fabbrica potrebbero utilizzare diverse classi di prodotti in base alla classe utente in cui verranno visualizzati. Un altro approccio sarebbe che i metodi di visualizzazione della classe di prodotto costruissero dati diversi per utenti diversi.

La buona notizia è che il tuo amico si rende conto che le cose stanno sfuggendo di mano. Speriamo che abbia il codice nel controllo di revisione. Ciò gli consentirà di annullare le decisioni sbagliate, che inevitabilmente prenderà. Parte dell'apprendimento è provare approcci diversi, alcuni dei quali falliranno. Se riuscirà a gestire i prossimi mesi, potrebbe trovare approcci che semplificano la sua vita e puliscono gli spaghetti. Poteva provare a lavorare su una cosa ogni settimana.


2
Potrebbe anche indicare ulteriormente, visto che "devi" infrangere la legge di Demetra, che il modello usato in superficie era inadatto. Perché il "modello di visualizzazione" (usato in senso lato) non ha solo la descrizione da visualizzare? (Vale a dire perché ci sono più della semplice descrizione attuale a livello di interfaccia utente?) Il livello aziendale potrebbe preparare un oggetto riempito in modo appropriato al livello di interfaccia utente che ha già contenuti diversi a seconda che si tratti del gestore o meno.
Cornelius,

7

La domanda sembra essere sbagliata in così tanti punti. Ma i palesi sono:

  • Per il modello di oggetti null che hai citato, dopo che i requisiti sono cambiati, modifichi un po 'il codice. Va bene, ma non significa che "uccidi" il Null Object Pattern (a proposito, stai attento con le tue parole, sembra troppo estremo, alcune persone troppo paranoiche non lo vedranno affatto divertente).

Molte persone hanno giustamente affermato che i modelli di progettazione riguardano molto l'etichettatura e la denominazione di una pratica comune. Quindi pensa a una camicia, una camicia ha un colletto, per qualche motivo rimuovi il colletto o parte del colletto. La denominazione e l'etichettatura cambiano, ma è ancora una maglietta in sostanza. Questo è esattamente il caso qui, piccoli cambiamenti nei dettagli che non significano che tu abbia "ucciso" quel modello. (ricorda ancora la formulazione estrema)

  • Il design di cui hai parlato è negativo perché quando i cambiamenti dei requisiti arrivano come elementi secondari, apporti enormi cambiamenti di progettazione strategica in tutti i luoghi. A meno che il problema aziendale di alto livello non cambi, non si può giustificare una modifica sostanziale del design.

Dalla mia esperienza, quando arrivano requisiti minori, devi solo cambiare una piccola parte della base di codice. Alcuni potrebbero essere un po 'confusi, ma nulla di troppo serio per influire in modo sostanziale sulla manutenibilità o sulla leggibilità e spesso bastano poche righe di commento per spiegare la parte confusa. Anche questa è una pratica molto comune.


7

Facciamo una pausa per un momento e esaminiamo qui il problema fondamentale: progettare un sistema in cui il modello di architettura è troppo accoppiato a funzionalità di basso livello nel sistema, causando la rottura frequente dell'architettura nel processo di sviluppo.

Penso che dobbiamo ricordare che l'uso dell'architettura e dei modelli di progettazione ad esso correlati devono essere posti a un livello adeguato e che l'analisi di quale sia il livello giusto non è banale. Da un lato, potresti facilmente mantenere l'architettura del tuo sistema ad un livello troppo alto con solo vincoli molto basilari come "MVC" o simili, che possono portare a opportunità mancate come in linee guida chiare e leva del codice e dove il codice spaghetti può facilmente prosperare in tutto quello spazio libero.

D'altra parte, potresti anche sovra-progettare il tuo sistema, come nell'impostare i vincoli a un livello dettagliato, dove presumi di poter fare affidamento su vincoli che in realtà sono più volatili di quanto ti aspetti, rompendo costantemente i tuoi vincoli e costringendoti a rimodellare e ricostruire costantemente, fino a quando non inizi a disperare.

Le modifiche ai requisiti di un sistema saranno sempre presenti, in misura minore o maggiore. E i potenziali vantaggi dell'utilizzo di modelli di architettura e design saranno sempre lì, quindi non si tratta davvero di utilizzare i modelli di design o meno, ma a quale livello si dovrebbero usare.

Ciò richiede non solo di comprendere gli attuali requisiti del sistema proposto, ma anche di identificare quali aspetti di esso possono essere visti come proprietà fondamentali stabili del sistema e quali proprietà potrebbero essere soggette a modifiche nel corso dello sviluppo.

Se scopri che devi costantemente combattere con il codice spaghetti non organizzato, probabilmente non stai facendo abbastanza architettura, o ad un livello elevato. Se scopri che la tua architettura si rompe frequentemente, probabilmente stai facendo un'architettura troppo dettagliata.

L'uso di modelli di architettura e design non è qualcosa in cui puoi semplicemente "rivestire" un sistema, come se dipingessi una scrivania. Sono tecniche che dovrebbero essere applicate in modo ponderato, a un livello in cui i vincoli su cui si deve fare affidamento hanno un'alta possibilità di essere stabili e dove queste tecniche in realtà valgono la pena di modellare l'architettura e implementare gli attuali vincoli / architettura / modelli come codice.

Per quanto riguarda il problema di un'architettura troppo dettagliata, puoi anche fare molto sforzo in architettura dove non sta dando molto valore. Vedi l'architettura orientata al rischio per riferimento, mi piace questo libro - Architettura del software quanto basta , forse lo farai anche tu.

modificare

Chiarito la mia risposta da quando mi sono reso conto che mi esprimevo spesso come "troppa architettura", dove intendevo veramente "architettura troppo dettagliata", che non è esattamente la stessa. Probabilmente un'architettura troppo dettagliata può essere vista come un'architettura "troppo", ma anche se si mantiene l'architettura a un buon livello e si crea il sistema più bello che l'umanità abbia mai visto, questo potrebbe essere ancora uno sforzo eccessivo sull'architettura se le priorità sono su funzionalità e time to market.


+1 Queste sono ottime considerazioni sull'altissimo livello che penso tu debba guardare in un sistema, ma come hai detto, richiede molta esperienza nella progettazione del software.
Samuel,

4

Il tuo amico sembra affrontare numerosi venti contrari in base al suo aneddoto. Questo è un peccato, e può essere un ambiente molto difficile in cui lavorare. Nonostante la difficoltà, era sulla strada giusta per usare gli schemi per rendere la sua vita più semplice, ed è un peccato che abbia lasciato quella strada. Il codice spaghetti è il risultato finale.

Poiché ci sono due diverse aree problematiche, tecnica e interpersonale, le affronterò separatamente.

interpersonale

La lotta che il tuo amico sta affrontando è con requisiti in rapida evoluzione e in che modo ciò influisce sulla sua capacità di scrivere codice gestibile. Direi innanzitutto che i requisiti che cambiano due volte al giorno, ogni giorno per un periodo di tempo così lungo sono un problema più grande e hanno un'aspettativa implicita non realistica. I requisiti stanno cambiando più velocemente di quanto il codice possa cambiare. Non possiamo aspettarci che il codice o il programmatore continuino. Questo rapido ritmo di cambiamento è sintomatico di una concezione incompleta del prodotto desiderato a un livello superiore. Questo è un problema. Se non sanno cosa vogliono veramente, perderanno molto tempo e denaro per non ottenerlo mai.

Potrebbe essere utile impostare i limiti per le modifiche. Raggruppare le modifiche in set ogni due settimane, quindi congelarle per le due settimane mentre vengono implementate. Crea un nuovo elenco per il prossimo periodo di due settimane. Ho la sensazione che alcuni di questi cambiamenti si sovrappongano o si contraddicano (ad esempio, alternando avanti e indietro tra due opzioni). Quando i cambiamenti arrivano veloci e furiosi, hanno tutti la massima priorità. Se li lasci accumulare in un elenco, puoi collaborare con loro per organizzare e dare priorità a ciò che è più importante per massimizzare gli sforzi e la produttività. Potrebbero vedere che alcuni dei loro cambiamenti sono sciocchi o meno importanti, dando al tuo amico un po 'di respiro.

Tuttavia, questi problemi non dovrebbero impedirti di scrivere un buon codice. Il codice errato porta a problemi peggiori. Potrebbe volerci del tempo per rifattorizzare da una soluzione all'altra, ma il fatto stesso che sia possibile mostra i benefici delle buone pratiche di codifica attraverso schemi e principi.

In un contesto di cambiamenti frequenti, il debito tecnica verrà venire a causa ad un certo punto. È molto meglio effettuare pagamenti verso di esso piuttosto che gettare la spugna e attendere che diventi troppo grande per essere superato. Se uno schema non è più utile, rifattalo via, ma non tornare ai modi di codifica da cowboy.

Tecnico

Il tuo amico sembra avere una buona conoscenza dei modelli di progettazione di base. L'oggetto nullo è un buon approccio al problema che stava affrontando. In verità, è ancora un buon approccio. Dove sembra avere delle difficoltà è capire i principi alla base degli schemi, il perché di ciò che sono. Altrimenti, non credo che avrebbe abbandonato il suo approccio.

(Quella che segue è una serie di soluzioni tecniche che non sono state richieste nella domanda originale, ma che mostrano come potremmo aderire ai modelli a scopo illustrativo.)

Il principio alla base dell'oggetto null è l'idea di incapsulare ciò che varia . Nascondiamo ciò che cambia, quindi non dobbiamo affrontarlo altrove. Qui, l'oggetto null stava incapsulando la varianza product.Pricenell'istanza (lo chiamerò un Priceoggetto e sarà un prezzo di oggetto null NullPrice). Priceè un oggetto di dominio, un concetto di business. A volte, nella nostra logica aziendale, non conosciamo ancora il prezzo. Questo succede. Caso d'uso perfetto per l'oggetto null. Prices hanno un ToStringmetodo che genera il prezzo, o stringa vuota se non è noto (o, NullPrice#ToStringrestituisce una stringa vuota). Questo è un comportamento ragionevole. Quindi i requisiti cambiano.

Dobbiamo eseguire l'output di nulla nella vista API o in una stringa diversa dalla vista dei gestori. In che modo ciò influisce sulla nostra logica aziendale? Beh, non lo fa. Nell'affermazione sopra, ho usato due volte la parola "view". Questa parola probabilmente non è stata pronunciata in modo esplicito, ma dobbiamo allenarci per ascoltare le parole nascoste nei requisiti. Quindi perché "visualizzare" è così importante? Perché ci dice dove deve davvero avvenire il cambiamento : dal nostro punto di vista.

A parte : se stiamo usando o meno un framework MVC è irrilevante qui. Sebbene MVC abbia un significato molto specifico per "Visualizza", lo sto usando nel significato più generale (e forse più applicabile) di un codice di presentazione.

Quindi abbiamo davvero bisogno di risolvere questo problema nella vista. Come potremmo farlo? Il modo più semplice per farlo sarebbe una ifdichiarazione. So che l'oggetto null era destinato a sbarazzarsi di tutti gli if, ma dobbiamo essere pragmatici. Possiamo controllare la stringa per vedere se è vuota e cambiare:

if(product.Price.ToString("c").Length == 0) { // one way of many
    writer.write("Price unspecified [Change]");
} else {
    writer.write(product.Price.ToString("c"));
}

In che modo influisce sull'incapsulamento? La parte più importante qui è che la logica della vista è incapsulata nella vista . In questo modo possiamo mantenere completamente isolati gli oggetti della nostra logica di business / dominio dalle modifiche della logica di visualizzazione. È brutto, ma funziona. Questa non è l'unica opzione, però.

Potremmo dire che la nostra logica aziendale è cambiata un po 'in quanto vogliamo produrre stringhe predefinite se non viene impostato alcun prezzo. Possiamo apportare una piccola modifica al nostro Price#ToStringmetodo (in realtà creare un metodo sovraccarico). Possiamo accettare un valore di ritorno predefinito e restituirlo se non viene impostato alcun prezzo:

class Price {
    ...
    // A new ToString method
    public string ToString(string c, string default) {
        return ToString(c);
    }
    ...
}

class NullPrice {
    ...
    // A new ToString method
    public string ToString(string c, string default) {
        return default;
    }
    ...
}

E ora il nostro codice di visualizzazione diventa:

writer.write(product.Price.ToString("c", "Price unspecified [Change]"));

Il condizionale è sparito. Fare questo troppo, tuttavia, potrebbe proliferare metodi di casi speciali nei tuoi oggetti di dominio, quindi questo ha senso solo se ci saranno solo pochi casi di questo.

Potremmo invece creare un IsSetmetodo su Priceche restituisca un valore booleano:

class Price {
    ...
    public bool IsSet() {
        return return true;
    }
    ...
}

class NullPrice {
    ...
    public bool IsSet() {
        return false;
    }
    ...
}

Visualizza logica:

if(product.Price.IsSet()) {
    writer.write(product.Price.ToString("c"));
} else {
    writer.write("Price unspecified [Change]");
}

Vediamo il ritorno del condizionale nella vista, ma il caso è più forte per la logica aziendale che dice se il prezzo è fissato. Possiamo usare Price#IsSetaltrove ora che lo abbiamo disponibile.

Infine, possiamo incapsulare l'idea di presentare un prezzo interamente in un aiuto per la vista. Ciò nasconderebbe il condizionale, preservando l'oggetto di dominio quanto vorremmo:

class PriceStringHelper {
    public PriceStringHelper() {}

    public string PriceToString(Price price, string default) {
        if(price.IsSet()) { // or use string length to not change the Price class at all
           return price.ToString("c");
        } else {
            return default;
        }
    }
}

Visualizza logica:

writer.write(new PriceStringHelper().PriceToString(product.Price, "Price unspecified [Change]"));

Esistono molti altri modi per apportare le modifiche (potremmo generalizzare PriceStringHelperin un oggetto che restituisce un valore predefinito se una stringa è vuota), ma questi sono alcuni rapidi che conservano (per la maggior parte) sia i modelli che i principi, come così come l'aspetto pragmatico di apportare un tale cambiamento.


3

La complessità di un modello di progettazione può morderti se il problema che doveva risolvere improvvisamente scompare. Purtroppo, a causa dell'entusiasmo e della popolarità dei modelli di progettazione, questo rischio viene raramente reso esplicito. L'aneddoto del tuo amico aiuta molto a mostrare come i modelli non pagano. Jeff Atwood ha alcune parole scelte sull'argomento.

Documentare i punti di variazione (sono rischi) nei requisiti

Molti dei modelli di progettazione più complessi (Null Object non tanto) contengono il concetto di variazioni protette , ovvero "Identificare i punti di variazione o instabilità previsti; assegnare responsabilità per creare un'interfaccia stabile attorno a loro". Adapter, Visitor, Facade, Layers, Observer, Strategy, Decorator, ecc. Sfruttano tutti questo principio. Si "ripagano" quando il software deve essere esteso nella dimensione della variabilità prevista e le ipotesi "stabili" rimangono stabili.

Se i tuoi requisiti sono così instabili che le tue "variazioni previste" sono sempre sbagliate, i modelli che applichi causeranno dolore o nella migliore delle ipotesi non saranno necessari.

Craig Larman parla di due opportunità per applicare le varianti protette:

  • punti di variazione : nel sistema o nei requisiti attuali e attuali, come interfacce multiple che devono essere supportate e
  • punti di evoluzione - punti di variazione speculativi che non sono presenti nei requisiti esistenti.

Entrambi dovrebbero essere documentati dagli sviluppatori, ma probabilmente dovresti avere l'impegno del cliente nei confronti dei punti di variazione.

Per gestire il rischio, si potrebbe dire che qualsiasi modello di progettazione che applica il fotovoltaico dovrebbe essere rintracciato in un punto di variazione nei requisiti approvati dal cliente. Se un cliente modifica un punto di variazione nei requisiti, il progetto potrebbe dover cambiare radicalmente (perché probabilmente hai investito nel design [modelli] per supportare tale variazione). Non è necessario spiegare coesione, accoppiamento, ecc.

Ad esempio, il cliente desidera che il software funzioni con tre diversi sistemi di inventario legacy. Questo è un punto di variazione in cui progetti. Se il cliente abbandona tale requisito, allora ovviamente hai un sacco di infrastrutture di progettazione inutili. Il cliente deve sapere che i punti di variazione costano qualcosa.

CONSTANTS nel codice sorgente sono una semplice forma di PV

Un'altra analogia con la tua domanda sarebbe quella di chiedere se l'uso CONSTANTSnel codice sorgente è una buona idea. Facendo riferimento a questo esempio , supponiamo che il cliente abbia abbandonato la necessità di password. Pertanto la MAX_PASSWORD_SIZEdiffusione costante nel codice diventerebbe inutile e persino un ostacolo alla manutenzione e alla leggibilità. Incolperesti l'uso di CONSTANTScome motivo?


2

Penso che dipenda almeno in parte dalla natura della tua situazione.

Hai citato requisiti in continua evoluzione. Se il cliente dice "Voglio che questa applicazione apistica funzioni anche con le vespe", quello sembra il tipo di situazione in cui un'attenta progettazione potrebbe aiutare a progredire, non ostacolarla (specialmente se si considera che in futuro potrebbe voler tenere anche moscerini della frutta.)

D'altra parte, se la natura del cambiamento è più simile a "Voglio che questa applicazione apistica gestisca il libro paga del mio conglomerato di lavanderia a gettoni", nessuna quantità di codice ti estrarrà dal tuo buco.

Non c'è nulla di intrinsecamente buono nei modelli di progettazione. Sono strumenti come tutti gli altri: li usiamo solo per semplificare i nostri lavori nel medio-lungo periodo. Se uno strumento diverso (come la comunicazione o la ricerca ) è più utile, allora lo usiamo.

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.