Фасад (шаблон проектирования)
Фасад | |
---|---|
Facade | |
Тип | структурный |
Описан в Design Patterns | Да |
Шаблон фасад (англ. Facade) — структурный шаблон проектирования, позволяющий скрыть сложность системы путём сведения всех возможных внешних вызовов к одному объекту, делегирующему их соответствующим объектам системы.
Описание
[править | править код]Проблема
[править | править код]Как обеспечить унифицированный интерфейс с набором разрозненных реализаций или интерфейсов, например, с подсистемой, если нежелательно сильное связывание с этой подсистемой или реализация подсистемы может измениться?
Решение
[править | править код]Определить одну точку взаимодействия с подсистемой — фасадный объект, обеспечивающий общий интерфейс с подсистемой, и возложить на него обязанность по взаимодействию с её компонентами. Фасад — это внешний объект, обеспечивающий единственную точку входа для служб подсистемы. Реализация других компонентов подсистемы закрыта и не видна внешним компонентам. Фасадный объект обеспечивает реализацию GRASP паттерна Устойчивый к изменениям (Protected Variations) с точки зрения защиты от изменений в реализации подсистемы.
Особенности применения
[править | править код]Шаблон применяется для установки некоторого рода политики по отношению к другой группе объектов. Если политика должна быть яркой и заметной, следует воспользоваться услугами шаблона Фасад. Если же необходимо обеспечить скрытность и аккуратность (прозрачность), более подходящим выбором является шаблон Заместитель (Proxy).
Примеры
[править | править код]C++
[править | править код]#include <iostream> #include <string> #include <memory> #include <string_view> /** Абстрактный музыкант - не является обязательной составляющей паттерна, введен для упрощения кода */ class Musician { const char* name; public: Musician(std::string_view name) { this->name = name.data(); } virtual ~Musician() =default; protected: void output(std::string_view text) { std::cout << this->name << " " << text << "." << std::endl; } }; /** Конкретные музыканты */ class Vocalist: public Musician { public: Vocalist(std::string_view name): Musician(name) {} void singCouplet(int coupletNumber) { std::string text = "спел куплет №"; text += std::to_string(coupletNumber); output(text); } void singChorus() { output("спел припев"); } }; class Guitarist: public Musician { public: Guitarist(std::string_view name): Musician(name) {} void playCoolOpening() { output("начинает с крутого вступления"); } void playCoolRiffs() { output("играет крутые риффы"); } void playAnotherCoolRiffs() { output("играет другие крутые риффы"); } void playIncrediblyCoolSolo() { output("выдает невероятно крутое соло"); } void playFinalAccord() { output("заканчивает песню мощным аккордом"); } }; class Bassist: public Musician { public: Bassist(std::string_view name): Musician(name) {} void followTheDrums() { output("следует за барабанами"); } void changeRhythm(std::string_view type) { std::string text = ("перешел на ритм "); text += type; text += "a"; output(text); } void stopPlaying() { output("заканчивает играть"); } }; class Drummer: public Musician { public: Drummer(std::string_view name): Musician(name) {} void startPlaying() { output("начинает играть"); } void stopPlaying() { output("заканчивает играть"); } }; /** Фасад, в данном случае - знаменитая рок-группа */ class BlackSabbath { std::unique_ptr<Vocalist> vocalist; std::unique_ptr<Guitarist> guitarist; std::unique_ptr<Bassist> bassist; std::unique_ptr<Drummer> drummer; public: BlackSabbath() { vocalist = std::make_unique<Vocalist>("Оззи Осборн"); guitarist = std::make_unique<Guitarist>("Тони Айомми"); bassist = std::make_unique<Bassist>("Гизер Батлер"); drummer = std::make_unique<Drummer>("Билл Уорд"); } void playCoolSong() { guitarist->playCoolOpening(); drummer->startPlaying(); bassist->followTheDrums(); guitarist->playCoolRiffs(); vocalist->singCouplet(1); bassist->changeRhythm("припев"); guitarist->playAnotherCoolRiffs(); vocalist->singChorus(); bassist->changeRhythm("куплет"); guitarist->playCoolRiffs(); vocalist->singCouplet(2); bassist->changeRhythm("припев"); guitarist->playAnotherCoolRiffs(); vocalist->singChorus(); bassist->changeRhythm("куплет"); guitarist->playIncrediblyCoolSolo(); guitarist->playCoolRiffs(); vocalist->singCouplet(3); bassist->changeRhythm("припев"); guitarist->playAnotherCoolRiffs(); vocalist->singChorus(); bassist->changeRhythm("куплет"); guitarist->playCoolRiffs(); bassist->stopPlaying(); drummer->stopPlaying(); guitarist->playFinalAccord(); } }; int main() { std::cout << "OUTPUT:" << std::endl; BlackSabbath band; band.playCoolSong(); return 0; } /** * OUTPUT: * Тони Айомми начинает с крутого вступления. * Билл Уорд начинает играть. * Гизер Батлер следует за барабанами. * Тони Айомми играет крутые риффы. * Оззи Осборн спел куплет №1. * Гизер Батлер перешел на ритм припевa. * Тони Айомми играет другие крутые риффы. * Оззи Осборн спел припев. * Гизер Батлер перешел на ритм куплетa. * Тони Айомми играет крутые риффы. * Оззи Осборн спел куплет №2. * Гизер Батлер перешел на ритм припевa. * Тони Айомми играет другие крутые риффы. * Оззи Осборн спел припев. * Гизер Батлер перешел на ритм куплетa. * Тони Айомми выдает невероятно крутое соло. * Тони Айомми играет крутые риффы. * Оззи Осборн спел куплет №3. * Гизер Батлер перешел на ритм припевa. * Тони Айомми играет другие крутые риффы. * Оззи Осборн спел припев. * Гизер Батлер перешел на ритм куплетa. * Тони Айомми играет крутые риффы. * Гизер Батлер заканчивает играть. * Билл Уорд заканчивает играть. * Тони Айомми заканчивает песню мощным аккордом. */
JavaScript
[править | править код]/* Complex parts */ function SubSystem1() { this.method1 = function() { console.log("вызван SubSystem1.method1"); }; } function SubSystem2() { this.method2 = function() { console.log("вызван SubSystem2.method2"); }; this.methodB = function() { console.log("вызван SubSystem2.methodB"); }; } /* Facade */ function Facade() { var s1 = new SubSystem1(), s2 = new SubSystem2(); this.m1 = function() { console.log("вызван Facade.m1"); s1.method1(); s2.method2(); }; this.m2 = function() { console.log("вызван Facade.m2"); s2.methodB(); }; } /* Client */ function test() { var facade = new Facade(); facade.m1(); facade.m2(); } test(); /* Выведет: "вызван Facade.m1" "вызван SubSystem1.method1" "вызван SubSystem2.method2" "вызван Facade.m2" "вызван SubSystem2.methodB" */
CoffeeScript
[править | править код]# Загрузчик изображений class ImageLoader loadImage = (src) -> # ... constructor : (hash = {}) -> @images = {} @images[name] = loadImage(src) for name, src of hash # Загрузчик аудио class SoundLoader loadSound = (src) -> # ... constructor : (hash = {}) -> @sounds = {} @sounds[name] = loadSound(src) for name, src of hash # Фасад class Loader constructor : ({images, sounds}) -> @images = new ImageLoader(images).images @sounds = new SoundLoader(sounds).sounds sound : (name) -> @sounds[name] image : (name) -> @images[name]
PHP
[править | править код]/** * Реализации отдельных частей компьютера. * У каждого метода классов имеется какая-то реализация, в данном примере она опущена. */ /** * Class CPU, отвечает за работу процессора */ class CPU { public function freeze() {} public function jump($position) {} public function execute() {} } /** * Class Memory, отвечает за работу памяти */ class Memory { const BOOT_ADDRESS = 0x0005; public function load($position, $data) {} } /** * Class HardDrive, отвечает за работу жёсткого диска */ class HardDrive { const BOOT_SECTOR = 0x001; const SECTOR_SIZE = 64; public function read($lba, $size) {} } /** * Пример шаблона "Фасад" * В качестве унифицированного объекта выступает Компьютер. * За этим объектом будут скрыты все детали работы его внутренних частей. */ class Computer { protected $cpu; protected $memory; protected $hardDrive; /** * Computer constructor. * Инициализируем части */ public function __construct() { $this->cpu = new CPU(); $this->memory = new Memory(); $this->hardDrive = new HardDrive(); } /** * Упрощённая обработка поведения "запуск компьютера" */ public function startComputer() { $cpu = $this->cpu; $memory = $this->memory; $hardDrive = $this->hardDrive; $cpu->freeze(); $memory->load( $memory::BOOT_ADDRESS, $hardDrive->read($hardDrive::BOOT_SECTOR, $hardDrive::SECTOR_SIZE) ); $cpu->jump($memory::BOOT_ADDRESS); $cpu->execute(); } } /** * Пользователям компьютера предоставляется Фасад (компьютер), * который скрывает все сложность работы с отдельными компонентами. */ $computer = new Computer(); $computer->startComputer();
Python
[править | править код]# Сложные части системы class CPU(object): def __init__(self): # ... pass def freeze(self): # ... pass def jump(self, address): # ... pass def execute(self): # ... pass class Memory(object): def __init__(self): # ... pass def load(self, position, data): # ... pass class HardDrive(object): def __init__(self): # ... pass def read(self, lba, size): # ... pass # Фасад class Computer(object): def __init__(self): self._cpu = CPU() self._memory = Memory() self._hardDrive = HardDrive() def startComputer(self): self._cpu.freeze() self._memory.load(BOOT_ADDRESS, self._hardDrive.read(BOOT_SECTOR, SECTOR_SIZE)) self._cpu.jump(BOOT_ADDRESS) self._cpu.execute() # Клиентская часть if __name__ == "__main__": facade = Computer() facade.startComputer()
C#
[править | править код]using System; namespace Library { /// <summary> /// Класс подсистемы /// </summary> /// <remarks> /// <li> /// <lu>реализует функциональность подсистемы;</lu> /// <lu>выполняет работу, порученную объектом <see cref="Facade"/>;</lu> /// <lu>ничего не "знает" о существовании фасада, то есть не хранит ссылок на него;</lu> /// </li> /// </remarks> internal class SubsystemA { internal string A1() { return "Subsystem A, Method A1\n"; } internal string A2() { return "Subsystem A, Method A2\n"; } } internal class SubsystemB { internal string B1() { return "Subsystem B, Method B1\n"; } } internal class SubsystemC { internal string C1() { return "Subsystem C, Method C1\n"; } } } /// <summary> /// Facade - фасад /// </summary> /// <remarks> /// <li> /// <lu>"знает", каким классами подсистемы адресовать запрос;</lu> /// <lu>делегирует запросы клиентов подходящим объектам внутри подсистемы;</lu> /// </li> /// </remarks> public class Facade { Library.SubsystemA a = new Library.SubsystemA(); Library.SubsystemB b = new Library.SubsystemB(); Library.SubsystemC c = new Library.SubsystemC(); public void Operation1() { Console.WriteLine("Operation 1\n" + a.A1() + a.A2() + b.B1()); } public void Operation2() { Console.WriteLine("Operation 2\n" + b.B1() + c.C1()); } } class Program { static void Main(string[] args) { Facade facade = new Facade(); facade.Operation1(); facade.Operation2(); // Wait for user Console.Read(); } }
Ruby
[править | править код]module Library # <summary> # Класс подсистемы # </summary> # <remarks> # <li> # <lu>реализует функциональность подсистемы;</lu> # <lu>выполняет работу, порученную объектом <see cref="Facade"/>;</lu> # <lu>ничего не "знает" о существовании фасада, то есть не хранит ссылок на него;</lu> # </li> # </remarks> class SubsystemA def a1; "Subsystem A, Method a1\n"; end def a2; "Subsystem A, Method a2\n"; end end class SubsystemB def b1; "Subsystem B, Method b1\n"; end end class SubsystemC def c1; "Subsystem C, Method c1\n"; end end end # <summary> # Facade - фасад # </summary> # <remarks> # <li> # <lu>"знает", каким классами подсистемы адресовать запрос;</lu> # <lu>делегирует запросы клиентам подходящим объектам внутри подсистемы;</lu> # </li> # </remarks> class Facade def initialize @a = Library::SubsystemA.new; @b = Library::SubsystemB.new; @c = Library::SubsystemC.new; end def operation1 puts "Operation 1\n" + @a.a1 + @a.a2 + @b.b1 end def operation2 puts "Operation 2\n" + @b.b1() + @c.c1() end end facade = Facade.new facade.operation1 facade.operation2 # Wait for user gets
VB.NET
[править | править код]Namespace Library 'Класс подсистемы ' . реализует функциональность подсистемы ' . выполняет работу, порученную объектом Facade ' . ничего не "знает" о существовании фасада, то есть не хранит ссылок на него Friend Class SubsystemA Friend Function A1() As String Return "Subsystem A, Method A1" & vbCrLf End Function Friend Function A2() As String Return "Subsystem A, Method A2" & vbCrLf End Function End Class Friend Class SubsystemB Friend Function B1() As String Return "Subsystem B, Method B1" & vbCrLf End Function End Class Friend Class SubsystemC Friend Function C1() As String Return "Subsystem C, Method C1" & vbCrLf End Function End Class End Namespace 'Facade - фасад ' . "знает", каким классами подсистемы адресовать запрос ' . делегирует запросы клиентов подходящим объектам внутри подсистемы Public NotInheritable Class Facade Private Sub New() End Sub Shared a As New Library.SubsystemA() Shared b As New Library.SubsystemB() Shared c As New Library.SubsystemC() Public Shared Sub Operation1() Console.WriteLine("Operation 1" & vbCrLf & a.A1() & a.A2() & b.B1()) End Sub Public Shared Sub Operation2() Console.WriteLine("Operation 2" & vbCrLf & b.B1() & c.C1()) End Sub End Class Class Program Shared Sub Main() Facade.Operation1() Facade.Operation2() 'Ожидаем действия пользователя Console.Read() End Sub End Class
Delphi
[править | править код]program FacadePattern; {$APPTYPE CONSOLE} uses SysUtils; type TComputer = class public procedure PlugIn; procedure PowerMonitor; procedure Power; end; procedure TComputer.PlugIn; begin WriteLn('Included in the network'); end; procedure TComputer.PowerMonitor; begin WriteLn('Turn on the monitor'); end; procedure TComputer.Power; begin WriteLn('Turn the system unit'); end; type TNotebook = class procedure Power; end; procedure TNotebook.Power; begin WriteLn('Press the power button'); end; type TKettle = class procedure PlugIn; procedure Power; end; procedure TKettle.Power; begin WriteLn('Press the power button'); end; procedure TKettle.PlugIn; begin WriteLn('Included in the network'); end; type TFacade = class public procedure PowerOn(aDevice: TObject); end; procedure TFacade.PowerOn(aDevice: TObject); begin if aDevice is TComputer then with TComputer(aDevice) do begin PlugIn; PowerMonitor; Power; end; if aDevice is TNotebook then with TNotebook(aDevice) do Power; if aDevice is TKettle then with TKettle(aDevice) do begin PlugIn; Power; end; WriteLn end; begin with TFacade.Create do try PowerOn(TComputer.Create); PowerOn(TNotebook.Create); PowerOn(TKettle.Create); finally Free; end; ReadLn; end.
Java
[править | править код]/* Complex parts */ class CPU { public void freeze() { System.out.println("freeze"); } public void jump(long position) { System.out.println("jump position = " + position); } public void execute() { System.out.println("execute"); } } class Memory { public void load(long position, byte[] data) { System.out.println("load position = " + position + ", data = " + data); } } class HardDrive { public byte[] read(long lba, int size) { System.out.println("read lba = " + lba + ", size = " + size); return new byte[size]; } } /* Facade */ class Computer { private final static long BOOT_ADDRESS = 1L; private final static long BOOT_SECTOR = 2L; private final static int SECTOR_SIZE = 3; private CPU cpu; private Memory memory; private HardDrive hardDrive; public Computer() { this.cpu = new CPU(); this.memory = new Memory(); this.hardDrive = new HardDrive(); } public void startComputer() { cpu.freeze(); memory.load(BOOT_ADDRESS, hardDrive.read(BOOT_SECTOR, SECTOR_SIZE)); cpu.jump(BOOT_ADDRESS); cpu.execute(); } } /* Client */ class Application { public static void main(String[] args) { Computer computer = new Computer(); computer.startComputer(); } }
Haxe
[править | править код]/** * Реализации отдельных частей компьютера. * У каждого метода классов имеется какая-то реализация, в данном примере она опущена. */ /** * Class CPU, отвечает за работу процессора */ class CPU { public function new() { } public function freeze():Void { //... } public function jump(position:Int):Void { //... } public function execute():Void { //... } } /** * Class Memory, отвечает за работу памяти */ class Memory { public static inline var BOOT_ADDRESS:Int = 0x0005; public function new() { } public function load(position:Int, data:haxe.io.Bytes):Void { //... } } /** * Class HardDrive, отвечает за работу жёсткого диска */ class HardDrive { public static inline var BOOT_SECTOR:Int = 0x001; public static inline var SECTOR_SIZE:Int = 64; public function new() { } public function read(lba:Int, size:Int):haxe.io.Bytes { //... return null; } } /** * Пример шаблона "Фасад" * В качестве унифицированного объекта выступает Компьютер. * За этим объектом будут скрыты, все детали работы его внутренних частей. */ class Computer { private var cpu:CPU; private var memory:Memory; private var hardDrive:HardDrive; /** * Computer constructor. * Инициализируем части */ public function new() { this.cpu = new CPU(); this.memory = new Memory(); this.hardDrive = new HardDrive(); } /** * Упрощённая обработка поведения "запуск компьютера" */ public function startComputer():Void { cpu.freeze(); memory.load( Memory.BOOT_ADDRESS, hardDrive.read(HardDrive.BOOT_SECTOR, HardDrive.SECTOR_SIZE) ); cpu.jump(Memory.BOOT_ADDRESS); cpu.execute(); } } /** * Пользователям компьютера предоставляется Фасад (компьютер), * который скрывает все сложность работы с отдельными компонентами. */ class Application { public static function main():Void { var computer:Computer = new Computer(); computer.startComputer(); } }
Swift
[править | править код]// Logic class CPU { public func freeze() -> String { return "Freezing processor." } public func jump(position: String) -> String { return "Jumping to: \(position)" } public func execute() -> String { return "Executing." } } class Memory { public func load(position: String, data: String) -> String { return "Loading from \(position) data: \(data)" } } class HardDrive { public func read(lba: String, size: String) -> String { return "Some data from sector \(lba) with size \(size)" } } //Facade class ComputerFacade { private let cpu = CPU() private let memory = Memory() private let hardDrive = HardDrive() public func start() { cpu.freeze() let ssd = hardDrive.read(lba: "100", size: "1024") memory.load(position: "0x00", data: ssd) cpu.jump(position: "0x00") cpu.execute() } } // Client let pc = ComputerFacade() pc.start()
Литература
[править | править код]- Э. Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссидес. Приемы объектно-ориентированного проектирования. Паттерны проектирования = Design Patterns: Elements of Reusable Object-Oriented Software. — СПб.: «Питер», 2007. — С. 366. — ISBN 978-5-469-01136-1. (также ISBN 5-272-00355-1)
Источники и ссылки
[править | править код]- Паттерн Facade (фасад) — назначение, описание, особенности и реализация на C++.
- Паттерны для масштабируемых JavaScript-приложений. Глава 9. Паттерн «Фасад» — описание, пример реализации.