Javenue logo

Javenue

Программирование на Java

Информационные технологии

Паттерн Facade на Java - Фасад

Вернемся к рассмотрению паттернов проектирования. На мой взгляд, паттерн Facade (Фасад) встречается достаточно часто и соответственно заслуживает внимания.

Нужен он вот для чего. При написании кода программисту приходится использовать различные third-party библиотеки. Часто API библиотек довольно абстрактное. Некоторые методы классов могут быть вообще не нужны или содержать параметры, которые не существенны для решения конкретной задачи.

Далее, для получения результата могут понадобиться длинные цепочки вызовов методов или же сам результат будет не совсем в подходящем виде. Худший вариант - это использование плохо спроектированной библиотеки, с чем тоже иногда приходится работать.

Все эти проблемы можно решить с помощью паттерна Facade.

Класс, реализующий этот паттерн, является неким интерфейсом, сочетающим в себе только необходимую функциональность в удобном для пользователя виде. Так можно скрыть реализацию сложных частей кода, уменьшить количество зависимостей от внешней библиотеки и, наконец, работать со множеством объектов через прозрачный и удобный интерфейс. Это, в свою очередь, гарантирует более качественную и простую поддержку всей системы.

Для тех, кому куча сложноподчиненных предложений не пришлась по вкусу, я подготовил небольшой пример. Этот пример основан на моем коде из проекта, в котором я принимал участие.

Некоторая сущность имела DOM представление (org.w3c.dom). Для того, чтобы сосредоточить работу с DOM-деревом в одном месте и предоставить централизованный способ работы с документом был использован паттерн Фасад.

В частности был создан интерфейс INodeAccessor (классы, которые я буду приводить, упрощены):

import org.w3c.dom.Node;
import java.util.List;
import java.util.Map;

public interface INodeAccessor {
    Node getChildNodeByName(Node parentNode, String nodeName);

    List getSiblingChildNodesByName(Node parentNode, String nodeName);

    Map getChildNodesMap(Node parentNode);

    Node createChildNode(Node parentNode, String nodeName);

    void setNodeValue(Node node, String value);

    String getNodeValue(Node node);

    Node getParentNode(Node node);

    Node removeChildNode(Node parentNode, Node childNode);

    boolean hasValue(Node node);
}

Думаю, имена методов достаточно понятны.

Далее был создан абстрактный класс на основании этого интерфейса и несколько конкретных классов. Один из них, например, добавлял функциональность валидации данных по xml-схеме при записи значений в ноды и создании новых нод.

Я приведу самый простой из этих классов (для дальнейшей простоты, от абстрактного класса я избавлюсь, а его методы перенесу в конкретный класс):

public class DefaultNodeAccessor implements INodeAccessor {
    public Node getChildNodeByName(Node parent, String nodeName) {
        for (Node node = parent.getFirstChild();
             node != null; node = node.getNextSibling()) {
            if (node.getNodeType() == Node.ELEMENT_NODE
                    && node.getNodeName().equals(nodeName)) {
                return node;
            }
        }
        return null;
    }

    public List getSiblingChildNodesByName(Node parent, String nodeName) {
        List foundNodes = new ArrayList();
        for (Node node = parent.getFirstChild();
             node != null; node = node.getNextSibling()) {
            if (node.getNodeType() == Node.ELEMENT_NODE
                    && node.getNodeName().equals(nodeName)) {
                foundNodes.add(node);
            }
        }
        return foundNodes;
    }

    public Map getChildNodesMap(Node parent) {
        Map foundNodes = new HashMap();
        for (Node node = parent.getFirstChild();
             node != null; node = node.getNextSibling()) {
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                Object obj = foundNodes.get(node.getNodeName());
                if (obj == null) {
                    foundNodes.put(node.getNodeName(), node);
                } else {
                    if (obj instanceof Node) {
                        List nodeList = new ArrayList();
                        nodeList.add(obj);
                        nodeList.add(node);
                        foundNodes.put(node.getNodeName(), nodeList);
                    } else {
                        ((List) obj).add(node);
                    }
                }
            }
        }
        return foundNodes;
    }

    public String getNodeValue(Node node) {
        if (hasValue(node)) {
            return node.getFirstChild().getNodeValue();
        }
        return "";
    }

    public void setNodeValue(Node node, String value) {
        if (node.getFirstChild() != null) {
            node.getFirstChild().setNodeValue(value);
        } else {
            Node textNode = node.getOwnerDocument().createTextNode(value);
            node.appendChild(textNode);
        }
    }

    public Node getParentNode(Node node) {
        return node.getParentNode();
    }

    public Node removeChildNode(Node parent, Node child) {
        return parent.removeChild(child);
    }

    public Node createChildNode(Node parentNode, String nodeName) {
        Document ownerDocument;
        if (parentNode.getNodeType() == Node.DOCUMENT_NODE) {
            ownerDocument = (Document) parentNode;
        } else {
            ownerDocument = parentNode.getOwnerDocument();
        }
        Node node = ownerDocument.createElement(nodeName);
        parentNode.appendChild(node);
        return node;
    }

    public boolean hasValue(Node node) {
        if (node != null) {
            if (node.getFirstChild() != null) {
                if (node.getFirstChild().getNodeValue() != null &&
                        node.getFirstChild().getNodeValue().trim().length() != 0) {
                    return true;
                }
            }
        }
        return false;
    }
}

Кроме выполнения своих прямых обязанностей, данный класс пригодился в небольшом тестировочном фреймворке для создания DOM-структур.

Если у вас есть какие-либо вопросы по приведенному примеру, буду рад объяснить подробнее.

Ну и несколько советов. Используйте паттерн Facade с умом. Не нужно писать новый метод только для того, чтобы вызов другого метода занимал меньше символов. Имена методам давайте информативные. Главное, чтобы Фасад был удобен и прост в использовании. Удачи.



Комментариев: 0

  Выйти

  * для публикации комментариев нужно