2013-05-19 11 views
5

Sono sicuro che questo deve essere stato chiesto prima, ma non riesco a trovare un esempio simile. Capisco bene il polimorfismo e il sovraccarico del metodo, ma qui è uno scenario apparentemente semplice con una soluzione che mi sfugge:gestione dei parametri simile al polimorfismo - OO semplice?

diciamo che ho una classe base con diverse classi derivate. Userò forme per questo esempio

base Shape 
derived Circle extends Shape 
derived LineSeg extends Shape 

ecc

Ora, la forma ha un metodo chiamato intersecano (altro) che i test contro un'altra forma per vedere se si intersecano. Con il polimorfismo è facile vedere come Circle, LineSeg, ecc. Possono implementare i propri metodi "intersect" e, con l'overloading del metodo, posso facilmente implementare tutte le combinazioni necessarie. per esempio,

Circle.intersect(LineSeg) 
Circle.intersect(Circle) 
LineSeg.intersect(Circle) 

ecc

finora tutto bene.

il problema è, se continuo a un elenco centrale di forme, io voglio fare questo:

for some shape s 
Foreach shape in Shapes 
    if (s.intersect(shape)) - do something 

Attualmente, io non sono sicuro di come questo sia possibile, dal momento che il metodo di sovraccarico seleziona il "Intersect" metodo per abbinare il tipo base Shape e non il tipo di parametro appropriato. Come posso farlo senza una catena if-else controllando i tipi e down-casting?

BTW, sto usando Java, ma non sono sicuro che la lingua sia del tutto pertinente poiché sembra essere una domanda di progettazione di base. Sembra così semplice, cosa mi sto perdendo?

Grazie!


Risolto sotto (Grazie!), Vedere i dettagli lì. Fondamentalmente, avendo una callback nelle classi derivate che poi chiama il metodo appropriato (un pattern visitor?), Puoi usare la "this" keyword per invocare il metodo intersect corretto, poiché ha il tipo corretto necessario.

+1

Alcuni linguaggi di programmazione supportano l'overload sui tipi di runtime gli argomenti più direttamente.Ad esempio, LISP supporta * multimethods * e suppongo che alcuni dei linguaggi di programmazione funzionale supportino lo stesso. In Java, questo è solitamente chiamato * double dispatch *, e il pattern GoF Visitor è la strada da percorrere. –

+1

Non mettere a tacere il tuo design, ma la tua eredità è un po 'sbagliata. I segmenti di linea non sono realmente forme. Formano forme. Dovresti controllare le intersezioni di una qualsiasi delle tante, a seconda della forma, dei segmenti di linea che costituivano le due forme in questione :) – ChiefTwoPencils

+0

@ C.Lang, buona cattura, ma cosa succede se rinominare lineSeg su "wall"? questo è fondamentalmente il modo in cui è usato, ma chiamarlo lineSeg rende più facile concettualizzarlo usando altre forme più complesse. – user1922401

risposta

3

Il mio primo pensiero è stato il modello di visitatore, quasi tutti i metodi di Shape due, uno che chiamerò intersect(Shape) e un metodo doIntersect() per tipo di forma.

Sembrerebbe su come questo:

interface Shape { 
    public abstract Intersection intersect(Shape other); 

    public abstract Intersection doIntersect(Circle circle); 

    public abstract Intersection doIntersect(LineSeg line); 
} 
class LineSeg implements Shape { 
    @Override 
    public Intersection intersect(Shape other) { 
     return other.doIntersect(this); 
    } 

    Intersection doIntersect(Circle circle) { 
     // Code to intersect with Circle 
    } 

    Intersection doIntersect(LineSeg other) { 
     // Code to intersect with another Lineseg 
    } 
} 

class Circle implements Shape { 
    @Override 
    public Intersection intersect(Shape other) { 
     return other.doIntersect(this); 
    } 

    public Intersection doIntersect(Circle other) { 
     // Code to intersect with another Circle 
    } 

    public Intersection doIntersect(LineSeg segment) { 
     // Code to intersect with LineSeg 
    } 
} 

Si potrebbe desiderare i metodi doIntersect per essere pacchetto private o hanno scelto nomi diversi rispetto a questi però.

+0

Questo è fantastico, grazie. Risolto il problema rapidamente e facilmente. Ho bisogno di aggiungere tutte le possibilità del tipo di parametro come prototipi nella classe base, costringendo i bambini a gestire qualsiasi possibilità. – user1922401

+0

Un ulteriore perfezionamento: dovresti essere in grado di nominare tutti i metodi 'intersect()' invece di nominare quelli specifici 'doIntersect()'. Per quanto ne so, il compilatore sceglie il sovraccarico più specifico che si adatta prima. – confusopoly

+0

Grazie - L'ho già fatto, ma ho deciso di lasciare il tuo suggerimento perché potrebbe essere meno confuso. Ho anche provato a metterlo nella classe base in modo che ne sia richiesto solo uno, ma usare "questo" in un metodo di classe base ha il tipo di classe base, sfortunatamente (vedo perché, però) – user1922401

0

See Generics Jdk5

Per ciascuna forma in forme <>

0

La classe forma deve essere una classe astratta, che significa che non arrivare a essere istanziati, solo il cerchio classi derivate e lineseg avere istanze. Il metodo Intersect deve essere virtuale in forma in modo che quando si esegue un ciclo su tutte le forme, il metodo Intersect di ogni forma si chiama

public abstract class Shape { 

    boolean intersect(Shape s); 
} 

public class Circle extends Shape { 

    boolean intersect(Shape s) { 
     ... 
     if(s instanceOf Circle) { 
      .... // Circle intersects cicrcle 
     } else if(s instanceOf Lineseg) { 
      .... // Circle intersects Lneseg 
     } else { 
      throw RuntimeException("Unrecognized shape"); 
     } 

    } 

} 

public class Lineseg extends Shape { 

    boolean intersect(Shape s) { 
     ... 
     if(s instanceOf Circle) { 
      .... // Lineseg intersects circle 
     } else if(s instanceOf Lineseg) { 
      .... // Lineseg intersects lineseg 
     } else { 
      throw RuntimeException("Unrecognized shape"); 
     } 
    } 

} 
+0

Scusa, stavo chiedendo una risposta senza quella if-else type-check chain – user1922401

+0

@ user1922401 Ma si è disposti a scrivere un metodo per ciascuna classe derivata in ciascuna delle classi derivate, che è la stessa. Solo in questo modo è il classico modo OO di gestire il polimorfismo. – ilomambo

+0

non è la stessa cosa, poiché tramite il pattern del visitatore hai solo un'indirizzamento extra. Con la catena if-else, nel peggiore dei casi n controlli ogni volta che chiami il metodo. Inoltre, la soluzione proposta è leggermente più sicura. Se aggiungo un nuovo tipo, devo aggiungere la nuova intestazione intersect digitata alla classe base o non verrà compilato e, impone a tutti gli altri tipi di forma di gestirlo, nella tua soluzione potrei dimenticarlo accidentalmente. Sebbene tu proponga di avere un if-else in ogni classe, questo potrebbe essere centralizzato – user1922401