Vereinfachung instanceof

Status
Nicht offen für weitere Antworten.

Alex04

Bekanntes Mitglied
Hallo,
ich hätte ne frage zu nem sehr unschönen Code-Teil in einem Programm:

Code:
...
while(it.hasNext()) {
            Object current = it.next();
            System.out.println(current.toString());

            if(current instanceof StartImpl) {
                //Start node
                this.transformStartImpl((StartImpl) current);
            }

            else if(current instanceof EndImpl) {
                //Transform EMF end node to generator end node
                this.transformEndImpl((EndImpl) current);
            }

            else if(current instanceof ActivityImpl) {
                //Transform EMF activity to generator activity
                this.transformActivityImpl((ActivityImpl) current);
            }

            else if(current instanceof ControlFlowImpl){
                //Transform EMF ControlFlow to generator ControlFlow
                this.transformControlFlowImpl((ControlFlowImpl) current);
            }

            else if(current instanceof UIActionImpl) {
                //Transform EMF UIAction to generator UIAction
                this.transformUIActionImpl((UIActionImpl) current);
            }

            else if(current instanceof ANDImpl) {
                //Transform EMF AND to generator AND

            }

            else if(current instanceof XORImpl) {
                //Transform EMF XOR to generator XOR

            }

            else if(current instanceof DomainModelActionImpl) {
                //Transform EMF DomainModelAction to generator DomainModelAction
                this.transformDomainModelActionImpl((DomainModelActionImpl) current);
            }

            else if(current instanceof UseCaseImpl){
                this.transformUseCaseImpl((UseCaseImpl) current);
            }
        }
...

Kann man das nicht irgendwie etwas schöner lösen?
Jede transform Methode hat eine andere Funktionalität. Außerdem gibt es auch Objekte in current die nicht transformiert werden müssen, d.h das keine transform Methode aufgerufen wird.

Wäre für jede Vereinfachung dankbar!
Lg
Alex
 
du könntest in jeder dieser Klassen eine Interface-Methode implementieren, die enweder die korrekte andere Methode aufruft,
oder einen Enum-Typ-Wert zurückgibt, der für ein switch/ für eine Map genutzt wird,

alles in allem kann das aber vom Quellcode her nicht kürzer werden als dieses if/ else,
nur die Performance ließe sich gegenüber instanceof steigern, aber darauf kommt es wohl nicht so an?

irgendwo muss die Verknüpfung aus Klasse und Methode nunmal stehen, weniger als 1-3 Zeilen pro Fall ist schwer
 
[HIGHLIGHT="Java"]public abstract class TransformImpl{

public abstract void transform ( whatever "this" is in your code );
}

