假设我有两个类:Animal和Zoo,它们有包含Animal实例的私有List。
我想要返回迭代器的原因是避免定义setter、getter和删除方法。
这会破坏封装吗?
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;
}
}
在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( ...
尽管在某些情况下返回迭代器是可以接受的,但在这种特殊情况下,iterator()
方法会破坏封装,因为该类提供了一个变异animalList
的方法。
因此,获得迭代器并将迭代与对addAnimal
的调用混合在一起的代码将导致异常。
是的,它破坏了封装。ArrayList的迭代器具有remove()
方法。
Zoo zoo = new Zoo();
// .....
for (Iterator<Animal> i = zoo.iterator(); i.hasNext(); ) {
i.remove();
}
最好提供返回不可修改的列表。
public List<Animal> animalList() {
return Collections.unmodifiableList(animalList);
}
我想要返回迭代器的原因是避免定义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
。
您可以使类可迭代:
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);
所以我对这个问题的回答是,最好让你的zoo可迭代,如果你想让它只读,可能会覆盖Iterator
。