2016-05-06 4 views
0

Sorry, wenn der Titel peinlich erscheint. Ich wusste nicht wirklich, wie ich die Frage formulieren sollte. Angenommen, ich habe eine Methode, mit der ich alle Instanzen der gegebenen Klasse erhalten möchte, ist das korrekt?Wie erhalte ich eine Liste von Elementen einer bestimmten Unterklasse aus der ursprünglichen Listenklasse?

class Test{ 
    private List<A> list; 
    //... all properly iniatialized on constructor 

    //... 
    public List<A> getSubListOfGivenClass(Class c){ 
     //if i can't assign an instance of a Class c to Class A - error, not subclass 
     if(!A.class.isAssignableFrom(c)) 
      throw new IllegalArgumentException(); 
     List<A> newList = new ArrayList<>(); 
     for(A aItem : list) 
      if(c.isInstance(aItem)) 
       newList.add(aItem); 
     return newList; 
    } 
} 

Oder ist dieses Problem mit Generics einfacher gelöst? (Ich weiß nicht viel über sie, deshalb stelle ich diese Frage, wenn ich das tun kann, ohne mich mit Generics zu beschäftigen). Und wieder tut mir leid, wenn ich einen Fehler mit Synthax oder etwas gemacht habe, schrieb nur diesen Code hier, um als Beispiel zu dienen. Danke für die Hilfe.

+2

Ziemlich sicher, dass Ihre 'getSubListOfGivenClass'-Methode selbst solide ist. Das Bedürfnis, es zu haben, ist für mich fragwürdig. Aber ohne konkretere Details ist es schwer, sicher zu sein, wie man es repariert (wenn es repariert werden sollte). – Ironcache

+0

@Ironcache, um zu vermeiden, eine separate Methode für jede andere Unterklasse, die ich haben kann, anstelle von getSubListOfBInstances(), getSubListOfCInstances(), etc ... –

+1

+1 zu @ Ironcache's Kommentar. Wie planen Sie dies zu nutzen? Ich würde einen Fabrikkonstruktor in Betracht ziehen, der jede Unterklasse erstellt und in jeder Art von Container Verweise auf jede Instanz speichert. –

Antwort

0

Also, von den Kommentaren scheint es mir, als gäbe es ein Einkapselungsproblem. Derzeit speichern Sie alle Daten als generische Objekte Tile, da Ihr Front-End nur Fliesen malt. Du möchtest jedoch, dass du diese Kacheln einzeln in deinem Backend behandeln kannst (IE: in der Lage zu sein, all deine Monster zu bekommen und zu bewegen) und somit die Notwendigkeit der Methode, die du erstellt hast.

Das zugrunde liegende Problem ist, dass Ihr Back-End nie davon abhängig sein sollte, wie Ihr Frontend Daten behandelt (und umgekehrt). Die Segmentierung von Ansichtselementen aus Modellelementen ist das häufigste Beispiel für die Kapselung von Daten in der Softwareentwicklung. Zum Beispiel, was Sie aus Ihrer Sicht wollen, ist eine Reihe von überstreichbaren Kacheln:

public abstract class Tile { 
    public void paintTile() { 
     ... 
    } 
} 

... 

public class PlayerTile extends Tile { 
    @Override 
    public void paintTile() { 
     super.paintTile(); 
     paintPlayer(); 
    } 

    public void paintPlayer() { 
     ... 
    } 
} 

... 

public class View { 
    private List<Tile> tiles; 

    public void paintGame() { 
     ... 
     for (Tile tile : tiles) { 
      tile.paintTile(); 
     } 
     ... 
    } 
} 

Diese Implementierung ist in Ordnung; Die Ansicht selbst interessiert sich nur für die Spielobjekte als eine Liste von Kacheln, die sie malen kann. Das Problem besteht jedoch, wenn Sie mehr mit dem Tile tun möchten, als nur malen (wie Sie jetzt erleben). Es funktioniert nicht mehr intuitiv; Sie müssen anfangen, diese generische Liste zu durchforsten, die eindeutig nicht in der Art und Weise verwendet werden sollte, wie wir sind.

Hier kommt die Trennung von Back-End und View ins Spiel. Ihre Ansicht interessiert sich nicht unbedingt genauso für Ihre Daten wie Ihr Back-End. So könnte Ihr Modell so etwas wie haben:

public abstract class Entity { 
    public void move() { 
     ... 
    } 
} 

... 

public class PlayerEntity extends Entity { 
    @Override 
    public void move() { 
     // User moves player 
    } 
} 

... 

public class MonsterEntity extends Entity { 
    @Override 
    public void move() { 
     // Moved by A* algorithm 
    } 
} 

... 

public class Model { 
    private PlayerEntity player; 
    private List<MonsterEntity> monsters; 

    public void gameRound() { 
     ... 
     player.move(); 
     ... 
     for (MonsterEntity monster : monsters) { 
      monster.move(); 
     } 
     ... 
    } 
} 

Diese pseudo-Implementierung irrelevant Fehler hat, aber ich glaube, es ist der Punkt zeigt; Auf der Betrachtungsseite haben Sie eine Liste von Kacheln, und das funktioniert für die Ansicht. Auf der Backend-Seite haben Sie Ihre Daten in einer Weise gespeichert, die für das Back-End kohärent ist. Die Implementierung ist an beiden Enden intuitiv und flüssig.

