Паттерны проектирования: Фасад (Facade). Что такое паттерн фасад Java Аналогия из жизни

Книга GoF описывает этот паттерн как предоставляющий унифицированный интерфейс к множеству интерфейсов в некоторой подсистеме. Книга "Паттерны проектирования " дает это же толкование и обращает внимание, что, скрывая слож­ность подсистемы, паттерн "Фасад " в то же время предоставляет все возможности подсистемы через удобный для использования интерфейс.

Для простого практического примера того, как работает паттерн "Фасад", пред­ставьте стиральную машину со всего лишь двумя режимами стирки: для сильно загрязненного белья и для слабо загрязненного.

Для каждого режима стиральная машина должна выполнить предопределенный набор операций: установить темпе­ратуру воды, нагреть воду, установить длительность цикла стирки, добавить сти­ральный порошок, добавить отбеливающее средство, добавить смягчитель ткани и т.д. Каждый режим требует различного набора инструкций по стирке (разное количество стирального порошка, более высокая/низкая температура, более долгий/короткий цикл отжима и т.д.).

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

Пользователь стиральной машины не должен думать о сложной логике стирки вещей (выбирать температуру, длитель­ность цикла и т.д.). Единственное, что должен сделать пользователь - решить, сильно загрязнено белье или нет. В этом состоит сущность паттерна "Фасад" при­менительно к конструкции стиральных машин.

Паттерн "Фасад" обычно реализуется в следующих целях и случаях:

  • для обеспечения простого и унифицированного доступа к унаследованной системе управления производством;
  • для создания общедоступного API к таким классам, как драйвер;
  • для предоставления крупно-модульного доступа к доступным сервисам. Серви­сы сгруппированы как в вышеприведенном примере со стиральной машиной;
  • чтобы снизить количество сетевых вызовов. Фасад выполняет множество об­ращений к подсистеме, в то время как удаленный клиент должен выполнить одно-единственное обращение к фасаду;
  • для инкапсуляции последовательности выполняемых действий и внутренних деталей приложения, чтобы обеспечить простоту и безопасность.

Кстати, фасады также иногда реализуют как абстрактные фабрики-одиночки.

Диаграмма классов фасада . Как можно увидеть на диаграмме классов на рис.3.1 , паттерн "Фасад" предоставляет простой интерфейс для базовой системы, инкап­сулируя сложную логику.

Рис 3.1 . Диаграмма классов паттерна "Фасад".

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

Как правило, фасад используется для создания некоторой абстракции, скрывающей за собой совершенно иную реальность. Паттерн «фасад» обеспечивает удобный высокоуровневый интерфейс для больших блоков кода, скрывая за собой их истинную сложность. Относитесь к фасаду, как к упрощенному API, который вы отдаете в пользование другим разработчикам.

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

Благодаря такому подходу, пользователь взаимодействует только с интерфейсом, не имея никакого представления о подсистемах, которые скрываются за ним.

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

Надежный фасад - наш упрощенный интерфейс - позволит нам не беспокоиться о тесных связях некоторых модулей нашей системы с dojo, jQuery, YUI, zepto или какой-либо другой библиотекой. Это становится не так важно. Вы можете переходить с одной библиотеки на другую не меняя слой взаимодействия. К примеру, с jQuery на dojo. Более того, у вас появляется возможность совершить такой переход на более поздних этапах, без изменений в остальных частях системы.

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

var module = (function () { var _private = { i : 5 , get : function () { console . log ("Текущее значение:" + this . i ); }, set : function (val ) { this . i = val ; }, run : function () { console . log ("процесс запущен" ); }, jump : function () { console . log ("резкое изменение" ); } }; return { facade : function (args ) { _private . set (args . val ); _private . get (); if (args . run ) { _private . run (); } } } }()); module . facade ({ run : true , val : 10 }); // Текущее значение: 10, процесс запущен

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

Фасад - это структурный паттерн проектирования, который предоставляет простой интерфейс к сложной системе классов, библиотеке или фреймворку.

Проблема

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

В результате бизнес-логика ваших классов тесно переплетается с деталями реализации сторонних классов. Такой код довольно сложно понимать и поддерживать.

Решение

Фасад - это простой интерфейс для работы со сложной подсистемой, содержащей множество классов. Фасад может иметь урезанный интерфейс, не имеющий 100% функциональности, которой можно достичь, используя сложную подсистему напрямую. Но он предоставляет именно те фичи, которые нужны клиенту, и скрывает все остальные.

Фасад полезен, если вы используете какую-то сложную библиотеку со множеством подвижных частей, но вам нужна только часть её возможностей.

К примеру, программа, заливающая видео котиков в социальные сети, может использовать профессиональную библиотеку сжатия видео. Но все, что нужно клиентскому коду этой программы - простой метод encode(filename, format) . Создав класс с таким методом, вы реализуете свой первый фасад.

Аналогия из жизни


