Architettura software che consente la crescita, situazione di vita reale [chiusa]

0

Conosco interfacce, classi astratte che servono come classe base, ho letto su accoppiamenti lenti, ... ma non riesco a trovare una soluzione decente quando una proprietà di una classe cambia, e il nuovo tipo deve essere implementato in altre classi utilizzando questa classe aggiornata.

Esempio

Di seguito è una rappresentazione semplificata di una situazione di vita reale. La fattura contiene un oggetto Contatto con una proprietà Indirizzo.

Abstract Class Contact{
    string Name{get;set;}
    string Address{get;set;}
}
Class Customer:Contact{
    string CustomerNumber{get;set;}
}
Class Invoice{
    string DocumentNumber{get;set;}
    Customer Customer{get;set;}
}

Successivamente, decidiamo di aggiornare la classe Contact per contenere più indirizzi. Di cui la fattura ne avrà bisogno.

Class Address{
    string Street{get;set;}
}
Abstract class Contact{
    string Name{get;set;}
    List<Address> Addresses{get;set;}
}

Logicamente, la classe Invoice deve essere cambiata in qualcosa di simile a

Class Invoice{
    string DocumentNumber{get;set;}
    Customer Customer{get;set;}
    Address InvoiceAddress{get;set;}
}

problema

Immediatamente vedi che ovunque ho usato la proprietà string Address prima, ora dovrò cambiarlo in un occorrenza del tipo Indirizzo

Come affermato in precedenza, questo è solo un esempio semplificato. Il vero problema della vita sarebbe molto, molto più grande in quanto i contatti saranno utilizzati nell'intero software.

Conclusione

Voglio sviluppare il software con un principio che manterrà il mio software leggero per le nuove versioni di determinati moduli. In questo modo, se decido di creare un nuovo modulo Contatti, l'impatto sugli altri moduli è quasi inesistente. Quindi, la funzionalità aggiunta può essere implementata gradualmente.

Voglio sapere come posso affrontare questi problemi di versioning. Questo progetto è attualmente ancora in fase di progettazione architettonica e desidero risolverlo prima di continuare.

    
posta Steven Ryssaert 28.02.2014 - 10:46
fonte

3 risposte

1

Quando una proprietà di una classe cambia, devi modificare tutti i chiamanti che usano quella proprietà. Non è possibile magicamente avere una modifica dell'interfaccia che non richiede modifiche ai chiamanti che utilizzano tale interfaccia.

Tuttavia, è possibile isolare le proprietà interne da interfacce esterne, in genere è semplice come non utilizzare getter e setter. Sono un vero anti = modello e dovrebbero essere evitati in ogni momento. Vedete, tutto ciò che state facendo implementando un getter sta esponendo una variabile interna usando un metodo. Non c'è un piccolo disaccoppiamento o nascondimento in corso. Cambia il tipo di variabile interna .. e devi cambiare anche il getter, il che significa che devi cambiare tutti i tuoi clienti. Potresti anche rendere pubblici tutti i tuoi membri e ritagliare l'intermediario. Vedi questo post per maggiori dettagli. Se si forniscono metodi veri che ottengono dati dalla classe, è possibile nascondere le variabili interne. Se il numero di fattura cambia da una stringa a una int, è possibile mantenere il vecchio metodo che ha esposto il numero Invoice e lo ha lanciato. Ovviamente questo non funzionerà in ogni caso, ma può aiutare a nascondere l'implementazione dai chiamanti.

L'altra cosa che puoi fare è implementare più interfacce sulla tua classe, in modo che i vecchi chiamanti possano ancora chiamare la vecchia interfaccia mentre quelli nuovi possono accedere a Interface2 o InterfaceEx o qualsiasi altra cosa.

    
risposta data 28.02.2014 - 13:15
fonte
2

I want to develop the software with a principle that will keep my software light for new versions of certain modules. So that if I decide to create a new Contact module, the impact to the other modules is close to non-existing. So, the added functionality can be implemented gradually.

Progetta la tua classe in modo che i consumatori siano isolati dalle modifiche, quindi:

class Contact {
  string Address {get; set;} 
} 

diventerebbe

class Contact {
  List<Address> Addresses {get; set;}
  Address getAddress() { return Addresses[0].StreetAddressLine; }
}

As stated before, this is just a simplified example. The real life problem would be much, much bigger as contacts will be used throughout the entire software.

Una volta che vedi un'entità logica più di una volta tra le classi, prendi in considerazione l'estrazione in una classe. La modifica potrebbe essere avvenuta dopo aver notato che l'indirizzo era sia in Contatti che in Fattura, e questo sarebbe stato corretto prima che fosse necessario supportare più indirizzi.

Estrai pattern di design della classe

I am very curious of the suggestions of other programmers / software architects, and how you tackled versioning problems like these in your projects. This project is currently still in the architectural design stage, and I want this sorted out before we continue.

Se hai scritto il codice e poi lo hai refactored, hai spostato la fase di progettazione e l'implementazione. Considera un miglioramento nelle seguenti aree:

1) Progettazione di piccoli componenti che possono essere implementati rapidamente

2) Isolamento di altri componenti dalle modifiche a questo componente

Non progettare per cambiamenti teorici, ma avere un ciclo di sviluppo abbastanza breve da reagire ai cambiamenti. Se la progettazione e l'implementazione sono piuttosto brevi, il processo di creazione del codice originale e del codice modificato sarebbe stato più breve rispetto alla combinazione dei due.

    
risposta data 28.02.2014 - 11:57
fonte
2

Segui SOLID Design Principles e non aver paura di refactoring non appena ti senti la necessità di. Rimuovi il codice inutilizzato, può rendere difficile il refactoring in futuro.

Non cercare di costruire un design perfetto in una sola volta. Falla funzionare, quindi rendila migliore. Segui principio di Pareto : il 20% degli sforzi dà l'80% del risultato.

    
risposta data 01.03.2014 - 21:54
fonte