package com.binarytree; public interface Node { int getMaxLeafDepth(); void addValue(int value); String toString(); }
Вот пример кода класса, в котором используется рекурсия.
package com.binarytree.procedure; import com.binarytree.Node; public class ProcedureRecursionTree implements Node { private NodeElement root; public ProcedureRecursionTree(int rootValue) { root = new NodeElement(rootValue); } @Override public void addValue(int newValue) { addValue(root, newValue); } private void addValue(NodeElement node, int newValue) { if (newValue < node.value) { node.left = addTo(node.left, newValue); } else if (node.value < newValue) { node.right = addTo(node.right, newValue); } } private NodeElement addTo(NodeElement node, int newValue) { if (node != null) { addValue(node, newValue); return node; } else { return new NodeElement(newValue); } } @Override public int getMaxLeafDepth() { return getMaxLeafDepthFrom(0, root); } private int getMaxLeafDepthFrom(int depth, NodeElement node) { if (node == null) { return depth; } return 1 + Math.max(getMaxLeafDepthFrom(depth, node.left), getMaxLeafDepthFrom(depth, node.right)); } @Override public String toString() { return toString(root); } private String toStringSubnode(NodeElement node) { if (node != null) { return toString(node); } return null; } private String toString(NodeElement node) { return String.format("(%s, %s, %s)", node.value, toStringSubnode(node.left), toStringSubnode(node.right)); } }Это класс, в котором хранятся данные
package com.binarytree.procedure; public class NodeElement { int value; int parentNodeValue; NodeElement left; NodeElement right; public NodeElement(int value) { this.value = value; } }Ну и? Все нормально на первый взгляд. Методы маленькие, дублирования нет, все вполне читабельно. Да, но тут код пахнет другим - класс ProcedureRecursionTree инкапсулируя NodeElement root рекурсивно проходится по всем дочерним узлам/листьям root ноды. Это удобно - все ноды одного типа. Но это не по OOP. Правило простое. Метод объекта должен работать только с полями своего объекта, иначе метод должен быть перемещен в тот объект, чьи данные использует интенсивнее всего. Если же данные не возможно расширить новым методом, тогда создается новый класс, их инкапсулирующий (или наследующий - что применимее), с последующим переносом в него исходного метода. Вот этой задачкой я и предлагаю заняться. Рефакторинг, как оказалось недавно, в более широком виде, Фаулеровский и называется "Преобразования процедурного проекта в объекты (Convert Procedural Design to Objects)", я же его раньше называл "замена рекурсии делегированием". Вывод пока напрашивается громкий - там где OOP там не должно быть рекурсии с передачей данных. Вот кстати тест:
package com.binarytree; import static org.junit.Assert.assertEquals; import org.junit.Test; import com.binarytree.Node; public class TreeTest { Node createNode(int rootValue) { return new ProcedureRecursionTree(rootValue); } @Test public void testAddLeftToRoot() { Node node = createNode(5); node.addValue(4); assertEquals("(5, (4, null, null), null)", node.toString()); } @Test public void testAddRightToRoot() { Node node = createNode(5); node.addValue(6); node.addValue(4); assertEquals("(5, (4, null, null), (6, null, null))", node.toString()); } @Test public void testStartSecondLevel() { Node node = createNode(5); node.addValue(6); node.addValue(12); node.addValue(3); assertEquals("(5, (3, null, null), " + "(6, null, (12, null, null)))", node.toString()); } @Test public void testGetDepthFrom3LevelOnlyRightTree() { Node node = createNode(5); node.addValue(6); node.addValue(12); assertEquals(3, node.getMaxLeafDepth()); assertEquals("(5, null, (6, null, (12, null, null)))", node.toString()); } @Test public void testGetDepthFrom3LevelLeftRightTree() { Node node = createNode(5); node.addValue(1); node.addValue(2); assertEquals(3, node.getMaxLeafDepth()); assertEquals("(5, (1, null, (2, null, null)), null)", node.toString()); } @Test public void testGetDepthFrom4LevelleftRightTree() { Node node = createNode(5); node.addValue(6); node.addValue(12); node.addValue(16); node.addValue(3); node.addValue(1); node.addValue(0); assertEquals(4, node.getMaxLeafDepth()); assertEquals("(5, (3, (1, (0, null, null), null), null), " + "(6, null, (12, null, (16, null, null))))", node.toString()); } @Test public void testGetDepthFrom5LevelTree() { Node node = createNode(5); node.addValue(4); node.addValue(6); node.addValue(3); node.addValue(7); node.addValue(2); node.addValue(8); node.addValue(1); node.addValue(9); assertEquals(5, node.getMaxLeafDepth()); assertEquals("(5, (4, (3, (2, (1, null, null), null), null), null), " + "(6, null, (7, null, (8, null, (9, null, null)))))", node.toString()); } }Приятного аппетита. Продолжение следует.