假设我有两个类: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;
}
}
答案 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
。