La mia attuale applicazione consente agli utenti di definire moduli Web personalizzati attraverso una serie di schermate di amministrazione. è essenzialmente un'applicazione di tipo EAV. Pertanto, non riesco a scrivere codice HTML o codice ASP.NET per eseguire il rendering di una determinata pagina. Invece, l'interfaccia utente richiede un'istanza di un oggetto Form dal livello di servizio, che a sua volta ne costruisce uno utilizzando diverse tabelle RDMBS. Modulo contiene il tipo di classi che ci si aspetta di vedere in un tale contesto: Form
=>IEnumerable<FormSections>
=>IEnumerable<FormFields>
Si tratta di un tipico caso d'uso per IOC?
Ecco ciò che il livello di servizio si presenta come:
public class MyFormService: IFormService{
public Form OpenForm(int formId){
//construct and return a concrete implementation of Form
}
}
Tutto funziona splendidamente (per un po ') . L'interfaccia utente è nient'altro su quali sezioni/campi esistano in un dato modulo: rende felicemente l'oggetto Form che riceve in una pagina ASP.NET funzionale.
Alcune settimane più tardi, ottengo un nuovo requisito dal business: Quando si visualizzano versioni non modificabili (cioè di sola lettura) di un modulo, alcuni valori di campo devono essere uniti insieme e altri campi inventati/calcolati devono essere aggiunto. Nessun problema, dico. Semplicemente modificare la mia classe di servizio in modo che i suoi metodi sono più esplicito:
public class MyFormService: IFormService{
public Form OpenFormForEditing(int formId){
//construct and return a concrete implementation of Form
}
public Form OpenFormForViewing(int formId){
//construct and a concrete implementation of Form
//apply additional transformations to the form
}
}
Anche in questo caso tutto funziona alla grande e l'equilibrio è stato ripristinato alla forza. L'interfaccia utente continua ad essere agnostica su ciò che è nella forma, e la nostra separazione delle preoccupazioni è raggiunta. Solo poche settimane dopo, tuttavia, l'azienda emette un nuovo requisito: in determinati scenari, dovremmo applicare solo un po 'di delle trasformazioni di modulo a cui ho fatto riferimento sopra.
A questo punto, sembra che l'approccio del "metodo esplicito" abbia raggiunto un punto morto, a meno che non voglio finire con un'esplosione di metodi (OpenFormViewingScenario1, OpenFormViewingScenario2, ecc.). Invece, presento un altro livello di riferimento indiretto:
public interface IFormViewCreator{
void CreateView(Form form);
}
public class MyFormService: IFormService{
public Form OpenFormForEditing(int formId){
//construct and return a concrete implementation of Form
}
public Form OpenFormForViewing(int formId, IFormViewCreator formViewCreator){
//construct a concrete implementation of Form
//apply transformations to the dynamic field list
return formViewCreator.CreateView(form);
}
}
In superficie, questo sembra approccio accettabile e tuttavia v'è un certo odore. Vale a dire, l'interfaccia utente, che ha vissuto in ignorante beatitudine sui dettagli di implementazione di OpenFormForViewing, deve possedere la conoscenza e creare un'istanza di IFormViewCreator.
- Le mie domande sono due: Esiste un modo meglio per ottenere il componibilità che sto cercando? (forse da utilizzando un container IoC o una casa di fabbrica arrotolata per creare il calcestruzzo IFormViewCreator )?
- Ho danneggiato fondamentalmente l'astrazione qui?
+1 per la buona domanda. – Lucero
Sono d'accordo con te riguardo l'odore. Sembra che l'interfaccia utente non debba conoscere IFormViewCreator. Una domanda per te: l'interfaccia utente è a conoscenza dei parametri che dettano il "sapore" di cui dovresti tornare? Potrebbe avere più senso che l'interfaccia utente passi in un oggetto che funge da contenitore per i vari valori che determinano quali trasformazioni applicare. Il tuo metodo OpenFormForViewing() sarebbe quindi responsabile per l'inoltro alla classe factory, o per capire in modo esplicito quali "sapori" restituire. –