2010-09-08 15 views
5

Supongamos que tengo una jerarquía de clases en Java:patrón de diseño de envío?

interface Item { ... }; 
class MusicBox implements Item { ... }; 
class TypeWriter implements Item { ... }; 
class SoccerBall implements Item { ... }; 

y tengo otra clase en el mismo paquete:

class SpecialItemProcessor { 
    public void add(Item item) 
    { 
     /* X */ 
    } 
} 

donde quiero hacer algo diferente para cada tipo de elemento, pero Don 't desea definir esa acción en las diferentes clases Item (MusicBox, TypeWriter, SoccerBall).

Una manera de manejar esto es:

class SpecialItemProcessor { 
    public void add(Item item) 
    { 
     if (item instanceof MusicBox) 
     { 
      MusicBox musicbox = (MusicBox)item; 
      ... do something ... 
     } 
     else if (item instanceof MusicBox) 
     { 
      TypeWriter typewriter = (TypeWriter)item; 
      ... do something ... 
     } 
     else if (item instanceof SoccerBall) 
     { 
      SoccerBall soccerball = (SoccerBall)item; 
      ... do something ... 
     } 
     else 
     { 
      ... do something by default ... 
     } 
    } 
} 

Esto funciona, pero parece muy torpe. ¿Hay alguna forma mejor de hacerlo, cuando conozco casos especiales? (obviamente si Item contiene un método doSomethingSpecial entonces puedo simplemente llamar al método de ese artículo sin importar de qué tipo es, pero si no quiero que ocurra esa diferenciación dentro del mismo artículo, ¿cómo lo manejo?)

+0

No es una respuesta Lo sé, pero aparentemente en ActionScript 3 se puede crear una instancia de una Cadena (es decir, 'com.djw.MusicBox' puede crear una instancia de MusicBox), este tipo de cosas posibles en Java tal vez? ¡Sólo una sugerencia! – danjah

+0

@dan: 'Clase # forName()'. Sin embargo, cuestiono el valor aquí. – BalusC

Respuesta

2

Creo que voy a utilizar la idea de la inversión de control y el visitor pattern:

interface Item { 
    public void accept(Visitor visitor); 
    ... 

    public interface Visitor { 
     public void visit(Item item); 
    } 
} 


class MusicBox implements Item { 
    public interface Visitor extends Item.Visitor { 
     public void visitMusicBox(MusicBox item); 
    } 
    ... 
    @Override public accept(Item.Visitor visitor) 
    { 
     if (visitor instanceof MusicBox.Visitor) 
     { 
      ((MusicBox.Visitor)visitor).visitMusicBox(this); 
     } 
    } 
} 

class TypeWriter implements Item { 
    public interface Visitor extends Item.Visitor { 
     public void visitTypeWriter(TypeWriter item); 
    } 
    ... 
    @Override public accept(Item.Visitor visitor) 
    { 
     if (visitor instanceof TypeWriter.Visitor) 
     { 
      ((TypeWriter.Visitor)visitor).visitTypeWriter(this); 
     } 
    } 
} 

class SoccerBall implements Item { 
    public interface Visitor extends Item.Visitorr { 
     public void visitSoccerBall(SoccerBall item); 
    } 
    ... 
    @Override public accept(Item.Visitor visitor) 
    { 
     if (visitor instanceof SoccerBall.Visitor) 
     { 
      ((SoccerBall.Visitor)visitor).visitSoccerBall(this); 
     } 
    } 
} 

y luego hacer lo siguiente, que al menos reduce el cheque instanceof a una por add() llamada :

class SpecialItemProcessor 
    implements 
     MusicBox.Visitor, 
     TypeWriter.Visitor, 
     SoccerBall.Visitor, 
     Item.Visitor 
{ 
    public void add(Item item) 
    { 
     item.accept(this); 
    } 
    @Override public void visitMusicBox(MusicBox item) 
    { 
     ... 
    } 
    @Override public void visitTypeWriter(TypeWriter item) 
    { 
     ... 
    } 
    @Override public void visitSoccerBall(SoccerBall item) 
    { 
     ... 
    } 
    @Override public void visit(Item item) 
    { 
     /* not sure what if anything I should do here */ 
    } 
} 
0