Когда вы звоните в магазин и делаете заказ по телефону, сотрудник службы поддержки является вашим фасадом ко всем службам и отделам магазина. Он предоставляет вам упрощённый интерфейс к системе создания заказа, платёжной системе и отделу доставки.

Структура



    Фасад предоставляет быстрый доступ к определённой функциональности подсистемы. Он «знает», каким классам нужно переадресовать запрос, и какие данные для этого нужны.

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

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

    Классы подсистемы не знают о существовании фасада и работают друг с другом напрямую.

    Клиент использует фасад вместо прямой работы с объектами сложной подсистемы.

Псевдокод

В этом примере Фасад упрощает работу со сложным фреймворком видеоконвертации.


Пример изоляции множества зависимостей в одном фасаде.

Вместо непосредственной работы с дюжиной классов, фасад предоставляет коду приложения единственный метод для конвертации видео, который сам заботится о том, чтобы правильно сконфигурировать нужные объекты фреймворка и получить требуемый результат.

// Классы сложного стороннего фреймворка конвертации видео. Мы // не контролируем этот код, поэтому не можем его упростить. class VideoFile // ... class OggCompressionCodec // ... class MPEG4CompressionCodec // ... class CodecFactory // ... class BitrateReader // ... class AudioMixer // ... // Вместо этого мы создаём Фасад - простой интерфейс для работы // со сложным фреймворком. Фасад не имеет всей функциональности // фреймворка, но зато скрывает его сложность от клиентов. class VideoConverter is method convert(filename, format):File is file = new VideoFile(filename) sourceCodec = new CodecFactory.extract(file) if (format == "mp4") destinationCodec = new MPEG4CompressionCodec() else destinationCodec = new OggCompressionCodec() buffer = BitrateReader.read(filename, sourceCodec) result = BitrateReader.convert(buffer, destinationCodec) result = (new AudioMixer()).fix(result) return new File(result) // Приложение не зависит от сложного фреймворка конвертации // видео. Кстати, если вы вдруг решите сменить фреймворк, вам // нужно будет переписать только класс фасада. class Application is method main() is convertor = new VideoConverter() mp4 = convertor.convert("funny-cats-video.ogg", "mp4") mp4.save()

Применимость

Когда вам нужно представить простой или урезанный интерфейс к сложной подсистеме.

Часто подсистемы усложняются по мере развития программы. Применение большинства паттернов приводит к появлению меньших классов, но в бóльшем количестве. Такую подсистему проще повторно использовать, настраивая её каждый раз под конкретные нужды, но вместе с тем, применять подсистему без настройки становится труднее. Фасад предлагает определённый вид системы по умолчанию, устраивающий большинство клиентов.

Когда вы хотите разложить подсистему на отдельные слои.

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

Например, возьмём ту же сложную систему видеоконвертации. Вы хотите разбить её на слои работы с аудио и видео. Для каждой из этих частей можно попытаться создать фасад и заставить классы аудио и видео обработки общаться друг с другом через эти фасады, а не напрямую.

Шаги реализации

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

    Создайте класс фасада, реализующий этот интерфейс. Он должен переадресовывать вызовы клиента нужным объектам подсистемы. Фасад должен будет позаботиться о том, чтобы правильно инициализировать объекты подсистемы.

    Вы получите максимум пользы, если клиент будет работать только с фасадом. В этом случае изменения в подсистеме будут затрагивать только код фасада, а клиентский код останется рабочим.

Последнее обновление: 31.10.2015

Фасад (Facade) представляет шаблон проектирования, который позволяет скрыть сложность системы с помощью предоставления упрощенного интерфейса для взаимодействия с ней.

Когда использовать фасад?

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

    Когда надо уменьшить количество зависимостей между клиентом и сложной системой. Фасадные объекты позволяют отделить, изолировать компоненты системы от клиента и развивать и работать с ними независимо.

    Когда нужно определить подсистемы компонентов в сложной системе. Создание фасадов для компонентов каждой отдельной подсистемы позволит упростить взаимодействие между ними и повысить их независимость друг от друга.

В UML общую схему фасада можно представить следующим образом:

Формальное определение программы в C# может выглядеть так:

Class SubsystemA { public void A1() {} } class SubsystemB { public void B1() {} } class SubsystemC { public void C1() {} } public class Facade { SubsystemA subsystemA; SubsystemB subsystemB; SubsystemC subsystemC; public Facade(SubsystemA sa, SubsystemB sb, SubsystemC sc) { subsystemA = sa; subsystemB = sb; subsystemC = sc; } public void Operation1() { subsystemA.A1(); subsystemB.B1(); subsystemC.C1(); } public void Operation2() { subsystemB.B1(); subsystemC.C1(); } } class Client { public void Main() { Facade facade = new Facade(new SubsystemA(), new SubsystemB(), new SubsystemC()); facade.Operation1(); facade.Operation2(); } }

