返回私有List(在类中)的迭代器被认为是不好的做法吗?

时间:2015-07-03 15:59:37

标签: java oop iterator

假设我有两个类:Animal和Zoo,它有包含Animal实例的私有List。

我想要返回迭代器的原因是为了避免定义setter和getter以及remove方法。

这会破坏封装吗?

class Zoo{
    private List<Animal> animalList;
    public Zoo(){
        animalList = new ArrayList<Animal>();
    }
    public void addAnimal(Animal animal){
        animalList.add(animal);
    }
    public Iterator<Animal> iterator(){
        return animalList.iterator();
    }
}

class Animal{
    private String name;
    private double weight, height;

    Animal(String name, double weight, double height){
        this.name = name;
        this.weight = weight;
        this.height = height;
    }
}

5 个答案:

答案 0 :(得分:3)

虽然有些情况下返回迭代器是可以接受的,但在这种特殊情况下,iterator()方法会破坏封装,因为该类提供了一种变异animalList的方法。

结果,获取迭代器的代码,以及将迭代与addAnimal的调用混合在一起会导致异常。

答案 1 :(得分:3)

在Iterable接口之外使用Iterator非常罕见。 我建议不要这样做。

我认为这会更好:

public Iterable<Animal> animals(){
    return Collections.unmodifiableList( animalList );
}

for(Animal a : zoo.animals()) {
    //do something
}

我反对Zoo implements Iterable<Animal>;不要引入不必要的关系。

在Java 8中,更优选的做法可能是使用Stream而不是Iterable

public Stream<Animal> animals(){
    return animalList.stream();
}

zoo.animals().forEach( ... 

答案 2 :(得分:2)

是的,它打破了封装。 ArrayList的迭代器具有remove()方法。

    Zoo zoo = new Zoo();
    // .....
    for (Iterator<Animal> i = zoo.iterator(); i.hasNext(); ) {
        i.remove();
    }

最好提供返回不可修改的列表。

    public List<Animal> animalList() {
        return Collections.unmodifiableList(animalList);
    }

答案 3 :(得分:1)

  

我想要返回迭代器的原因是为了避免定义setter和getter以及删除方法。

没有充分理由不将这些方法添加到您的课程中。你的代码肯定打破了封装,但我们稍后会讨论。现在,可以肯定Zoo打破了一个称为Tell Don't Ask的优秀OO设计规则。

使用您当前的实现,客户端代码将如下所示:

zoo.iterator().remove();
zoo.iterator().next().getName();

上面的代码实际上是不可读的。什么是理想的是有这样的东西:

zoo.removeLastAnimal();
zoo.getNextAnimalName();

Zoo类可以修改如下:

class Zoo{
    private List<Animal> animalList;
    Iterator<Animal> iterator;

    public Zoo(){
        animalList = new ArrayList<Animal>();
        iterator = animalList.iterator();

    }

    public void removeLastAnimal() {
        try {
           iterator.remove();
        } catch(IllegalStateException e) {
            //handle exception
        }
    }      

    public String getNextAnimalName() {
       if(iterator.hasNext()) {
          return iterator.next().getName();
       }
    }  
}

这样,您将隐藏外部世界的实现细节。您将在使用代码时防止客户端代码出错。您将能够处理异常情况,而不是要求客户端代码处理它们。这就是你得到的好encapsulation

答案 4 :(得分:0)

你可以让你的类可迭代:

public class Zoo implements Iterable<Animal> {

    ...

    public Iterator<Animal> iterator() {
        return animalList.iterator();
    }
}

然后你可以做这样的事情:

for (Animal a : zoo) {
    ...
}

如果你返回迭代器,你必须这样做:

for (Animal a : zoo.iterator()) {
    ...
}

这是多余的。

您也可以编写自己的迭代器,以防止用户调用iterator.remove()并修改您的列表:

public class ReadOnlyAnimalIterator implements Iterator<Animal> {

    private Iterator iter;

    public ReadOnlyIterator(List<Animal> list) {
        this.iter = list.iterator();
    }

    public boolean hasNext() {
        return iter.hasNext();
    }

    public Animal next() {
        return iter.next();
    }

    public void remove() {
        throw new UnsupportedOperationException();
    }
}

然后在iterator()方法中:

return new ReadOnlyAnimalIterator(list);

所以我对这个问题的回答是,如果你想让它成为只读的话,最好让你的动物园可迭代,可能会覆盖Iterator