Thi*_*thi 11 java oop design-patterns java.util.scanner
我只是想知道为什么java.util.Scanner实现了java.util.Iterator?
Scanner实现remove方法并抛出UnsupportedOperationException.
但是,在实现接口时,不应该是一个类,履行接口的契约?
实现iterator和添加抛出异常的方法有什么用?
为什么不避免接口的实现并保持简单?
有人可以争辩说,它被定义为可以扩展的类Scanner可以实现该方法,就像AbstractList有一个抛出一个的add方法UnsupportedOperationException.但是AbstractList是一个abstract班级,而是Scanner一个final班级.
这不是一个糟糕的设计实践吗?
我会说是的,Iterator有一个设计缺陷,并将其抛入与尝试创建不可变Collection实现相同的类别.
它违反了接口隔离原则,并强制开发人员将一个角落案例包含在JavaDocs(臭名昭着UnsupportedOperationException)中,以避免违反Liskov Subsitution Principle.你也可以在Collection#remove方法中找到它.
我相信设计可以通过分解界面,隔离hasNext()并next()进入一个新的(不可变的)接口并让(可变)Iterator接口从中派生来改进:
interface Traversable<E> {
boolean hasNext();
E next();
}
interface Iterator<E> extends Traversable<E> {
void remove();
}
final class Scanner implements Traversable<String> {
}
Run Code Online (Sandbox Code Playgroud)
绝对可以使用更好的名字.由于命名选择不当,请不要关闭这篇文章.
Scanner执行Iterator摆在首位?Scanner在遍历集合的意义上,它不是迭代器.但是a的想法Scanner是提供它被" 扫描 "的输入,这在某种意义上是迭代某些东西(a中的字符String).
我可以看到为什么Scanner会实现Iterator(你要求用例).例如,如果要创建自己的Iterable类型以迭代String指定分隔符:
class ScannerWrapper implements Iterable<E> {
public Scanner scanner;
public ScannerWrapper(Scanner scanner) {
this.scanner = scanner;
}
public Iterator<String> iterator() {
return scanner;
}
}
Scanner scanner = new Scanner("one,two,three");
scanner.useDelimiter(",");
ScannerWrapper wrapper = new ScannerWrapper(scanner);
for(String s : wrapper) {
System.out.println(s);
}
Run Code Online (Sandbox Code Playgroud)
但是如果JDK支持一个Traversable类型并允许增强循环来接受Traversable项目,这也会有效,因为以这种方式从集合中删除可能会抛出一个ConcurrentModificationException,这导致使用迭代器.
那么好的设计呢?不.它违反了ISP,导致合同混乱.这只是一种典型的代码味道.真正的问题是语言缺乏对不变性的支持,这应该允许开发人员指定行为是否应该改变状态,允许行为合同被剥夺其可变性.或类似的规定..
JDK充满了这样的东西(糟糕的设计选择,比如暴露length数组和ImmutableMap上面提到的尝试),现在更改它会导致代码破坏.