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

Фасад (Facade).

Тип

Структурный шаблон проектирования (Structural).

Описание

Шаблон Фасад объединяет группу объектов в рамках одного специализированного интерфейса и переадресует вызовы его методов к этим объектам.

Шаблон используется если необходимо:

  • упростить доступ к сложной системе;
  • (или) создать различные уровни доступа к системе;
  • (или) уменьшить число зависимостей между системой и клиентом.

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

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

  • переадресация вызовов интерфейса шаблона объектам системы;
  • уменьшение числа параметров метода подстановкой заранее определенных значений;
  • создание новых методов, которые объединяют вызовы объектов системы и/или добавляют свою логику;
  • часть исходных методов и свойств будут недоступны через Фасад, т.к. не играют роли для решения поставленной задачи.

Такой подход упрощает использование сложной системы. Одновременно это понижает количество вызываемых методов и общее число их вызовов. В итоге – уменьшение числа зависимостей в приложении.

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

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

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

Схожие шаблоны и их отличия

Фасад Объединяет группу объектов под одним специализированным интерфейсом. Упрощает работу с группой объектов, вносит новый уровень абстракции. Содержит или ссылается на объекты, необходимые для реализации специализированного интерфейса.
Адаптер Изменяет интерфейс объекта не изменяя его функциональности. Может адаптировать несколько объектов к одному интерфейсу. Позволяет повторно использовать уже существующий код. Содержит или наследует адаптируемый объект.
Мост Разделяет объект на абстракцию и реализацию. Используется для иерархии объектов. Позволяет отдельно изменять (наследовать) абстракцию и реализацию, повышая гибкость системы. Содержит объект(реализацию), который предоставляет методы для заданной абстракций и ее уточнений (наследников).
Декоратор Расширяет возможности объекта, изменяет его поведение. Поддерживает интерфейс декорируемого объекта, но может добавлять новые методы и свойства. Дает возможность динамически менять функциональность объекта. Является альтернативой наследованию (в том числе множественному). Содержит декорируемый объект. Возможна цепочка объектов, вызываемых последовательно.
Прокси Прозрачно замещает объект и управляет доступом к нему. Не изменяет интерфейс или поведение. Упрощает и оптимизирует работу с объектом. Может добавлять свою функциональность, скрывая ее от клиента. Содержит объект или ссылку на него, может управлять существованием замещенного объекта.
Компоновщик Предоставляет единый интерфейс для взаимодействия с составными объектами и их частями. Упрощает работу клиента, позволяет легко добавлять новые варианты составных объектов и их частей. Включается в виде интерфейса в составные объекты и их части.
Приспособленец

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

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

Отдельно остановимся на двух очень схожих шаблонах – Фасад и Адаптер , использующий несколько адаптируемых объектов. Может появиться вопрос: в чем отличие? Обратите внимание на их цели. Если объединение объектов используется для реализации существующего интерфейса и для повторного использования кода, то это Адаптер . Выделение же сути задачи, упрощение операции, появление нового интерфейса – все это признаки Фасада.

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

Реализация шаблона в общем виде

Возможны следующие подходы к реализации шаблона:

  1. Создание отдельного класса, который ссылается на экземпляр системы. Это классический вариант и реализация отчетливо выделена, т.к. является отдельным объектом. public class FacadeImpl: IFacade { private AppSubSystem _system; public FacadeImpl(AppSubSubSystem s) { this._system = s; } /* Skipped */ } Кроме того, экземпляр системы может создаваться внутри Фасада: public class FacadeImpl: IFacade { private readonly AppSubSystem _system = new AppSubSystem(); /* Skipped */ }
  2. Добавление интерфейса Фасада в систему. Может пригодиться, если у Фасада много клиентов и нет смысла создавать каждому по собственному экземпляру реализации шаблона. public class AppSubSystem { public IFacade FacadeInterface { get; private set; } public AppSubSystem () { this.FacadeInterface = new FacadeImpl(this); } /* Skipped */ }
  3. Поддержка интерфейса Фасада в классе самой системы, используя наследование. Реализация получается "размытой" по системе. Однако, теперь ее экземпляр может использоваться там, где требуется интерфейс Фасада. Кроме того, появляется возможность доступа к закрытым полям и методам системы, а так же нет необходимости переадресации вызовов одинаковых методов. public class AppSubSystem: IFacade { /* Skipped */ }

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

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

