"Здравей, приятелю!"

„Здравей, Билаабо!“

„Остава ни още малко време, така че ще ви разкажа за още три модела.“

"Още три? Колко са общо?"

„В момента има десетки популярни модели, но броят на „успешните решения“ е неограничен.“

— Разбирам. Значи трябва да науча няколко дузини модели?

„Докато нямате истински опит в програмирането, те няма да ви дадат много.“

„По-добре придобийте малко повече опит и след година се върнете към тази тема и се опитайте да ги разберете по-задълбочено. Поне няколко дузини от най-популярните шаблони за проектиране.“

„Грях е да не използваш чуждия опит и instead of това да измислиш нещо за 110-ти път.“

"Съгласен съм."

— Тогава да започваме.

Модел на адаптер (or обвивка).

Модели: адаптер, прокси, мост - 1

„Представете си, че идвате в Китай и установявате, че електрическите контакти следват различен стандарт. Дупките не са кръгли, а плоски. В този случай ще ви трябва адаптер.“

"Нещо подобно може да се случи и в програмирането. Класовете работят на подобни, но различни интерфейси. Така че трябва да направим адаптер между тях."

„Ето How изглежда:“

Пример
interface Time
{
 int getSeconds();
 int getMinutes();
 int getHours();
}

interface TotalTime
{
 int getTotalSeconds();
}

„Да предположим, че имаме два интерфейса: време  и  общо време .“

„Интерфейсът Time ви позволява да получите текущото време с помощта на методите getSeconds (),  getMinutes () и  getHours ().“

„ Интерфейсът TotalTime ви позволява да получите броя секунди, изминали от полунощ до текущия момент.“

„Какво трябва да направим, ако имаме обект TotalTime , но имаме нужда от обект Time or обратното?“

„Можем да напишем адаптерни класове за това. Например:“

Пример
class TotalTimeAdapter implements Time
{
 private TotalTime totalTime;
 public TotalTimeAdapter(TotalTime totalTime)
 {
  this.totalTime = totalTime;
 }

 int getSeconds()
 {
  return totalTime.getTotalSeconds() % 60; // seconds
 }

 int getMinutes()
 {
  return totalTime.getTotalSeconds() / 60; // minutes
 }

 int getHours()
 {
  return totalTime.getTotalSeconds() / (60 * 60); // hours
 }
}
Използване
TotalTime totalTime = TimeManager.getCurrentTime();
Time time = new TotalTimeAdapter(totalTime);
System.out.println(time.getHours() + " : " + time.getMinutes () + " : " +time.getSeconds());

„И адаптер в другата посока:“

Пример
class TimeAdapter implements TotalTime
{
 private Time time;
 public TimeAdapter(Time time)
 {
  this.time = time;
 }

 int getTotalSeconds()
 {
  return time.getHours() * 60 * 60 + time.getMinutes() * 60 + time.getSeconds();
 }
}
Използване
Time time = new Time();
TotalTime totalTime = new TimeAdapter(time);
System.out.println(time.getTotalSeconds());

"Ах. Харесва ми. Но има ли примери?"

"Разбира се! Като пример, InputStreamReader е класически адаптер. Той преобразува InputStream в Reader."

„Понякога този модел се нарича още обвивка, защото новият клас „обвива“ друг обект.“

„Можете да прочетете някои други интересни неща тук .“

Прокси модел

„Моделът на проксито е донякъде подобен на модела на обвивката. Но целта му не е да преобразува интерфейси, а да контролира достъпа до оригиналния обект, съхраняван вътре в прокси класа. Освен това и оригиналният клас, и проксито обикновено имат един и същ интерфейс, което улеснява заместването на обект от оригиналния клас с прокси обект."

"Например:"

Интерфейс на реалния клас
interface Bank
{
 public void setUserMoney(User user, double money);
 public int getUserMoney(User user);
}
Реализация на оригиналния клас
class CitiBank implements Bank
{
 public void setUserMoney(User user, double money)
 {
  UserDAO.updateMoney(user, money);
 }

 public int getUserMoney(User user)
 {
  return UserDAO.getMoney(user);
 }
}
Реализация на прокси класа
class BankSecurityProxy implements Bank
{
 private Bank bank;
 public BankSecurityProxy(Bank bank)
 {
  this.bank = bank;
 }
 public void setUserMoney(User user, double money)
 {
  if (!SecurityManager.authorize(user, BankAccounts.Manager))
  throw new SecurityException("User can’t change money value");

  bank.setUserMoney(user, money);
 }

