Come tenere il passo con 'richiedere nuovi metodi in un'interfaccia' (seguire LSP e aggiungere nuovi metodi all'interfaccia sembra violare ISP)

3
  • Ho un gioco che si occupa delle porte di apertura e chiusura e del motore della porta si occupa dell'interfaccia IDoor che ha contratti Open () e Close ()
  • Fin qui tutto bene. il gioco è testato e funziona bene.
  • Ora è necessaria una nuova funzionalità, le porte possono essere chiuse automaticamente con a timer, cioè un nuovo contratto: SetTimer () è necessario. Solo le parti del mio motore della porta devono occuparsi di impostare il timer, il resto di del codice non ha bisogno di impostare il timer, cioè la vecchia interfaccia IDoor è sufficiente.

Come lo faresti?

  1. Aggiungi l'interfaccia da SetTimer() a IDoor ? e implementare il metodo nelle classi derivate approp'ly, le classi Door esistenti possono avere un metodo dummy e il nostro speciale TimedDoor può avere un'attuazione appropriata. Inoltre, potresti avere un contratto "DoesSupportTimedClosing" per farlo perfetto LSP compatibile.
  2. Rispetto ISP (so che l'ISP consente ai clienti di creare le loro implementazioni senza dover implementare l'intera interfaccia, ma possiamo considerare anche il mio codice interno come client, o ho una comprensione sbagliata) e creare una nuova interfaccia - ITimedDoor : IDoor (ITimedDoor avrà il SetTimer di contratto) è possibile creare delle timeddoors che implementano ITimedDoor, ma questa violazione di LSP? Se voglio un pezzo arbitrario di codice esistente per impostare un timer su una porta, dovrei fare controlli di tipo e digitare cast per chiamare ITimedDoor.SetTimer() sull'oggetto IDoor che ha.

In una shell dado, se hai un'interfaccia - IInterface1 e codice esistente che è già stato testato e funziona bene, come gestiresti il requisito di aggiungere nuovi metodi ad esso?

Alcuni riferimenti su argomenti correlati:

posta user136627 15.07.2015 - 15:42
fonte

1 risposta

4

La mia comprensione del principio di sostituzione di Liskov è diversa dalla tua, vale a dire qualsiasi implementazione di IDoor dovrebbe comportarsi come una porta: fai ciò che ci si aspetta quando viene chiamato Open() o Close() - questo include oggetti che implementano ITimedDoor (con ITimedDoor una sottoclasse di IDoor ). not implica che qualsiasi parte arbitraria di codice dovrebbe essere in grado di chiamare SetTimer() su qualsiasi IDoor con la maggior parte delle porte che non fanno nulla di utile su una chiamata a SetTimer() .

Il principio di segregazione dell'interfaccia - ovvero nessun client deve dipendere dai metodi che non usa - si combina molto bene con la mia comprensione del principio di sostituzione di Liskov:

  1. Qualsiasi interfaccia dovrebbe essere minima: il codice del codice dovrebbe dipendere dai metodi che non usa.
  2. Ogni classe che implementa un'interfaccia dovrebbe aderire alla stessa semantica e ogni oggetto di una classe che implementa l'interfaccia dovrebbe essere scambiabile con ogni altro oggetto di qualsiasi altra classe che implementa l'interfaccia: le classi non dovrebbero implementare metodi che si comportano diversamente ( incluso no-op) rispetto ad altre implementazioni del metodo di quella interfaccia.

In generale, avere una collezione di IInterface1 -oggetti e quindi eseguire un'azione diversa a seconda del tipo (o dell'interfaccia secondaria) sarà inevitabile (o almeno difficile da evitare). L'utilizzo del modello visitatore può essere un'alternativa per eseguire il controllo del tipo orario.

Tuttavia, in questa situazione specifica - di un gioco con porte alcune delle quali sono porte temporizzate - potresti essere meglio con un level builder che costruisce un livello contenente porte, alcune temporizzate e imposta i timer delle porte temporizzate (o collega i timer a qualche altro evento nel livello).

    
risposta data 15.07.2015 - 21:31
fonte