Пример реализации

Рассмотрим пример создания простого редактора сообщений блога в рамках системы управления сайтом. Перечислим какие объекты будут входить в состав этой подсистемы. Авторизацию и прочие несущественные для примера методы уберем для краткости изложения.

Начнем с основного – записи в блог, представленной классом BlogPost. Этот класс хранит свойства и содержимое одной записи.

Public class BlogPost { ///

Gets or sets the post properties. public BlogPostProperties Properties { get; set; } /// Gets or sets the post content. public BlogPostContent Content { get; set; } }

Следующий класс BlogPostStorage – хранилище для записей блога на сервере. Он позволяет задавать данные для авторизации, загружать и публиковать записи.

Public class BlogPostStorage { ///

public string Server { get; set; } /// Gets or sets the blogger"s login. public string Login { get; set; } /// Gets or sets the blogger"s password. public string Password { private get; set; } /// /// The start date. /// The end date. /// public List GetPostList(DateTime startDate, DateTime endDate) { /* Skipped */ } /// Gets the post associated with the specified id. /// The id of the post to get. /// The instance for the id. public BlogPost GetPost(int postId) { /* Skipped */ } /// Deletes the post associated with the specified id. /// The id of the post to delete. public void DeletePost(int postId) { /* Skipped */ } /// Publishes the specified post. /// The post to publish. public void Publish(BlogPost post) { /* Skipped */ } }

Для проверки орфографии есть простой класс SimpleSpellChecker .

Public class SimplepellChecker { ///

Checks the word for spelling errors. /// The word to check. /// When this method returns, contains the suggestions /// for the specified word. /// true on correct word; otherwise, false public bool Check(string word, out List suggestions) { /* Skipped */ } }

И последний из компонентов – текстовый редактор. Уберем практически все его методы для краткости.

Public class PostEditor { ///

Spell checker. private readonly SimpleSpellChecker _spellChecker = new SimpleSpellChecker(); /// public bool IsSpellCheckComplete { get; private set; } /// Gets or sets the post to edit. public BlogPost Post { get; set; } /// Gets the post properties. public BlogPostProperties PostProperties { get { if (this.Post == null) { return null; } return this.Post.Properties; } } /// Gets the post content. public BlogPostContent PostContent { get { if (this.Post == null) { return null; } return this.Post.Content; } } }

Перейдем к редактору записей блога. Его подсистема будет выглядеть следующим образом:

Public class BlogEditor { private readonly BlogPostStorage _storage = new BlogPostStorage(); public BlogPostStorage Storage { get { return this._storage; } } private readonly PostEditor _editor = new PostEditor(); public PostEditor Editor { get { return this._editor; } } }

Клиент может непосредственно использовать эти объекты. Но нужно ли ему это? Если посмотреть внимательно, то выяснится что необходимы следующие функции:

  • указать реквизиты для входа на сервер: Server , Login , Password ;
  • загрузить список записей: GetPostList() ;
  • загрузить запись для редактирования: LoadPost() ;
  • получить свойства записи и ее текст: PostProperties , PostContent ;
  • убедиться что орфография записи проверена: IsSpellCheckComplete ;
  • опубликовать запись: Publish() ;
  • удалить запись: DeletePost() .

Все это можно записать в виде интерфейса для Фасада:

Public interface IBlogEditorFacade { ///

Gets or sets the server address. string Server { get; set; } /// Sets the blogger"s login. string Login { set; } /// Sets the blogger"s password. string Password { set; } /// Gets a value indicating whether spell check is complete. bool IsSpellCheckComplete { get; } /// Gets the current post properties. BlogPostProperties PostProperties { get; } /// Gets the current post content. BlogPostContent PostContent { get; } /// Returns the list that represents the collection of post on server /// for a specified period of time. /// The start date. /// The end date. /// The list that represents the collection of post on server. List GetPostList(DateTime startDate, DateTime endDate); /// Loads the specified post. /// The id of the post to get. void LoadPost(int postId); /// Publishes the current post to blog. void Publish(); /// Deletes the specified post. /// The id of the post to delete. void DeletePost(int postId); }

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

Public class BlogEditorFacade: IBlogEditorFacade { private BlogEditor _blogEditor; ///

Initializes a new instance of the /// class. /// Reference to a blog editor instance. public BlogEditorFacade(BlogEditor blogEditor) { if (blogEditor == null) { throw new ArgumentNullException("blogEditor can"t be null"); } this._blogEditor = blogEditor; } public string Server { get { return this._blogEditor.Storage.Server; } set { this._blogEditor.Storage.Server = value; } } public void LoadPost(int postId) { BlogPost post = this._blogEditor.Storage.GetPost(postId); if (post != null) { this._blogEditor.Editor.Post = post; } } /* IBlogEditorFacade implementation skipped */ }

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

Public class BlogClient { private readonly IBlogEditorFacade _blogEditor = null; public BlogClient(IBlogEditorFacade iBlogEditor) { this._blogEditor = iBlogEditor; } public bool ValidateAndPublish() { bool isPostValid = true; BlogPostContent content = this._blogEditor.PostContent; /// TODO: Validate the content if (isPostValid) { this._blogEditor.Publish(); } return isPostValid; } }

Использование BlogClient выглядит следующим образом:

BlogEditor blogEditorObject = new BlogEditor(); /// ... BlogClient blogClient = new BlogClient(new BlogEditorFacade(blogEditorObject));

Обратите внимание, что в конструктор Фасада передается интерфейс. Кроме того, создание объекта системы (BlogEditor ) вынесено из реализации шаблона.

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

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

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

  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); } }

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

Простейшая схема работы паттерна:

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

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

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

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

Создадим классы для работы привода микроволновки (вращения), мощности и оповещения.

В классе привода, будет всего 2 действия: поворот направо и поворот налево.

Class Drive { public void TurlLeft() { Console.WriteLine("Повернуть влево"); } public void TurlRight() { Console.WriteLine("Повернуть вправо"); } }

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

Class Power { private int _power; public int MicrowavePower { get { return _power; } set { _power = value; Console.WriteLine("Задана мощность {0}w ", _power); } } }

В классе оповещения добавлены методы для оповещения о начале и завершении работы.

Class Notification { public void StopNotification() { Console.WriteLine("Пик-пик-пик - операция завершена"); } public void StartNotification() { Console.WriteLine("Пик - начат процесс готовки"); } }

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

Class Microwave { private Drive _drive; private Power _power; private Notification _notification; public Microwave(Drive drive, Power power, Notification notification) { _drive = drive; _power = power; _notification = notification; } public void Defrost() { _notification.StartNotification(); _power.MicrowavePower = 1000; _drive.TurlRight(); _drive.TurlRight(); _power.MicrowavePower = 500; _drive.TurlLeft(); _drive.TurlLeft(); _power.MicrowavePower = 200; _drive.TurlRight(); _drive.TurlRight(); _power.MicrowavePower = 0; _notification.StopNotification(); } public void Heating() { _notification.StartNotification(); _power.MicrowavePower = 350; _drive.TurlRight(); _drive.TurlRight(); _drive.TurlRight(); _drive.TurlRight(); _drive.TurlRight(); _drive.TurlLeft(); _drive.TurlLeft(); _drive.TurlRight(); _drive.TurlRight(); _drive.TurlLeft(); _drive.TurlLeft(); _drive.TurlLeft(); _drive.TurlLeft(); _power.MicrowavePower = 0; _notification.StopNotification(); } }

Вот и всё, пример готов, осталось протестировать.

Var drive = new Drive(); var power = new Power(); var notification = new Notification(); var microwave = new Microwave.Microwave(drive, power, notification); Console.WriteLine("Разморозим"); microwave.Defrost(); Console.WriteLine(); Console.WriteLine("Подогреем"); microwave.Heating();

Результат будет следующий:


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

Проблема

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

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

Решение

Фасад - это простой интерфейс для работы со сложной подсистемой, содержащей множество классов. Фасад может иметь урезанный интерфейс, не имеющий 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()

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

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

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

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

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

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

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

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

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

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

11 марта 2010 в 10:30

Паттерн проектирования «Фасад» / «Facade»

  • Совершенный код

Описание других паттернов.

Проблема

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

Описание

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

Таким образом, минимизация зависимости подсистем, а также снижение объема передаваемой между ними информации - одна из основных задач проектирования.

Один из способов решения данной задачи - использование паттерна «Фасад».

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

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

Практическая задача

Используя паттерн «Фасад», реализуем унифицированный интерфейс к некоторой подсистеме авторизации пользователей. Сама подсистема авторизации (в данном примере), безусловно не претендует на «сложную систему», однако она отчетливо отображает основные достоинства паттерна.

Диаграмма классов

Рассмотрим диаграмму. Каркас подсистемы авторизации, для наглядности, выделен в прямоугольник. Фасад Authorizator предоставляет клиенту унифицированный интерфейс для работы с подсистемой. В данном случае, это всего один метод - authorizate(), однако их могло быть и больше. При этом, клиент может использовать фасад для работы с подсистемой, а может, непосредственно пользоваться классами, составляющими ее. Сам процесс авторизации достаточно прост. На основании имени пользователя ищется соответствующая запись в базе данных, посредством интерфейса DB. Затем, сравнивается пароль найденной записи с паролем указанным пользователем.

Реализация на С#

В коде реализации нет класса PgSQLDB.
using System;
using System.Collections.Generic ;
using System.Linq;
using System.Text;
using System.Security;

namespace Facade
{
//Абстрактный класс пользователя
abstract class User
{
protected string username;
protected string passwd;

public abstract string getUserRole();

public string getPasswdHash()
{
// Это строка не несет какой-либой смысловой нагрузки.
// Безусловно, таким образом мы получаем небезопасный хеш-код пароля
return passwd.GetHashCode().ToString();
}
}

// Уточнение пользователя, в качестве пользователя по-умолчанию
class DefaultUser: User
{
public DefaultUser(string username, string passwd)
{
this .username = username;
this .passwd = passwd;
}


{
return "DEFAULT_USER" ;
}
}

// Уточнение пользователя, в качестве администратора
class Administrator: User
{
public Administrator(string username, string passwd)
{
this .username = username;
this .passwd = passwd;
}

public override string getUserRole()
{
return "ADMINISTRATOR" ;
}

// Интерфейс доступа к базе данных
interface DB
{
User search(string username);
}

// Реализация интерфейса БД для SQLite
class SQLiteDB: DB
{
public SQLiteDB(string filename)
{
// Инициализация драйвера БД
}

public User search(string username)
{
// Заглушка
throw new NotImplementedException();
}
}

// Авторизация пользователя
public void authorizate(string username, string passwd)
{
DB db = new SQLiteDB("db.sqlite" );
User user = db.search(username);
if (user.getPasswdHash() == passwd)
{
// все хорошо, пользователь опознан
}
else
{
// что-то пошло не так
throw new SecurityException("Wrong password or username!" );
}
}
}

class Program
{
static void Main(string args)
{
// Вымышленный пользователь
string username = "Vasya" ;
string passwd = "qwerty" .GetHashCode().ToString();

Authorizator auth = new Authorizator();
try
{
auth.authorizate(username, passwd);
}
catch (SecurityException ex)
{
// Пользователь не прошел аутентификацию
}
}
}
}


* This source code was highlighted with Source Code Highlighter .

PS : У меня у одного хабраредактор не работает?