public class StartImpl extends TransformImpl{

@Override
public abstract void transform ( whatever "this" is in your code ){
// ...
}

// analog zu allen anderen Klassen[/HIGHLIGHT]

dann:

[HIGHLIGHT="Java"]while(it.hasNext(){
Object next = it.next();
if(next instanceof TransformImpl){
((TransformImpl)next).transform(this);
}
}[/HIGHLIGHT]
 
mit ein wenig Polymorphie und Generics geht so einiges, allerdings geht aus dem code ziemlich nichts hervor, außer dass es hässlich ist. Da fallen mir leider auch keine konkreteren Tipps zum design ein, außer dem Vorschlag es etwas hübscher zu machen. Wenn es möglich ist, sollte jedes dieser objekte sich irgendwie "selbst transformieren" was auch immer dieses "transformieren" heißen soll, etwa so:
[HIGHLIGHT="Java"]
//interface
interface Transformable{
void transformYourself(Transformator t);
}

//viele viele klassen, die mit instanceof unterschieden werden
class A implements Transformable{
void transformYourself(Transformator t){
stuff=t.tellMeSomeImportantStuffHowToTransformMyself();
//sich selbst umformen
}
}

class B implements Transformable{...
...
}

//liste füllen
Collection<Transformable> list=new LinkedList<Transformable>();
list.add(new A());
list.add(new B());

//irgendwie umtransformieren
Transformator trafo=new ExteremelyIntelligentTransformatorOfWhatever();
for(Transformable e:list){
e.transformYourself(trafo);
}
[/HIGHLIGHT]
sowas in der Art...

edit: hdi war schneller...
 
Wäre das dann sowas?!😕
[HIGHLIGHT="Java"]
Transformator{
void transform(StartImpl x){
...
}
void transform(EndImpl y){
...
}
}

interface Transformable{
void invokeTransformationMethod(Transformator t);
}

StartImpl implements Transformable{
void invokeTransformationMethod(Transformator t){
t.transform(this);
}
}

EndImpl implements Transformable{
void invokeTransformationMethod(Transformator t){
t.transform(this);
}
}
[/HIGHLIGHT]
Ich kann nur hoffen, dass ich das jetzt falsch verstanden hab, denn das wäre imho nur eine Krücke dafür:
[HIGHLIGHT="Java"]
public class _ {
static class X{}
static class A extends X{}
static class B extends X{}

public static void f(A a){ System.out.println("a"); }
public static void f(B a){ System.out.println("b"); }
public static void f(X a){ System.out.println("x"); }

public static void main(String[] args){
X x=new B();
f(x); //gibt "x" und nicht etwa "b" aus
}
}
[/HIGHLIGHT]
bzw. eigentlich keinen millimeter besser, als diese gruselige instanceof-abfrage, nur auf 100 Klassen statt einer verteilt... Oder?
 
Sieht dann in etwa so aus:
[HIGHLIGHT="Java"]current.accept(this);

visit(StartImpl impl){
//do something
}

visit(EndImpl impl){
//do something
}[/HIGHLIGHT]

In den 'Impls' steht dann eine einfache, generelle visit Methode:
[HIGHLIGHT="Java"]
public void accept(Visitor visitor){
visitor.visit(this);
}[/HIGHLIGHT]
Man umgeht damit die Single Dispatch Limitierung und spart sich das instanceof.
Das traversieren über die Objekte kann ebenfalls direkt in die visit Implementierung verlagert werden.
Somit baust du in dein Model eine Art wiederverwendbarer 'Switch' und die Möglichkeit über die Objekte zu traversieren.
 
Auch nicht viel hübscher:
Code:
interface TransHelper { void trans(Object o); }

private static Map<Class<?>, TransHelper> map = new HashMap<Class<?>,TransHelper>();
{
    map.put(StartImpl.class, new TransHelper(){ public void trans(Object o){transformStartImpl((StartImpl) o)}}); 
    map.put(EndImpl.class, new TransHelper(){ public void trans(Object o){transformEndImpl((EndImpl) o)}}); 
    ...
}

...
while(it.hasNext()) {
            Object current = it.next();
            Class<?> clazz = current.clazz;
            if (map.contains(clazz) {
                 map.get(clazz).trans(current);
            } 
}
 
Das Visitor-Pattern hat auch Nachteile, insbesondere ist es aufwendig, zusätzliche Methoden hinzuzufügen. In diesem Fall ist es schwierig zu sagen, was die bessere Lösung ist, weil das Beispiel aus dem Kontext gerissen ist. Meine Variante kann sicher auch noch verbessert werden (ich hatte mal in einem Thread was über eine DependentMap gepostet, dann wären wir schon mal typsicher).

Prinzipiell kann man hier gut sehen, dass in Java eine Art "switch-statement für Typen" fehlt (andere Sprachen haben das als "Pattern Matching", z.B. Haskell, Erlang und ihr wisst schon welche noch...), wodurch das Visitor-Pattern in den meisten Fällen überflüssig ist.
 
Das Visitor-Pattern hat auch Nachteile, insbesondere ist es aufwendig, zusätzliche Methoden hinzuzufügen. In diesem Fall ist es schwierig zu sagen, was die bessere Lösung ist, weil das Beispiel aus dem Kontext gerissen ist.
Da kann ich nur zustimmen. Ohne zusätzlich Randbedingungen ist ein Visitor erstmal eine saubere Lösung, aber je nach Kontext kann es auch die falsche Wahl sein.
 
Hallo,
vielen Dank schonmal für eure Hilfe,
Problem ist, das ich es gerne ohne Polymorphismus lösen möchte, da die Klassen wie StartImpl usw. nicht verändert werden sollen!
Hier fällt dann ja auch das Visitor pattern raus!
Die Lösung von Landei passt damit am besten und ich werde diese mal versuchen...

Besten Dank nochmal
Lg
Alex
 
Status
Nicht offen für weitere Antworten.

Neue Themen


Zurück
Oben