 public int getUserMoney(User user)
 {
  if (!SecurityManager.authorize(user, BankAccounts.Manager))
  throw new SecurityException("User can’t get money value");

  return bank.getUserMoney(user);
 }
}

„В примера по-горе описахме интерфейса на Bank и класа CitiBank , реализация на този интерфейс.“

„Интерфейсът ви позволява да получите or промените салдото на сметката на потребителя.“

И след това създадохме BankSecurityProxy , който също имплементира банковия интерфейс и съхранява препратка към различен банков интерфейс. Методите от този клас проверяват дали потребителят е собственик на сметка or банков мениджър. Ако не е, тогава се хвърля SecurityException."

„Ето How работи на практика:“

Код без проверки за сигурност:
User user = AuthManager.authorize(login, password);
Bank bank = BankFactory.createUserBank(user);
bank.setUserMoney(user, 1000000);
Код с проверки за сигурност:
User user = AuthManager.authorize(login, password);
Bank bank = BankFactory.createUserBank(user);
bank = new BankSecurityProxy(bank);
bank.setUserMoney(user, 1000000);

„В първия пример създаваме банков обект и извикваме неговия метод setUserMoney .

„Във втория пример обгръщаме оригиналния обект на банката в обект BankSecurityProxy . Те имат същия интерфейс, така че последващият code продължава да работи, Howто е работил. Но сега ще се извършва проверка за сигурност при всяко извикване на метод.“

"Готино!"

„Да. Можете да имате много такива проксита. Например, можете да добавите друг прокси, който проверява дали салдото по сметката е твърде голямо. Банковият мениджър може да реши да вложи много пари в собствената си сметка и да се укрие в Куба със средствата ."

„Нещо повече... Създаването на всички тези вериги от обекти може да бъде поставено в клас BankFactory , където можете да активирате/деактивирате тези, от които се нуждаете.“

" BufferedReader работи на подобен принцип. Това е Reader , но върши допълнителна работа."

„Този ​​подход ви позволява да „сглобите“ обект с необходимата функционалност от различни „парчета“.“

„О, почти забравих. Прокситата се използват много по-широко от това, което току-що ви показах. Можете да прочетете за други употреби тук .“

Мост модел

Модели: адаптер, прокси, мост - 2

„Понякога, докато програмата се изпълнява, е необходимо значително да се промени функционалността на даден обект. Например, да предположим, че имате игра с герой магаре, който по-късно е превърнат в дракон от магьосник. Драконът има напълно различно поведение и свойства, но е същият обект!"

„Не можем ли просто да създадем нов обект и да приключим с него?“

„Не винаги. Да предположим, че вашето магаре е приятел с куп герои, or може би е било под влиянието на няколко заклинания, or е участвало в определени куестове. С други думи, обектът може вече да се използва на много места – и свързан с много други обекти. Така че в този случай не е опция просто да създадете нов обект."

— Е, Howво може да се направи тогава?

„Мостът е едно от най-успешните решения.“

„Този ​​модел включва разделяне на обект на два обекта: „обект на интерфейс“ и „обект за изпълнение“.“

"Каква е разликата между интерфейса и класа, който го прилага?"

„С интерфейс и клас завършваме с един обект. Но тук – имаме два. Вижте този пример:“

Пример
class User
{
 private UserImpl realUser;

 public User(UserImpl impl)
 {
  realUser = impl;
 }

 public void run() //Run
 {
  realUser.run();
 }

 public void fly() //Fly
 {
  realUser.fly();
 }
}

class UserImpl
{
 public void run()
 {
 }

 public void fly()
 {
 }
}

„И тогава можете да декларирате няколко подкласа на UserImpl, например UserDonkey (магаре) и UserDragon (дракон).“

"Все пак не разбирам How ще стане това."

"Ами нещо подобно:"

Пример
class User
{
 private UserImpl realUser;

 public User(UserImpl impl)
 {
  realUser = impl;
 }

 public void transformToDonkey()
 {
  realUser = new UserDonkeyImpl();
 }

 public void transformToDragon()
 {
  realUser = new UserDragonImpl();
 }
}
Как работи
User user = new User(new UserDonkey()); // Internally, we're a donkey
user.transformToDragon(); // Now we're a dragon internally

— Значи е нещо като прокси.

„Да, но в прокси основният обект може да се съхранява някъде отделно и instead of това codeът работи с проксита. Тук казваме, че всеки работи с основния обект, но неговите части се променят вътрешно.“

„А. Благодаря. Ще ми дадеш ли връзка, за да прочета повече за това?“

„Разбира се, Амиго, приятелю. Ето го: Мост модел .“