Quali sono alcune strategie per eliminare le interruzioni nel codice dinamico polimorfico distribuito su più livelli?

1

Contesto: progettazione con classi polimorfiche correlate

Ad esempio, consideriamo le seguenti definizioni di classe per la rappresentazione di messaggi che otterremmo da un servizio remoto:

class ImageMessage : IMediaMessage
class VideoMessage : IMediaMessage
class DocumentMessage : IMediaMessage

I messaggi arriverebbero in una codifica di alto livello come ad esempio JSON e assomiglia a questo:

{
 type: 'image|video|document',
 ...
}

Quindi, a seconda del tipo, utilizzeremmo una factory per creare un'istanza del messaggio del tipo concreto corretto.

Ora stiamo scrivendo un'applicazione web e abbiamo bisogno di visualizzare anche i messaggi. Il client del codice sa solo che sta funzionando con Collection<IMediaMessage> e deve essere in grado di delegare a visualizzazioni specifiche, ad esempio:

class ImageView : IMediaView
class VideoView : IMediaView
class DocumentView : IMediaView

Quindi finiamo per aver bisogno di ancora "un altro pezzo di codice" per inviare i tipi di messaggi alle loro viste corrispondenti. In Javascript questo potrebbe essere fatto con un switch e instanceof controlli poiché il "compilatore" (non ce n'è uno) non può scegliere quello giusto in fase di runtime.

Problema: mantenere la coerenza quando si aggiungono nuovi tipi di calcestruzzo

Quindi, in questo esempio, quando si aggiunge un nuovo tipo di messaggio multimediale, lo sviluppatore dovrebbe ricordare di fare due cose:

  1. Aggiorna il factory per il nuovo tipo di messaggio multimediale, quindi può essere serializzato su un nuovo tipo concreto (e rendere questo tipo concreto)
  2. Aggiorna e crea una vista per associare il nuovo messaggio multimediale per la visualizzazione

A seconda di cosa succede, ci sono molte altre cose che si accendono, ma questo è un caso piuttosto tipico.

Come potrebbe essere migliorata la situazione?

Le domande sono:

  1. In che modo comunichi chiaramente a un nuovo programmatore che questa è la procedura per aggiungere un nuovo messaggio multimediale? Ordinazione di pacchi? Documentazione?
  2. C'è un modo per ridisegnarlo meglio in modo che sia più ovvio senza documentazione?

    Stavo pensando a qualcosa di simile:

    createConcreteAndViewFor(mediaMessage) {
       return {
         view: ImageView,
         concrete: ImageMessage
       }
    }
    

    ma questo fonde insieme due concetti, poiché è possibile utilizzare il tipo concreto per altre cose, ad esempio forse un generico

  3. Test automatici: possono aiutare? In questi esempi, scansionando a) il tipo oi tipi che si estendono eb) i tipi disponibili nel tipo di messaggio multimediale enum, si potrebbe affermare che è implementato almeno una volta in ciascuna delle classi rilevanti ... avere la funzione lanciata se non può gestire un nuovo input che è stato creato.

Nota importante: questa domanda riguarda come risolvere il dispacciamento e il mantenimento di strutture di codice polimorfico in linguaggi dinamici - non una panoramica di alto livello dei linguaggi dinamici!

    
posta Vaughan Hilts 16.08.2018 - 07:40
fonte

3 risposte

2

Definirei due interfacce sul messaggio in entrata:

class ImageMessage : IMediaMessage, IViewable
class VideoMessage : IMediaMessage, IViewable
class DocumentMessage : IMediaMessage, IViewable

Il IMediaMessage è l'interfaccia necessaria per ricostruire l'oggetto binario di grandi dimensioni. L'interfaccia IViewable è quella necessaria per estrarre i metadati dal messaggio.

Quindi definisci la tua vista in questo modo:

class MessageView<T> where T : IViewable

MessageView dovrebbe essere scritto dove può consumare qualsiasi oggetto fintanto che implementa IViewable . Se sei attento all'interfaccia IViewable e la vista può essere veramente generica, questo design non dovrebbe mai richiedere if (typeof(T) == typeof(concreteType) e non dovrebbe richiedere modifiche come tipi di file multimediali aggiunti.

Avrai ancora bisogno di una factory per estrarre i messaggi dal JSON, ma una volta che il messaggio è stato estratto, dovrebbe avere abbastanza dati al suo interno per servire a entrambi gli scopi.

    
risposta data 29.09.2018 - 04:20
fonte
0

Per ogni nuovo tipo di messaggio è necessario creare sottoclassi appropriate di IMediaMessage e IMediaView e anche insegnare alla factory come istanziarle. Non puoi andartene da questo.

Quando arriva un nuovo messaggio, il suo tipo (immagine | video | documento) deve essere letto e gli oggetti IMediaMessage e IMediaView corrispondenti possono essere istanziati (eventualmente singleton).

Se si dispone di una raccolta di IMediaMessage, perché non è possibile che ogni oggetto IMediaMessage contenga un riferimento all'oggetto IMediaView corrispondente? Quindi non "finiamo per aver bisogno di ancora" un altro pezzo di codice "per inviare i tipi di messaggi alle loro viste corrispondenti" ... perché il client invierà polimorficamente assumendo che tutte le sottoclassi di IMediaView implementino la stessa interfaccia.

Ho anche faticato a capire questa affermazione ....

"In Javascript questo potrebbe essere fatto con uno switch e un'istanza di controlli poiché il" compilatore "(non ce n'è uno) non può scegliere quello giusto in fase di esecuzione." Volevi dire che non puoi scegliere il giusto uno "in fase di compilazione" ?. In generale, tuttavia, è possibile implementare una singola spedizione in qualsiasi lingua con un codice di runtime che esamina i tipi o con altri mezzi che non richiedono alcun codice di runtime (ad esempio C ++ vtable) - ma perché questo è importante per la tua domanda?

    
risposta data 16.08.2018 - 10:44
fonte
-1

Hai solo alcune scelte. Ogni tipo di messaggio ha due tipi di dati strettamente correlati e la vista che immagino dipende dal messaggio, ma non viceversa. O:

  • Combina i due tipi di dati, in modo che uno sia nidificato nell'altro e il factory per uno è un'istanza dell'altro (probabilmente non quello che vuoi).
  • Inserisci il meta tipo (classe) di ciascuno in una tabella o dizionario, dove la chiave della tabella è la forma della stringa dell'identificatore del messaggio e le classi di ciascun tipo sono due valori nella tabella, per quella chiave.

Nella seconda opzione il factory per il messaggio o l'istanza di visualizzazione probabilmente necessiterebbe di riflessione, o usa la clonazione (se si memorizza un'istanza e non una classe). L'unico vantaggio del secondo approccio è che il prossimo sviluppatore vedrà che ogni voce della tabella deve supportare la creazione di due tipi di dati.

    
risposta data 17.08.2018 - 03:06
fonte

Leggi altre domande sui tag