Решаю задачку. Наколбасил кода. И в один прекрасный момент немного засутпорился. Есть метод
Он чудный :) но не об этом сейчас. Нужно очень быстро сделать так, чтобы в зависимости от boolean direction параметра метод итерировался по второму циклу (int x) в прямом, либо в обратном порядке (но в том же диапазоне).
Копипаст и if с двумя циклами не катит - дублирование. Выделить внутренности цикла int x тоже не катит - много зависимостей. Да и не интересно - я так уже делал в молодости :)
Некрасивый код стал еще более некрасивым.
Как быть? И тут приходит идея. А что если поспользоваться интерфейсом Iterable? По нему for ходит без пороблем. Выглядеть это будет приблизительно так.
А вот и сам класец. Только внимание, я его сделал таким, что о сам определяет где from где to даже если ты их перепутал местами - то немного неочевидно, фор не работает так, но мне удобно!
Он чудный :) но не об этом сейчас. Нужно очень быстро сделать так, чтобы в зависимости от boolean direction параметра метод итерировался по второму циклу (int x) в прямом, либо в обратном порядке (но в том же диапазоне).
Копипаст и if с двумя циклами не катит - дублирование. Выделить внутренности цикла int x тоже не катит - много зависимостей. Да и не интересно - я так уже делал в молодости :)
Как быть? И тут приходит идея. А что если поспользоваться интерфейсом Iterable? По нему for ходит без пороблем. Выглядеть это будет приблизительно так.
public class XIterator implements Iterable<Integer>{ private int from; private int to; private int x; private boolean increase; public XIterator(int from, int to, boolean increase) { if (increase) { this.from = Math.min(from, to); this.to = Math.max(from, to); } else { this.from = Math.max(from, to); this.to = Math.min(from, to); } this.increase = increase; this.x = this.from; } @Override public Iterator<Integer> iterator() { return new Iterator<Integer>() { @Override public boolean hasNext() { if (increase) { return x <= to; } else { return x >= to; } } @Override public Integer next() { if (increase) { return x++; } else { return x--; } } @Override public void remove() { throw new UnsupportedOperationException(); } }; } }Вот тесты для него
public class XIteratorTest { @Test public void increase() { assertIteratorValues(new XIterator(0, 10, true), "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"); } @Test public void decrease() { assertIteratorValues(new XIterator(0, 10, false), "[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]"); } @Test public void increaseNegative() { assertIteratorValues(new XIterator(-5, 5, true), "[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]"); } private void assertIteratorValues(Iterable<Integer> iterator, String expected) { LinkedList list = new LinkedList(); for (Integer i : iterator) { list.add(i); } assertEquals(expected, list.toString()); } @Test public void decreaseNegative() { assertIteratorValues(new XIterator(-5, 5, false), "[5, 4, 3, 2, 1, 0, -1, -2, -3, -4, -5]"); } @Test public void increaseBadFromTo() { assertIteratorValues(new XIterator(5, -5, true), "[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]"); } @Test public void decreaseBadFromTo() { assertIteratorValues(new XIterator(5, -5, false), "[5, 4, 3, 2, 1, 0, -1, -2, -3, -4, -5]"); } @Test public void zeroIterator() { assertIteratorValues(new XIterator(0, 0, false), "[0]"); assertIteratorValues(new XIterator(0, 0, true), "[0]"); } }
Да... тяжело же в жабе без linq. Печаль:(
ОтветитьУдалитьНе то слово :)
ОтветитьУдалитьСаша, а я не считаю вызов метода в двух местах дублированием кода. То, что ты с нуля написал итератор, является большим злом, как по мне.
ОтветитьУдалитьПосчитай сам: кол-во потенциальных ошибок и проблем при "дублировании" вызовов метода и написании нового велосипеда вообще не сравнимо.
Кстати, у апача есть реверсный итератор: http://commons.apache.org/collections/api-release/org/apache/commons/collections/iterators/ReverseListIterator.html
Велосипеды писать стоит. На них учишься. Не поймешь как оно работает, пока сам не разберешь и не соберешь обратно. Эксперименты, вот чего не стоит бояться. Ошибки делать.
ОтветитьУдалитьКстати, студенты наши реализовывают List'ы, Queue вкачестве домашки - откуда у них это желание, как думаешь? :)
Вообще, как это говорится - сделай это рабочим, сделай это красивым и сделай это быстрым. http://c2.com/cgi/wiki?MakeItWorkMakeItRightMakeItFast
После того, как написал велосипед стоит найти кого-то, кто это сделал уже давно - у него код оптимальнее и сделать замену реализации.
А вообще этот код я даже обсуждать бы не стал, я вчера на него смотрел и он казался ужасным, а сегодня вообще молчу.
Но идея с экстрактом for'a в контексте еще одного типа реаткоринга я заценил. Потому тут образовался этот пост. Уверен, когда-нибудь этот тип рефакторинга будет очень к месту. Люблю их экспериментировать.