如何从原始List类中获取给定子类的元素列表?

时间:2016-05-06 16:41:04

标签: java list oop

很抱歉,如果标题看起来很尴尬。我真的不知道该怎么说这个问题。 假设我有一个方法,我想获得给定Class的所有实例,这是正确的吗?

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; 
    }
}

或者使用泛型更容易解决这个问题? (我不太了解他们,这就是我问这个问题的原因,如果我能在不打扰Generics的情况下做到这一点)。 如果我在使用synthax或其他什么方面犯了任何错误,请再次对不起,只需在此处编写此代码作为示例。 谢谢你的帮助。

1 个答案:

答案 0 :(得分:0)

所以,从评论中,我觉得有一个封装问题。目前,您将所有数据存储为通用Tile对象,因为您的前端只绘制了切片。但是,您希望能够在后端单独处理这些切片(IE:能够获取并移动所有怪物),从而创建您创建的方法的必要性。

这里的根本问题是你的后端永远不应该依赖于你的前端如何处理数据(反之亦然)。从模型元素中隔离视图元素是在软件开发中封装数据的最常见示例。例如,您在视图中想要的是一系列可绘制的图块:

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();
        }
        ...
    } 
}

这个实现很好;视图本身只关心游戏实体作为它可以绘制的图块列表。但是,当您想要使用Tile做更多事情时,问题就存在,而不仅仅是绘制它(正如您现在所经历的那样)。它不再直观地操作;你需要开始挖掘这个通用列表,这显然不是以我们的方式使用。

这是你的后端和视图的隔离进入的地方;您的视图不一定像您的后端那样关心您的数据。所以,你的模型可能有类似的东西:

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();
        }
        ...
    } 
}

这种伪实现具有无关的缺陷,但我相信它说明了这一点;在视图方面,您有一个切片列表,适用于视图。在后端,您可以以一致的方式存储数据,以便后端使用。两端的实施都是直观且流畅的。

这里缺少的是胶水:连接ViewModel的是什么?有几种方法可以解决这个问题,但是,为简单起见,我们只是说视图知道模型,并且可以请求模型中所有游戏实体的位置:

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) {
        ...
    }
}

这将有助于将两者联系起来。但是,重要的是要注意这个实现有一点设计缺陷;由于Model直接将Entity对象传递给View,因此视图现在链接到后端实现细节,这意味着我们已经增加了应该是的元素之间的耦合不同。

那么解决方法是什么?好吧,一个选项是Mediator对象,它在ModelView之间运行。这个中介对象基本上在ModelView之间进行通信,因此他们不需要了解另一个:

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) {
        ...
    }
}

现在我们有一个View,对Model一无所知(反之亦然); Presenter对象处理它们之间的通信。你可以说我们刚刚解决了这个问题; Presenter对象现在必须知道模型对象和视图对象,而不是View。这在某种程度上是有效的(我将在下面讨论)。但是,在ModelView之间充当调解人是Presenter的责任;按SRP 是改变的原因。如果模型或视图改变了它的通信方式,那么这就是为什么Presenter应该改变的唯一原因。如果我们在没有Presenter的情况下查看实现,那么View将有多个原因需要更改(模型更改它的通信方式,想要添加新的视图功能),并且会违反SRP。

但是,通过DIP循环回Presenter了解模型和视图对象,Presenter不应直接依赖于Entity或{{1}的实现};它应该使用接口在模型和视图之间进行通信。所以解决方法很简单:让实体和平铺成为Tile而不是interface(并创建内部使用但不与其他模块通信的抽象类)。然后,Presenter使用契约接口在模型和视图模块之间进行通信,而不是需要了解任何模块的实现细节的abstract class

这个答案相当啰嗦,但希望它传达了我想要传达的观点。我还建议您查看MVP