Come evitare il downcast continuo in questo caso?

1

Ho una classe astratta Dog e più sottoclassi ( Beagle , Labrador , Bulldog ...) estendendolo. Ho un DogHouse che può memorizzare Dog .

Il mio problema è che quando metto ad esempio un Beagle in un DogHouse come doghouse.addDog(beagle) e poi lo tolgo con doghouse.getDog() , questo metodo mi dà il beagle come Dog , ma voglio per recuperare il mio Beagle .

DogHouse ha molti più metodi, tutti mi danno Dogs , ma lavoro con cani specifici nella mia applicazione. Significa che devo sempre abbattere?

Aggiornamento:

Sto usando TypeScript e posso modificare qualsiasi parte del codice in modo che le mie opzioni siano praticamente infinite. Quello che non capisco:

Immagina di avere due scuole di cani. Sapendo che le diverse razze di cani sono capaci di cose molto diverse e hanno bisogno di cure e allenamenti diversi, la scuola di cane 1 è specializzata in Greyhound s, mentre l'altra è allenamento diciamo solo Bulldog s.

C'è un'azienda che vende DogHouse s. Dicono che il loro prodotto è compatibile con qualsiasi tipo di Dog , quindi entrambe le scuole di cani decidono di acquistare da loro.

Un allenatore alla scuola di cani 1 dice a un Greyhound di correre molto velocemente: greyhound.runVeryFast(); - qualcosa che solo i greyhound possono fare. Funziona.

Quindi, manda il levriero in una DogHouse: doghouse.addDog(greyhound); - che funziona anche.

Dopo che il cane ha potuto riposare abbastanza, lei lo chiama e gli dice di correre di nuovo molto velocemente: doghouse.getDog().runVeryFast(); - Non funziona.

Si rivolge alla società che vende il DogHouse s per lamentarsi di quale stupida casa hanno costruito che un cane dimentica in quale razza appartiene. "Perché devo dire al cane (il cast di lui) che è un Greyhound ogni volta che esce da casa sua?"

La società risponde "Scusa signora, forse possiamo costruire una nuova casa usando i farmaci generici." Ma poi la signora risponde: "E accetterà allora anche Cat s"? Voglio solo una casa per Dog s. "

Quindi la mia domanda è: come costruire un DogHouse corretto?

    
posta tom 17.11.2018 - 13:47
fonte

5 risposte

8

Potresti usare generici e creare una DogHouse of T.

class DogHouse<T>
    where T : Dog
{
    private T dog

    T getDog() { return this.dog; }

    void addDog(T dog) { this.dog = dog; }
}

var house = new DogHouse<Bulldog>();
var dog = new Bulldog();
house.addDog(dog);

Bulldog bulldog = house.getDog();
    
risposta data 17.11.2018 - 14:11
fonte
11

Chiediti: perché vuoi downcast?

La dichiarazione " DogHouse contiene Dog s" dice esattamente questo: puoi essere sicuro di trattare con Dog , niente di più. Se vuoi che beagle e labadors facciano cose diverse nella stessa situazione, dovresti sovrascrivere quei metodi (ad esempio bark() ) in ciascuna sottoclasse e chiamare semplicemente dog.bark() .

Se non è possibile organizzare le cose in questo modo nella propria lingua (ad esempio, se è necessario disporne di più per distinguere tra le azioni appropriate), la sottoclasse non è il modo giusto per strutturare il modello dati. Se deve conoscere il tipo concreto di un oggetto per poterlo utilizzare in modo efficace, non ha senso archiviarli in un contenitore generico.

    
risposta data 17.11.2018 - 14:02
fonte
1

Ho un caso simile in un progetto in cui solo il tipo di "cane" è di particolare interesse. Quindi ho preso in considerazione tutti i comportamenti comuni nella classe genitore (ordinamento, nome, ecc.) E ho sviluppato solo il tipo interessante con molte più capacità. Questo ovviamente richiede il controllo dell'is-of-type nei luoghi strategici e poi si diramano per analizzare l'interessante "cane" con un downcast. Questo naturalmente ha delle limitazioni sulla futura estensibilità, ma nel mio caso penso di capire abbastanza bene il dominio del problema per dire che è la cosa giusta. Naturalmente questo va contro molte buone pratiche, ma se sei molto sicuro di poter vivere con le limitazioni potrebbe essere più semplice che iniettare particolari interfacce o rendere tutti i cani capaci di rispondere a tutti i metodi.

Il punto è che se non riesci ad affrontare la maggior parte dei casi a livello di cane generico, allora l'idea dell'eredità ha un valore limitato. Ma poi se è possibile e quindi bisogno di alcuni speciali che richiedono il controllo del tipo e downcast potrebbe essere ok.

    
risposta data 19.11.2018 - 08:04
fonte
0

A trainer at dog school 1 tells a Greyhound to run very fast: greyhound.runVeryFast(); - something that only greyhounds can do. It works.

Questo è un odore del design e la base dei problemi che stai incontrando. Questa è una violazione di LSP: le classi base e derivate dovrebbero avere le stesse funzionalità.

Nel tuo caso, ogni cane (o nessuno) dovrebbe avere un metodo RunVeryFast e solo le loro implementazioni possono variare.

Seguendo la LSP, puoi essere sicuro che ogni cane che recuperi dalla cuccia ha un metodo RunVeryFast ().

    
risposta data 18.11.2018 - 17:20
fonte
0

Obiettivo-C consente un tipo " kindof <Dog> ". Se il metodo getter restituisce " kindof <Dog> " anziché "Dog", il risultato può essere assegnato a una variabile contenente un riferimento a Dog o qualsiasi sottoclasse senza cast. E sono consentite tutte le chiamate di metodo implementate da Dog o da qualsiasi sottoclasse. Quindi puoi scrivere [dogHouse.dog runVeryFast] che è implementato solo da Greyhound. Il compilatore lo accetta. Ci sarà un errore di runtime se il cane nella cuccia non è un Greyhound (o qualche altro cane che implementa runVeryFast).

Questo non è typesafe. Ma è meglio del casting, il che permetterebbe di trasmettere a "SiameseCat". Né l'assegnazione a un oggetto SiameseCat né a [dogHouse.dog catchMouse] verranno compilati.

A differenza della soluzione con tipi generici, DogHouse può contenere qualsiasi cane.

(È anche possibile avere una classe DogHouse parametrizzata su un qualche tipo di Cane, che sarebbe tipicamente, ma avrebbe oggetti DogHouse incompatibili per ogni tipo di Cane).

    
risposta data 18.11.2018 - 16:36
fonte

Leggi altre domande sui tag