У этого термина существуют и другие значения, см. Адаптер.
Адаптер
Adapter
Представление структуры шаблона Адаптер
Тип
структурный
Назначение
для организации использования функций объекта, недоступного для модификации, через специально созданный интерфейс (приводит интерфейс класса (или нескольких классов) к интерфейсу требуемого вида)
Применяется в случаях
система поддерживает требуемые данные и поведение, но имеет неподходящий интерфейс. Чаще всего шаблон Адаптер применяется если необходимо создать класс, производный от вновь определяемого или уже существующего абстрактного класса.
Плюсы
инкапсуляция реализации внешних классов (компонентов, библиотек), система становится независимой от интерфейса внешних классов;
переход на использование других внешних классов не требует переделки самой системы, достаточно реализовать один класс Adapter.
Класс Adapter приводит интерфейс класса Adaptee в соответствие с интерфейсом класса Target (который реализуется классом Adapter). Это позволяет объекту Client использовать объект Adaptee (посредством адаптера Adapter) так, словно он является экземпляром класса Target.
Таким образом Client обращается к интерфейсу Target, реализованному классом Adapter, который перенаправляет обращение к Adaptee.
Шаблон Адаптер позволяет в процессе проектирования не принимать во внимание возможные различия в интерфейсах уже существующих классов. Если есть класс, обладающий требуемыми методами и свойствами (по крайней мере, концептуально), то при необходимости всегда можно воспользоваться шаблоном Адаптер для приведения его интерфейса к нужному виду.
Близким Адаптеру является шаблон Фасад, не всегда можно отличить один от другого[2].
Типичным примером использования шаблона Адаптер можно назвать создание классов, приводящих к единому интерфейсу функции языка PHP обеспечивающие доступ к различным СУБД[3].
Вариант решения данной проблемы с использованием шаблона Адаптер показан на рисунке.
Включение уже существующего класса в другой класс. Интерфейс включающего класса приводится в соответствие с новыми требованиями, а вызовы его методов преобразуются в вызовы методов включённого класса.
Убедитесь, что у вас есть два класса с несовместимыми интерфейсами:
полезный сервис — служебный класс, который вы не можете изменять (он либо сторонний, либо от него зависит другой код);
один или несколько клиентов — существующих классов приложения, несовместимых с сервисом из-за неудобного или несовпадающего интерфейса.
Опишите клиентский интерфейс, через который классы приложения смогли бы использовать класс сервиса.
Создайте класс адаптера, реализовав этот интерфейс.
Поместите в адаптер поле, которое будет хранить ссылку на объект сервиса. Обычно это поле заполняют объектом, переданным в конструктор адаптера. В случае простой адаптации этот объект можно передавать через параметры методов адаптера.
Реализуйте все методы клиентского интерфейса в адаптере. Адаптер должен делегировать основную работу сервису.
Приложение должно использовать адаптер только через клиентский интерфейс. Это позволит легко изменять и добавлять адаптеры в будущем.
moduleAdapterPattern# Allows Client to use Adaptees with incompatible interfaces via Adapters with interface Target# AdapteeclassTwitterdeftwitputs'Twit was published'endend# AdapteeclassFacebookdefpostputs'Facebook post was published'endend# TargetmoduleWebServiceInterfacedefsend_messageraiseNotImplementedErrorendend# AdapterclassTwitterAdapterincludeWebServiceInterfacedefinitialize@webservice=Twitter.newenddefsend_message@webservice.twitendend# AdapterclassFacebookAdapterincludeWebServiceInterfacedefinitialize@webservice=Facebook.newenddefsend_message@webservice.postendend# ClientclassMessageattr_accessor:webservicedefsend@webservice.send_messageendenddefself.runputs'=> Adapter'message=Message.newmessage.webservice=TwitterAdapter.newmessage.sendmessage.webservice=FacebookAdapter.newmessage.sendputs''endendAdapterPattern.run
packageobjectadapter{objectBattlefield{protectedvarredTroops:Array[Troop]=Array()protectedvarblueTroops:Array[Troop]=Array()defaddTroop(troop:Troop):Unit={if(troop.side=="red"){redTroops:+=troop}elseif(troop.side=="blue"){blueTroops:+=troop}else{thrownewException(s"Invalid side ${troop.side} for troop ${troop.name}")}}defgetClosestEnemyTroop(side:String):Troop={if(side=="red"){getTroop(blueTroops)}else{getTroop(redTroops)}}privatedefgetTroop(troops:Array[Troop]):Troop={if(troops.length==0){thrownewException("No available troops")}troops(0)}}classTroop(valside:String,valname:String,valcloseWeapon:String,valdistanceWeapon:String){defmove(direction:String,distance:Int):Unit={println(s"Troop $name moves $direction on $distance yards")}defattack(enemyTroop:Troop,attackType:String):Unit={valweapon=attackTypematch{case"distance"=>distanceWeaponcase"close"=>closeWeaponcase_=>thrownewException(s"Invalid attack type $attackType for troop $name")}println(s"Troop $name attacks enemy troop ${enemyTroop.name} with their ${weapon}s")}}traitLanceKnightTroopTrait{defmoveForward(distance:Int):UnitdefattackClosest(attackType:String):Unit}classLanceKnightTroop(overridevalside:String,overridevalname:String,overridevalcloseWeapon:String,overridevaldistanceWeapon:String)extendsTroop(side,name,closeWeapon,distanceWeapon)withLanceKnightTroopTrait{overridedefmoveForward(distance:Int):Unit={move("forward",distance)}overridedefattackClosest(attackType:String):Unit={attack(Battlefield.getClosestEnemyTroop(side),attackType)}}objectAdapterTestextendsAbstractTest{overridedefrun():Unit={valtroop=newTroop("blue","Archers","sword","longbow")vallanceKnightTroop=newLanceKnightTroop("red","Lance Knights","pike","crossbow")Battlefield.addTroop(troop)Battlefield.addTroop(lanceKnightTroop)println("Output:")lanceKnightTroop.moveForward(300)lanceKnightTroop.attackClosest("close")}}}// Output:// Troop Lance Knights moves forward on 300 yards// Troop Lance Knights attacks enemy troop Archers with their pikes
<?phpclassIndependentDeveloper1{publicfunctioncalc($a,$b){return$a+$b;}}classIndependentDeveloper2{publicfunctionnameIsVeryLongAndUncomfortable($a,$b){return$a+$b;}}interfaceIAdapter{publicfunctionsum($a,$b);}classConcreteAdapter1implementsIAdapter{protected$object;publicfunction__construct(){$this->object=newIndependentDeveloper1();}publicfunctionsum($a,$b){return$this->object->calc($a,$b);}}classConcreteAdapter2implementsIAdapter{protected$object;publicfunction__construct(){$this->object=newIndependentDeveloper2();}publicfunctionsum($a,$b){return$this->object->nameIsVeryLongAndUncomfortable($a,$b);}}//в одном месте мы создаём конкретный адаптер а потом пользуемся интерфейсом$adapter1=newConcreteAdapter1();$adapter2=newConcreteAdapter2();/** * Везде в коде мы не используем классы напрямую а через интерфейс * этой функции нет разницы какой класс мы используем, так как мы опираемся на интерфейс * * @param IAdapter $adapter */functionsum(IAdapter$adapter){echo$adapter->sum(2,2);}sum($adapter1);sum($adapter2);
functionSearch(text,word){vartext=text;varword=word;this.searchWordInText=function(){returntext;};this.getWord=function(){returnword;};};functionSearchAdapter(adaptee){this.searchWordInText=function(){return'Эти слова '+adaptee.getWord()+' найдены в тексте '+adaptee.searchWordInText();};};varsearch=newSearch("текст","слова");varsearchAdapter=newSearchAdapter(search);searchAdapter.searchWordInText();
classGameConsole:defcreate_game_picture(self):return'picture from console'classAntenna:defcreate_wave_picture(self):return'picture from wave'classSourceGameConsole(GameConsole):defget_picture(self):returnself.create_game_picture()classSourceAntenna(Antenna):defget_picture(self):returnself.create_wave_picture()classTV:def__init__(self,source):self.source=sourcedefshow_picture(self):returnself.source.get_picture()g=SourceGameConsole()a=SourceAntenna()game_tv=TV(g)cabel_tv=TV(a)print(game_tv.show_picture())print(cabel_tv.show_picture())
usingSystem;namespaceAdapter{classMainApp{staticvoidMain(){// Create adapter and place a requestTargettarget=newAdapter();target.Request();// Wait for userConsole.Read();}}// "Target"classTarget{publicvirtualvoidRequest(){Console.WriteLine("Called Target Request()");}}// "Adapter"classAdapter:Target{privateAdapteeadaptee=newAdaptee();publicoverridevoidRequest(){// Possibly do some other work// and then call SpecificRequestadaptee.SpecificRequest();}}// "Adaptee"classAdaptee{publicvoidSpecificRequest(){Console.WriteLine("Called SpecificRequest()");}}}
usingSystem;namespaceAdapter{classMainApp{staticvoidMain(){// Create adapter and place a requestAdapteradapter=newAdapter();adapter.Request();// Wait for userConsole.Read();}}// "Target"interfaceITarget{publicvoidRequest();}// You can use abstract class// "Adapter"classAdapter:Adaptee,ITarget{publicvoidRequest(){// Possibly do some other work// and then call SpecificRequestSpecificRequest();}}// "Adaptee"classAdaptee{publicvoidSpecificRequest(){Console.WriteLine("Called SpecificRequest()");}}}
program Adapter;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
(*Client use interface of class TTarget realized as TAdapter*)
(*TAdapter redirects the call to TAdaptee*)
type
TTarget = class
function Request: string; virtual;
end;
TAdaptee = class
function SpecificRequest: string;
end;
TAdapter = class(TTarget)
fAdaptee: TAdaptee;
function Request: string; override;
constructor Create;
end;
{ TTarget }
function TTarget.Request: string;
begin
Result:= 'Called Target Request()';
end;
{ TAdaptee }
function TAdaptee.SpecificRequest: string;
begin
Result:= 'Called SpecificRequest()';
end;
{ TAdapter }
constructor TAdapter.Create;
begin
fAdaptee:= TAdaptee.Create;
end;
function TAdapter.Request: string;
begin
(*Possibly do some other work and when call SpecificRequest*)
Result:= fAdaptee.SpecificRequest;
end;
var target: TTarget;
begin
try
{ TODO -oUser -cConsole Main : Insert code here }
(*create adapter and place a request*)
target:= TAdapter.Create;
WriteLn(target.Request);
WriteLn(#13#10+'Press any key to continue...');
ReadLn;
target.Free;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
↑Близость значений терминов оболочка и обёртка (англ.wrapper — используется как синоним декоратора) иногда приводит к путанице и Адаптер определяют как синоним шаблона Декоратор, в то время как это два разных шаблона и последний решает иную задачу, а именно: подключение дополнительных обязательств к объекту.
↑Разница состоит в том, что шаблон Фасад предназначен для упрощения интерфейса, тогда как шаблон Адаптер предназначен для приведения различных существующих интерфейсов к единому требуемому виду.
↑В устаревших версиях языка PHP доступ к СУБД реализован в виде набора функций, для каждой СУБД они имеют различные наименования и, иногда, различный набор используемых параметров, что приводит к значительным проблемам при переходе с одной СУБД на другую, если такой переход заранее не обеспечен использованием шаблона Адаптер.
Алан Шаллоуей, Джеймс Р. Тротт. Шаблоны проектирования. Новый подход к объектно-ориентированному анализу и проектированию = Design Patterns Explained: A New Perspective on Object-Oriented Design. — М.: «Вильямс», 2002. — С. 288. — ISBN 0-201-71594-5.
Э. Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссидес. Приёмы объектно-ориентированного проектирования. Паттерны проектирования = Design Patterns: Elements of Reusable Object-Oriented Software. — СПб.: «Питер», 2007. — С. 366. — ISBN 978-5-469-01136-1. (также ISBN 5-272-00355-1)
Эрик Фримен, Элизабет Фримен. Паттерны проектирования = Head First Design Patterns. — СПб.: Питер, 2011. — 656 с. — ISBN 978-5-459-00435-9.