Антипаттерны проектирования
Считаю важным знать не только удачные способы решения задач, но и возможные ошибки при их решении.
Интерфейс для констант - используем Java интерфейс не по назначению;
Вресенная сложность - добавляем ненужные сущности в иерархию классов.
Есть мнение, что необходимость в некоторых паттернах вызвана недостатком конкретного языка программирования. Может и так, но раз уж используемый нами язык не предоставляет нативный способ решения проблемы, то почему бы не воспользоваться идеей, которая уже кем-то проверена и является достаточно эффективной, если не оптимальной.
Паттерн Iterator в Java - Итератор
Iterator - это поведенческий шаблон проектирования, который позволяет пройтись по всем элементам некоторого составного объекта. Одним из важных условий при реализации паттерная является то, что итератор должен гарантировать нераскрытие внутреннего устройства объекта.
В Java итераторы очень активно используются в Collection Framework. Для этих целей создан специальный interface с одноименным названием java.util.Iterator. Интерфейс содержит следующие методы:
hasNext() - возвращает буелове значение в зависимости от того, есть ли еще элементы в коллекции.
next() - возвращает следующий элемент в коллекции. В соответствии с API метод должен бросать NoSuchElementException если все элементы уже пройдены.
remove() - удаляет элемент, который был возвращен последним вызовом next. К этому методу есть некоторые вопросы :) - почему собственно он находится в интерфейсе, ведь не каждая коллекция позволяет удалять элементы, в конце концов есть read only коллекции. Именно поэтому начиная с Java 1.8 метод remove описан как default с такой реализацией по умолчанию: default void remove() { throw new UnsupportedOperationException("remove"); }
Давайте опишем некоторый класс и реализуем для него Iterator. Предположим у нас есть ужасный монстр, который состоит из головы, руки и ноги. Для каждой части есть сеттер, геттер, а так же для удобства метод, который говорит нам о наличии либо отсутствии части.
public class Monster {
private Head head = new Head();
private Hand theOnlyHand = new Hand();
private Leg theOnlyLeg = new Leg();
public Head getHead() { return head; }
public void setHead(Head rightHead) { this.head = rightHead; }
public boolean hasHead() { return head != null; }
public Hand getTheOnlyHand() { return theOnlyHand; }
public void setTheOnlyHand(Hand theOnlyHand) { this.theOnlyHand = theOnlyHand; }
public boolean hasHand() { return theOnlyHand != null; }
public Leg getTheOnlyLeg() { return theOnlyLeg; }
public void setTheOnlyLeg(Leg theOnlyLeg) { this.theOnlyLeg = theOnlyLeg; }
public boolean hasLeg() { return theOnlyLeg != null; }
public interface Part { }
public static class Head implements Part { }
public static class Hand implements Part { }
public static class Leg implements Part { }
public static void main(String[] args) {
// создадим монстра
Monster monster = new Monster();
// отрубим ему голову
monster.setHead(null);
// создадим итератор
Iterator iterator = new MonsterIterator(monster);
// есть ли у него еще части?
System.out.println(iterator.hasNext()); // true
// какая следующая часть
System.out.println(iterator.next()); // Hand
// а еще есть?
System.out.println(iterator.hasNext()); // true
// какая следующая часть
System.out.println(iterator.next()); // Leg
// а если отрубить
iterator.remove();
// а еще есть?
System.out.println(iterator.hasNext()); // false
// а что с ногой-то?
System.out.println(monster.getTheOnlyLeg()); // null
}
}
Класс итератора можно разместить в том же файле. Пусть индекс (index) отвечает за так называемый курсор в итераторе. Значение -1 будет установлено при создании нового объекта. Значения 0, 1 и 2 будут соответствовать голове, руке и ноге.
Реализуем методы hasNext, next и remove. Кстати, remove можно не реализовывать начиная с версии Java 1.8. В этом случае будет использована реализация по-умолчанию (смотрите начало статьи).
class MonsterIterator implements Iterator{
private int index = -1;
private Monster m;
public MonsterIterator(Monster m) {
this.m = m;
}
public boolean hasNext() {
if (index == -1) return m.hasHead() || m.hasHand() || m.hasLeg();
if (index == 0) return m.hasHand() || m.hasLeg();
if (index == 1) return m.hasLeg();
return false;
}
public Monster.Part next() {
if (index == -1) {
if (m.hasHead()) { index = 0; return m.getHead(); }
if (m.hasHand()) { index = 1; return m.getTheOnlyHand(); }
if (m.hasLeg()) { index = 2; return m.getTheOnlyLeg(); }
}
if (index == 0) {
if (m.hasHand()) { index = 1; return m.getTheOnlyHand(); }
if (m.hasLeg()) { index = 2; return m.getTheOnlyLeg(); }
}
if (index == 1) {
if (m.hasLeg()) { index = 2; return m.getTheOnlyLeg(); }
}
throw new NoSuchElementException();
}
public void remove() {
if (index == -1) throw new IllegalStateException();
if (index == 0)
if (m.hasHead()) m.setHead(null);
else throw new IllegalStateException();
if (index == 1)
if (m.hasHand()) m.setTheOnlyHand(null);
else throw new IllegalStateException();
if (index == 2)
if (m.hasLeg()) m.setTheOnlyLeg(null);
else throw new IllegalStateException();
}
}
Реализация итератора получилась не очень красивая. Просто я хотел показать, что итерировать можно не только по коллекции, а по любому составному объекту.
Для того, чтобы можно было удобнее создавать экземпляр итератора был введен интерфейс Iterable с методом iterator(). Можно описать класс Monster как реализующий этот интерфейс и реализовать метод iterator таким образом:
public class Monster implements Iterable {
...
public Iterator
iterator() {
return new MonsterIterator(this);
}
...
}
Do'stlaringiz bilan baham: |