13
December
2005

Паттерн Facade (Шаблон Фасад на Java)

Posted in: Паттерны проектирования |

Вернемся к рассмотрению паттернов проектирования. На мой взгляд, паттерн 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 с умом. Не нужно писать новый метод только для того, чтобы вызов другого метода занимал мень�?е символов. �?мена методам давайте информативные. Главное, чтобы Фасад был удобен и прост в использовании. Удачи.

Updated (25.01.2008): Добавлен более сложный пример реализации паттрена Фасад.

7 Comments »

RSS feed for comments on this post. TrackBack URI



Igor
August 5, 2006 #

Я удивляюсь как легко и просто Вам удается описывать паттерны. Огромное спасибо!

19th
September 4, 2006 #

Судя по примерам, это не Facade, а Helper без упоминания о приватности конструктора.
Ещё один момент: функция и метод - это не синонимы.

Ash
October 20, 2006 #

А мне все равно синонимы или нет - я изучаю паттерны. Спасибо за доходчивый пример

walv
October 9, 2008 #

Пример вы?е более похож на реализацию паттерна Медиатор, так как Медиатор описывает новую ф-сть. Фасад же не описывает новый функционал, он как бы интерфейс между двумя групами классов которые не должны знать ничего друг о друге.
С теоретическим описание я вполне согласен, но пример мне кажется неудачным. Это действительно похоже на Helper.

roman
November 25, 2008 #

Что за паттерн такой - Helper. Откуда вы его взяли?

Sergey
October 14, 2009 #

>Что за паттерн такой - Helper. Откуда вы его взяли?

+1!

There is no “Helper” in GoF:
http://www.dofactory.com/Patterns/Patterns.aspx

Alex
June 2, 2010 #

GoF не догма

Leave a comment

XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong> <pre>