Участники

    Классы SubsystemA , SubsystemB , SubsystemC и т.д. являются компонентами сложной подсистемы, с которыми должен взаимодействовать клиент

    Client взаимодействует с компонентами подсистемы

    Facade - непосредственно фасад, который предоставляет интерфейс клиенту для работы с компонентами

Рассмотрим применение паттерна в реальной задаче. Думаю, большинство программистов согласятся со мной, что писать в Visual Studio код одно удовольствие по сравнению с тем, как писался код ранее до появления интегрированных сред разработки. Мы просто пишем код, нажимаем на кнопку и все - приложение готово. В данном случае интегрированная среда разработки представляет собой фасад, который скрывает всю сложность процесса компиляции и запуска приложения. Теперь опишем этот фасад в программе на C#:

Class Program { static void Main(string args) { TextEditor textEditor = new TextEditor(); Compiller compiller = new Compiller(); CLR clr = new CLR(); VisualStudioFacade ide = new VisualStudioFacade(textEditor, compiller, clr); Programmer programmer = new Programmer(); programmer.CreateApplication(ide); Console.Read(); } } // текстовый редактор class TextEditor { public void CreateCode() { Console.WriteLine("Написание кода"); } public void Save() { Console.WriteLine("Сохранение кода"); } } class Compiller { public void Compile() { Console.WriteLine("Компиляция приложения"); } } class CLR { public void Execute() { Console.WriteLine("Выполнение приложения"); } public void Finish() { Console.WriteLine("Завершение работы приложения"); } } class VisualStudioFacade { TextEditor textEditor; Compiller compiller; CLR clr; public VisualStudioFacade(TextEditor te, Compiller compil, CLR clr) { this.textEditor = te; this.compiller = compil; this.clr = clr; } public void Start() { textEditor.CreateCode(); textEditor.Save(); compiller.Compile(); clr.Execute(); } public void Stop() { clr.Finish(); } } class Programmer { public void CreateApplication(VisualStudioFacade facade) { facade.Start(); facade.Stop(); } }

В данном случае компонентами системы являются класс текстового редактора TextEditor, класс компилятора Compiller и класс общеязыковой среды выполнения CLR. Клиентом выступает класс программиста, фасадом - класс VisualStudioFacade, который через свои методы делегирует выполнение работы компонентам и их методам.

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

  1. Identify the desired unified interface for a set of subsystems
  2. Design a "wrapper" class that can encapsulate the use of the subsystems
  3. The client uses (is coupled to) the Facade
  4. The facade/wrapper "maps" to the APIs of the subsystems
// 1. Subsystem class PointCartesian { private double x, y; public PointCartesian(double x, double y) { this.x = x; this.y = y; } public void move(int x, int y) { this.x += x; this.y += y; } public String toString() { return "(" + x + "," + y + ")"; } public double getX() { return x; } public double getY() { return y; } } // 1. Subsystem class PointPolar { private double radius, angle; public PointPolar(double radius, double angle) { this.radius = radius; this.angle = angle; } public void rotate(int angle) { this.angle += angle % 360; } public String toString() { return "[" + radius + "@" + angle + "]"; } } // 1. Desired interface: move(), rotate() class Point { // 2. Design a "wrapper" class private PointCartesian pointCartesian; public Point(double x, double y) { pointCartesian = new PointCartesian(x, y); } public String toString() { return pointCartesian.toString(); } // 4. Wrapper maps public void move(int x, int y) { pointCartesian.move(x, y); } public void rotate(int angle, Point o) { double x = pointCartesian.getX() - o.pointCartesian.getX(); double y = pointCartesian.getY() - o.pointCartesian.getY(); PointPolar pointPolar = new PointPolar(Math.sqrt(x * x + y * y),Math.atan2(y, x) * 180 / Math.PI); // 4. Wrapper maps pointPolar.rotate(angle); System.out.println(" PointPolar is " + pointPolar); String str = pointPolar.toString(); int i = str.indexOf("@"); double r = Double.parseDouble(str.substring(1, i)); double a = Double.parseDouble(str.substring(i + 1, str.length() - 1)); pointCartesian = new PointCartesian(r*Math.cos(a*Math.PI/180) + o.pointCartesian.getX(), r*Math.sin(a * Math.PI / 180) + o.pointCartesian.getY()); } } class Line { private Point o, e; public Line(Point ori, Point end) { o = ori; e = end; } public void move(int x, int y) { o.move(x, y); e.move(x, y); } public void rotate(int angle) { e.rotate(angle, o); } public String toString() { return "origin is " + o + ", end is " + e; } } public class FacadeDemo { public static void main(String args) { // 3. Client uses the Facade Line lineA = new Line(new Point(2, 4), new Point(5, 7)); lineA.move(-2, -4); System.out.println("after move: " + lineA); lineA.rotate(45); System.out.println("after rotate: " + lineA); Line lineB = new Line(new Point(2, 1), new Point(2.866, 1.5)); lineB.rotate(30); System.out.println("30 degrees to 60 degrees: " + lineB); } }