Was hier fehlt ist der Kleber: Was verbindet die View und Model? Es gibt mehr Möglichkeiten, dies zu handhaben, aber aus Gründen der Einfachheit, kann nur sagen, dass der Blick über das Modell kennt, und kann die Position aller Spiele Einheiten innerhalb des Modells anfordern:

public class Model { 
    private PlayerEntity player; 
    private List<MonsterEntity> monsters; 

    public void gameRound() { 
     ... 
    } 

    public List<Entity> getEntities() { 
     List<Entity> entities = Lists.newArrayList(); 
     entities.add(player); 
     entities.addAll(monsters); 
     ... 
     return entities; 
    } 
} 

... 

public class View { 
    private Model model; 
    private List<Tile> tiles; 

    public View(Model model) { 
     this.model = model; 
    } 

    public void paintGame() { 
     ... 
     List<Entity> entities = model.getEntities(); 
     tiles = convertToTiles(entities); 
     ... 
     for (Tile tile : tiles) { 
      tile.paintTile(); 
     } 
     ... 
    } 

    private List<Tile> convertToTiles(List<Entity> entities) { 
     ... 
    } 
} 

Dies wird dazu dienen, verbinde die beiden. Es ist jedoch wichtig zu beachten, dass diese Implementierung ein Designfehler aufweist. da die Model direkt Entity Objekt an die View übergibt, ist die Ansicht jetzt mit Back-End-Implementierungsdetails verknüpft, was bedeutet, dass wir die Kopplung zwischen Elementen erhöht haben, die eindeutig sein sollten.

Also, was ist die Problemumgehung? Nun, eine Option ist Mediator Objekt, das zwischen Model und View arbeitet.Dieses Mediator Objekt kommuniziert im Wesentlichen zwischen dem Model und View, so dass sie nicht brauchen, um zu wissen nichts über die anderen:

public class Model { 
    private PlayerEntity player; 
    private List<MonsterEntity> monsters; 

    public void gameRound() { 
     ... 
    } 

    public List<Entity> getEntities() { 
     List<Entity> entities = Lists.newArrayList(); 
     entities.add(player); 
     entities.addAll(monsters); 
     ... 
     return entities; 
    } 
} 

... 

public class View { 
    private Presenter presenter; 
    private List<Tile> tiles; 

    public View(Presenter presenter) { 
     this.presenter = presenter; 
    } 

    public void paintGame() { 
     ... 
     List<Tile> tiles = presenter.getTiles(); 
     ... 
     for (Tile tile : tiles) { 
      tile.paintTile(); 
     } 
     ... 
    } 
} 

... 

public class Presenter { 
    private Model model; 

    public Presenter(Model model) { 
     this.model = model; 
    } 

    public List<Tile> getTiles() { 
     return convertToTiles(model.getEntities()); 
    } 

    private List<Tile> convertToTiles(List<Entity> entities) { 
     ... 
    } 
} 

Jetzt haben wir ein View, dass nichts weiß über die Model (und umgekehrt); Das Presenter Objekt behandelt die Kommunikation zwischen ihnen. Sie könnten argumentieren, dass wir das Problem gerade verschoben haben; Das Objekt Presenter muss jetzt nicht nur die View, sondern auch die Modellobjekte und die Ansichtsobjekte kennen. Dies ist bis zu einem gewissen Grad gültig (darauf werde ich später eingehen). Als Vermittler zwischen den Model und View fungiert jedoch die Verantwortung der Presenter; von SRP ist dieser sein Grund zu ändern. Wenn das Modell oder die Ansicht ändert, wie es kommuniziert, dann ist dies der einzige Grund, warum sich die Presenter ändern sollte. Wenn wir uns die Implementierung ohne die Presenter ansehen, dann hätte die View mehrere Gründe zu ändern (Modell ändert, wie es kommuniziert, möchte neue Ansichtsfunktionalität hinzufügen) und würde SRP verletzen.

jedoch die Presenter über beiden Modelle und Ansicht Objekte zu wissen Schleife zurück, von DIP, der Moderator sollte nicht direkt über die Umsetzung der Entity oder Tile wird abhängig; Es sollte über Schnittstellen zwischen dem Modell und der Ansicht kommunizieren. Die Problemumgehung ist einfach: Entity und Tile be und interface anstelle von abstract class (und abstrakte Klassen erstellen, die intern verwendet werden, aber nicht an andere Module kommuniziert werden). Dann kommuniziert der Presenter zwischen den Modell- und Ansichtsmodulen unter Verwendung von vertraglichen Schnittstellen, anstatt die Presenter zu kennen, die irgendetwas über die Implementierungsdetails jedes Moduls wissen muss.

Diese Antwort ist ziemlich langatmig, aber hoffentlich vermittelt sie die Punkte, die ich vermitteln will. Ich empfehle auch einen Blick auf MVP.

+0

Ich sehe, nun, ich denke, ich könnte schwierig sein, dies zu implementieren, da meine GUI (View class?) Nicht geändert werden soll, es erweitert Observable und soll von etwas beobachtet werden. Aber ich denke, ich verstehe die Back-End-Änderungen, die getan werden müssen, so dass ich in meiner Room-Klasse, die den aktuellen Room definiert, eine Art Instanzvariable haben wird, die den Hero, Monster und Static Tiles repräsentiert. So ähnlich wie dein Model. –

Verwandte Themen