"Helló barát!"

– Szia, Bilaabo!

– Még van egy kis időnk, szóval még három mintáról mesélek.

"Még három? Hányan vannak összesen?"

"Jelenleg több tucat népszerű minta létezik, de a "sikeres megoldások" száma korlátlan."

– Értem. Szóval több tucat mintát kell megtanulnom?

– Amíg nincs valódi programozási tapasztalatod, addig nem fognak sokat adni.

"Jobb, ha szerez egy kis tapasztalatot, majd egy év múlva visszatér ehhez a témához, és próbálja meg mélyebben megérteni őket. Legalább pár tucat a legnépszerűbb tervezési minták közül."

"Bűn nem felhasználni valaki más tapasztalatait, és helyette 110. alkalommal kitalálni valamit."

"Egyetértek."

– Akkor kezdjük.

Adapter (vagy burkolat) minta

Minták: Adapter, Proxy, Bridge - 1

"Képzelje el, hogy Kínába jön, és azt tapasztalja, hogy az elektromos aljzatok más szabványt követnek. A lyukak nem kerekek, hanem laposak. Ebben az esetben adapterre lesz szüksége."

"Valami hasonló történhet a programozásban is. Az osztályok hasonló, de eltérő interfészeken működnek. Adaptert kell tehát készítenünk közöttük."

"Így néz ki:"

Példa
interface Time
{
 int getSeconds();
 int getMinutes();
 int getHours();
}

interface TotalTime
{
 int getTotalSeconds();
}

"Tegyük fel, hogy két interfészünk van: Time  és  TotalTime ."

"A Time felület lehetővé teszi az aktuális idő lekérését a getSeconds (),  a getMinutes () és  a getHours () metódusokkal."

"A TotalTime felület lehetővé teszi, hogy megtudja, hány másodperc telt el éjféltől az aktuális pillanatig."

"Mit tegyünk, ha van TotalTime objektumunk, de szükségünk van egy Time objektumra vagy fordítva?"

"Ehhez adapterosztályokat írhatunk. Például:"

Példa
 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
 }
}
 
Használat
TotalTime totalTime = TimeManager.getCurrentTime();
Time time = new TotalTimeAdapter(totalTime);
System.out.println(time.getHours() + " : " + time.getMinutes () + " : " +time.getSeconds());

"És egy adapter a másik irányba:"

Példa
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();
 }
}
Használat
Time time = new Time();
TotalTime totalTime = new TimeAdapter(time);
System.out.println(time.getTotalSeconds());

"Ah. tetszik. De van rá példa?"

"Természetesen! Például az InputStreamReader egy klasszikus adapter. Az InputStream-et olvasóvá alakítja."

"Néha ezt a mintát burkolónak is nevezik, mert az új osztály egy másik objektumot "burkol be".

– Olvashatsz itt más érdekességeket is .

Proxy minta

"A proxy minta némileg hasonlít a wrapper mintához. De célja nem az interfészek konvertálása, hanem a proxy osztályon belül tárolt eredeti objektumhoz való hozzáférés szabályozása. Sőt, mind az eredeti osztály, mind a proxy általában ugyanazzal a felülettel rendelkezik, ami megkönnyíti az eredeti osztály objektumának helyettesítését egy proxy objektummal."

"Például:"

A valós osztály felülete
interface Bank
{
 public void setUserMoney(User user, double money);
 public int getUserMoney(User user);
}
Az eredeti osztály megvalósítása
class CitiBank implements Bank
{
 public void setUserMoney(User user, double money)
 {
  UserDAO.updateMoney(user, money);
 }

 public int getUserMoney(User user)
 {
  return UserDAO.getMoney(user);
 }
}
A proxy osztály megvalósítása
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);
 }
}

"A fenti példában leírtuk a Bank felületet és a CitiBank osztályt, ennek a felületnek a megvalósítását."

"A felület lehetővé teszi a felhasználói számlaegyenleg lekérését vagy módosítását."

Ezután létrehoztuk a BankSecurityProxy-t , amely szintén megvalósítja a Bank felületet, és egy másik banki felületre való hivatkozást tárol. Ennek az osztálynak a metódusai ellenőrzik, hogy a felhasználó számlatulajdonos vagy bankmenedzser. Ha nem, akkor biztonsági kivételt dobnak ki."

