December
2005
Вернемся к рассмотрению паттернов проектирования. На мой взгляд, паттерн 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): Добавлен более сложный пример реализации паттрена Фасад.
8 Comments »
RSS feed for comments on this post. TrackBack URI
Я удивляюсь как легко и просто Вам удается описывать паттерны. Огромное спасибо!
Судя по примерам, это не Facade, а Helper без упоминания о приватности конструктора.
Ещё один момент: функция и метод - это не синонимы.
А мне все равно синонимы или нет - я изучаю паттерны. Спасибо за доходчивый пример
Пример вы?е более похож на реализацию паттерна Медиатор, так как Медиатор описывает новую ф-сть. Фасад же не описывает новый функционал, он как бы интерфейс между двумя групами классов которые не должны знать ничего друг о друге.
С теоретическим описание я вполне согласен, но пример мне кажется неудачным. Это действительно похоже на Helper.
Что за паттерн такой - Helper. Откуда вы его взяли?
>Что за паттерн такой - Helper. Откуда вы его взяли?
+1!
There is no “Helper” in GoF:
http://www.dofactory.com/Patterns/Patterns.aspx
GoF не догма
По моему не совсем удачный и доходчивый пример. ![]()