Podría crear un patrón de puente para Artículo, en el que el otro lado eran los procesos asociados a hacer cuando se llama a add(). También podría agregar un método de fábrica a la mezcla.

class SpecialItemProcessor { 
    public void add(Item item) 
    { 
    Process p = Item.createCorrespondingProcessor(p); 
    p.doWhenAddin(); 
    } 
} 

Espero que esto ayude.

+0

¿Qué es esa sintaxis? Y, ¿cómo se usa 'p' antes de asignarlo inicialmente? – jjnguy

+0

Esto no es PHP ... Versión correcta: 'p.doWhenAddin()' – TheLQ

+0

Lo siento, finalmente lancé algo de sintaxis de C++. Este código fuente es Java. El método create processor correspondiente es un método de fábrica que crea la clase apropiada correspondiente a la jerarquía de clases de procesadores, vinculada a la jerarquía de clases de Items. – Baltasarq

7

En Java puede hacer varias expediciones con un patrón de visitante (-like). Las implementaciones de elementos no necesitan contener la lógica de procesamiento, solo necesitan un tipo de método accept().

public interface Item { 
/** stuff **/ 

void processMe(ItemProcessor processor); 

} 

public interface ItemProcessor { 

void process(MusicBox box); 

void process(SoccerBall ball); 

//etc 

} 

public class MusicBox implements Item { 

    @Override 
    public void processMe(ItemProcessor processor) { 
    processor.process(this); 
    } 

} 

public class ItemAddingProcessor implements ItemProcessor { 

    public void add(Item item) { 
    item.processMe(this); 
    } 

    @Override 
    public void process(MusicBox box) { 
    //code for handling MusicBoxes 
    //what would have been inside if (item instanceof MusicBox) {} 
    } 

//etc 
} 
+0

Hmm. Me da algunas ideas, pero en tu código el elemento de interfaz ahora tiene una dependencia en ItemProcessor que tiene una dependencia en las clases MusicBox y SoccerBall (así que no estoy seguro de si esto incluso compilaría) –

+0

Se compilaría, puedes tener dependencias cruzadas entre clases en Java. Sin embargo, no puede tener dependencias corss entre .jars, por lo que no funcionará si Item y ItemProcessor están en diferentes .jars. – amorfis

+0

@Jason: sí, se compila :) Es solo una forma de darle la vuelta a las cosas para que en lugar de tener un método que intente averiguar cómo manejar un tipo de artículo en particular, tenga los diferentes tipos de elementos que se pasen a un método que tiene su manejo. El acoplamiento de tiempo de compilación puede ser una ventaja ya que, si tuviera esa instancia de bloqueo if-else en muchos lugares alrededor de la aplicación, es mucho más fácil perder una actualización cuando crea un nuevo tipo de elemento, que cuando todos implementan una interfaz eso cambió – Affe

0

¿Por qué no definir alguna función de devolución de llamada a la interfaz del artículo?

public Interface Item { 
    void onCallBack(); 
} 

Luego, en cada clase que implementa un elemento, como MusicBox, debe implementar la función de devolución de llamada.

public class MusicBox { 
    @override 
    public void onCallBack() { 
    // business logic 
    ... 
    ... 
    } 
} 

A continuación, puede crear un despachador, cuyo nombre es "SpecialItemProcessor".

public SpecialItemProcessor { 
    private final Item _item; 

    public SpecialItemProcessor(Item item) { 
    _item = item; 
    } 

    public dispatch() { 
    _item.onCallBack() 
    } 
} 

Y luego, en la clase de cliente que contiene el SpecialItemProcessor simplemente podría llamar al método, como:

public void XXXX() { 
    .... 
    SpecialItemProcessor specialItemProcessor = new SpecialItemProcessor(new MusicBox()); 
    specialItemProcessor.dispatch(); 
    .... 
} 

En realidad, en C++, este es el enlace dinámico. Y esta es la razón por la cual existe una clase abstracta pura ...

+1

Lea la pregunta: "donde quiero hacer algo diferente para cada tipo de elemento, ** pero no quiero definir esa acción en las diferentes clases de elementos ** (MusicBox, TypeWriter, SoccerBall). " –