"Íme, hogyan működik a gyakorlatban:"

Kód biztonsági ellenőrzések nélkül :
User user = AuthManager.authorize(login, password);
Bank bank = BankFactory.createUserBank(user);
bank.setUserMoney(user, 1000000);
Kód biztonsági ellenőrzésekkel :
User user = AuthManager.authorize(login, password);
Bank bank = BankFactory.createUserBank(user);
bank = new BankSecurityProxy(bank);
bank.setUserMoney(user, 1000000);

"Az első példában létrehozunk egy bankobjektumot, és meghívjuk a setUserMoney metódust.

"A második példában az eredeti bankobjektumot egy BankSecurityProxy objektumba csomagoljuk . Ugyanaz a felületük van, így a következő kód továbbra is ugyanúgy működik, mint korábban. Most azonban biztonsági ellenőrzést hajtanak végre minden metódus meghívásakor."

"Menő!"

"Igen. Sok ilyen meghatalmazottja lehet. Például hozzáadhat egy másik proxyt, amely ellenőrzi, hogy a számlaegyenleg túl nagy-e. A bankvezető dönthet úgy, hogy sok pénzt helyez a saját számlájára, és elszökik Kubába a pénzeszközökkel. ."

"Mi több... Mindezen objektumláncok létrehozása behelyezhető egy BankFactory osztályba, ahol engedélyezheti/letilthatja a szükségeseket."

" A BufferedReader hasonló elven működik. Ez egy Reader , de további munkát végez."

"Ez a megközelítés lehetővé teszi, hogy különféle "darabokból" „összeállítson” egy objektumot a szükséges funkciókkal.

"Ó, majdnem elfelejtettem. A proxykat sokkal szélesebb körben használják, mint amit az imént mutattam. Egyéb felhasználási módokról itt olvashat ."

Híd minta

Minták: adapter, proxy, híd – 2

"Néha a program futása közben jelentősen meg kell változtatni egy objektum funkcionalitását. Tegyük fel például, hogy van egy játékod egy szamár karakterrel, akit később egy mágus sárkánnyá változtat. A sárkány viselkedése és tulajdonságai teljesen eltérőek, de ez ugyanaz a tárgy!"

– Nem tudnánk egyszerűen létrehozni egy új objektumot, és elkészülni vele?

"Nem mindig. Tegyük fel, hogy a szamarad egy csomó karakterrel barátkozik, esetleg több varázslat hatása alatt állt, vagy részt vett bizonyos küldetésekben. Más szóval, az objektum már sok helyen használatban van - és sok más objektumhoz kapcsolódik. Tehát ebben az esetben nem lehet egyszerűen új objektumot létrehozni."

– Nos, akkor mit lehet tenni?

"A Bridge minta az egyik legsikeresebb megoldás."

"Ez a minta azt jelenti, hogy egy objektumot két objektumra osztanak fel: egy "interfészobjektumra" és egy "megvalósítási objektumra".

"Mi a különbség az interfész és az azt megvalósító osztály között?"

"Egy interfész és egy osztály esetén egy objektumhoz jutunk. Itt azonban kettő van. Nézze meg ezt a példát:"

Példa
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()
 {
 }
}

"És ezután deklarálhat több UserImpl alosztályt, például UserDonkey (szamár) és UserDragon (sárkány)."

– Mindazonáltal nem igazán értem, hogyan fog ez működni.

– Nos, valami ilyesmi:

Példa
class User
{
 private UserImpl realUser;

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

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

 public void transformToDragon()
 {
  realUser = new UserDragonImpl();
 }
}
Hogyan működik
User user = new User(new UserDonkey()); // Internally, we're a donkey
user.transformToDragon(); // Now we're a dragon internally

– Szóval valami proxy.

"Igen, de egy proxyban a fő objektumot lehetne valahol külön tárolni, és helyette a kód proxykkal működik. Itt azt mondjuk, hogy mindenki a fő objektummal dolgozik, de a részei belülről változnak."

"Ah. Köszönöm. Adsz egy linket, ahol többet olvashatok róla?"

"Persze, Amigo, barátom. Tessék: Hídminta ."