Decoratore o facciata

0

Affronto un problema se utilizzare un decoratore o un modello di facciata per soddisfare le mie esigenze.

Immagina che un cliente voglia riprodurre un video. Può usare l'interfaccia

public interface IVideoPlayer
{
    // Prepares everything to set up and plays the video
    void Play();
}

Il client non dovrebbe preoccuparsi della preparazione, quindi il riepilogo del metodo dell'interfaccia afferma che la preparazione è fatta dal metodo Play() .

La preparazione può includere cose diverse come la connessione a Internet, l'accesso al servizio e così via. Esistono diverse fasi di preparazione per diversi sistemi.

Modello di facciata

Potrei implementare IVideoPlayer usando il modello di facciata come segue

VideoPlayerFacade : IVideoPlayer
{
    ctor(...)

    void Play()
    {
        _internetConnection.Connect();
        _loginService.Login();
        _actualVideoPlayer.Play();
    }
}

Ciò che non mi piace di questo approccio:

  • La preparazione (connessione, accesso) sembra più un'aggiunta all'algoritmo di gioco effettivo, che mi porta più al modello di decoratore. Capisco piuttosto il modello di facciata per comporre alcuni passaggi "di livello uguale" per raggiungere un obiettivo comune, come CreateCar() delegando a CreateEngine() , CreateCarBody() e così via.
  • Affronto un problema di denominazione perché l'interfaccia del tipo di _actualVideoPlayer implementazioni potrebbe anche essere denominata IVideoPlayer , perché è il vero riproduttore video, che mi guida anche al modello di decoratore. Avrei bisogno di due interfacce quasi identiche (una con e una senza preparazione) con nomi abbastanza simili, che al momento non riesco a immaginare.

Schema decoratore

IVideoPlayer potrebbe essere implementato con l'algoritmo di riproduzione effettivo come segue

VideoPlayer : IVideoPlayer
{
    void Play()
    {
        // do stuff and algorithms
    }
}

La preparazione potrebbe essere eseguita in un decoratore come segue

PreparatingVideoPlayer : IVideoPlayer
{
    ctor(...)

    void Play()
    { 
        _internetConnection.Connect();
        _loginService.Login();
        _decoratee.Play();
    }
}

Ciò che non mi piace di questo approccio:

  • In realtà VideoPlayer viola l'LSP perché non è conforme all'interfaccia non eseguendo tutta la preparazione nel metodo Play() , il che mi porta piuttosto al modello di facciata. O non viola l'LSP perché sarà sempre decorato da PreparatingVideoPlayer quando viene iniettato nel sistema?

  • Cosa succede se un altro cliente desidera riprodurre video senza preparazione (perché il client sa che tutto è preparato o preparato tutto da solo)? Non sarebbe in grado di farlo perché lasciare che IVideoPlayer venga iniettato genererà sempre PreparatingVideoPlayer , il che mi porterebbe di nuovo al modello di facciata.

Conclusione

Quindi quale modello è più appropriato o questi modelli sono appropriati? I miei dubbi sono validi?

    
posta Creepin 21.11.2018 - 12:44
fonte

1 risposta

4

Schema decoratore

Iniziamo bene: io odio la tua seconda soluzione.

IVideoPlayer è un'interfaccia; un "contratto" che qualsiasi classe di implementazione fa una certa cosa. È un contratto sul lato codice (tra chiamante e implementazione), ma in un certo senso è anche interpersonale tra i programmatori che lavorano con esso - il compilatore non mi fermerà se implemento IVideoPlayer con Play() cancellando il disco rigido.

Le 2 implementazioni non soddisfano lo stesso contratto, hanno solo un metodo con lo stesso nome.

La sicurezza che un'interfaccia dovrebbe fornire non è data qui. Ancor peggio, a causa di questa aspettativa di un simile contratto, è più probabile che qualcuno possa sbagliare.

Assegnazione di nomi alle cose

Ci sono 2 cose difficili nell'informatica , uno di questi è il nome delle cose. Non sottovalutarlo, un thesaurus è uno degli strumenti di programmazione più sottovalutati. Dovresti avere 2 nomi diversi che comunichino la differenza tra le 2 implementazioni.

Per dare esempi, puoi rinominare lo "esterno" in VideoSystem : uno è l'intero sistema, mentre l'altro è solo il giocatore. Oppure puoi rinominare quello interno VideoRenderer : uno è l'intero giocatore, mentre l'altro è solo la parte "rendering" del video.

Poiché quella esterna sembra essere un'API esposta, preferirei rinominare quella interna, perché puoi spiegare più facilmente quella interna ai tuoi colleghi che quella esterna ai tuoi clienti.

Modelli

Wikipedia dice del Decorator Pattern

What problems can the Decorator design pattern solve?

  • Responsibilities should be added to (and removed from) an object dynamically at run-time.
  • A flexible alternative to subclassing for extending functionality should be provided.

Informazioni sul pattern di facciata, Wikipedia dice

What problems can the Facade design pattern solve?

  • To make a complex subsystem easier to use, a simple interface should be provided for a set of interfaces in the subsystem.
  • The dependencies on a subsystem should be minimized.

Nessuno di questi è il tuo obiettivo qui.

Non stai lavorando su questo perché vuoi aggiungere o rimuovere cose dinamicamente in fase di esecuzione, o vuoi estendere funzionalità, o perché il tuo sottosistema è troppo difficile da usare, o hai troppe dipendenze da un sottosistema.

Stai provando a creare alcune funzionalità in primo luogo.

Soluzione

Non pensare "quale modello dovrei applicare". Fai ciò che funziona. Alla fine il tuo codice potrebbe rivelarsi un modello già stabilito e poi, quando i tuoi colleghi chiedono del codice, puoi dire " Ho risolto il problema con {pattern name} ". Questo è tutto il vantaggio che ottieni dall'usare un modello.

Tutto ciò che viene detto, fai la prima cosa.

Tuttavia, chiamare che una facciata potrebbe essere confusa, perché molto di persone vedere le facciate come piuttosto semplici classi, solo metodi "passanti" (es. se hai anche un metodo Connect() che chiama _internetConnection.Connect(); ). Gli esempi su Wikipedia dicono il contrario, ma non sembrano particolarmente buoni (ad es. ripetutamente richiamo nuovo nei costruttori ).

Hai una classe VideoPlayer e, per fare il suo lavoro, ha bisogno di altre classi: chiamerei solo la buona vecchia composizione. Nessuno ti fraintenderà se lo chiami composizione.

    
risposta data 21.11.2018 - 19:53
fonte

Leggi altre domande sui tag