Domanda

In origine ho avuto un problema di progettazione in cui avevo bisogno di cinque sottoclassi di una superclasse, in cui tutti tranne due sarebbero utilizzare lo stesso metodo generico di fare le cose e le altre due classi avrebbero bisogno di un trattamento speciale. Ho voluto evitare di scrivere il metodo cinque volte; due casi speciali e tre quelli identici.

Così ho dovuto ogni classe erediterà SuperClass, e il suo metodo doSomething (), e aveva SubClassSpecial1 e SubClassSpecial2 di override con il proprio metodo doSomething.

Questo è stato tutto bene fino a quando ho scritto un metodo che sembrava qualcosa di simile

void fooBar(SuperClass obj) {
    obj.doSomething()
}

e potrebbe essere chiamato come fooBar( new SubClassSpecial1() );

Il problema è che la classe runtime della variabile obj ora è quello della sua superclasse, e così sarà chiamare i metodi definiti nella superclasse. Potrei, naturalmente, fare un metodo astratto doSometing () nella superclasse, e rendere ogni sottoclasse implementare la propria versione, ma che sarebbe duplicare il codice in tre delle classi. E voglio evitare di fare che ...

avrei perso ogni polimorfismo guadagno dà se ho avuto un sacco di ramificazioni attraverso

if(obj.getClass().getName() == "SubClassSpecial1" )  ((SubClassSpecial1) obj).doSomething()K;
else if ...

Che cosa devo fare, al fine di rendere il design più elegante e non-hacker?

È stato utile?

Soluzione

Quello che hai descritto dovrebbe funzionare bene come è.

è obj.doSomething () in realtà chiamare l'implementazione della superclasse? Se lo è, non stai override correttamente. Verificare che non è stato modificato la firma nelle versioni ignorati.

Altri suggerimenti

Sono un po 'confuso da questa parte della tua domanda:

  

Naturalmente, quello che volevo fare era avere ogni oggetto chiamare la propria versione di doSomething (), ma non è riuscito a rendersi conto che, al fine di farlo, OBJ doveva essere dichiarata come uno dei metodi di sottoclasse. E ora è un casino.

Naturalmente la dichiarazione non ha importanza, il metodo doSomething () sarà sempre invocata in base al tipo di esecuzione della classe.

Quindi penso che quello che stavi cercando di fare dovrebbe funzionare bene, per esempio queste dichiarazioni possono essere utilizzati per passare al metodo foobar:

SuperClass sc1 = new SubClassSpecial1();
SubClassSpecial2 sc2 = new SubClassSpecial2();
//etc..

Quando hai questo:

void fooBar(SuperClass obj) {
    obj.doSomething();
}

allora la a tempo di compilazione tipo di obj è SuperClass. Ciò significa che il compilatore sta per verificare che SuperClass ha un metodo doSomething().
A runtime è possibile sostituire una sottoclasse di SuperClass, questo è il Liskov principio di sostituzione di . Metodo foobar() non lo sa, e non dovrebbe sapere, ciò che il tipo di runtime di obj è, solo che deriva da SuperClass e così doSomething() può essere chiamato.

Per quanto riguarda il tuo esempio:

fooBar( new SubClassSpecial1() );

In questo caso vi capita di sapere che il runtime tipo del parametro è SubClassSpecial1 che ignora specificamente doSomething(). In tutti i casi il metodo giusto è chiamato.

Una parola su refactoring. Si può prendere in considerazione il refactoring la gerarchia.
La vostra base di classe SuperClass dovrebbe definire doSomething() come astratto. I suoi tre classi che hanno bisogno la stessa implementazione di doSomething() dovrebbe ereditare da una classe base intermedia che ha quella specifica applicazione. I suoi due classi speciali devono ereditare direttamente da SuperClass e hanno la loro attuazione doSomething().

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top