"Hej ven!"

"Hej, Bilaabo!"

"Vi har stadig lidt tid tilbage, så jeg vil fortælle dig om tre mønstre mere."

"Tre mere? Hvor mange er der i alt?"

"Der er i øjeblikket snesevis af populære mønstre, men antallet af "vellykkede løsninger" er ubegrænset."

"Jeg kan se. Så jeg skal lære flere dusin mønstre?"

"Indtil du har rigtig programmeringserfaring, vil de ikke give dig meget."

"Du må hellere få lidt mere erfaring og så om et år vende tilbage til dette emne og prøve at forstå dem mere dybt. Mindst et par dusin af de mest populære designmønstre."

"Det er synd ikke at bruge andres erfaringer og i stedet finde på noget for 110. gang."

"Jeg er enig."

"Så lad os begynde."

Adapter (eller omslag) mønster

Mønstre: Adapter, Proxy, Bridge - 1

"Forestil dig, at du kommer til Kina og finder ud af, at stikkontakterne følger en anden standard. Hullerne er ikke runde, men flade. I dette tilfælde skal du bruge en adapter."

"Noget lignende kan også ske i programmering. Klasser opererer på lignende, men forskellige grænseflader. Så vi skal lave en adapter mellem dem."

"Sådan ser det ud:"

Eksempel
interface Time
{
 int getSeconds();
 int getMinutes();
 int getHours();
}

interface TotalTime
{
 int getTotalSeconds();
}

"Antag, at vi har to grænseflader: Time  og  TotalTime ."

"Time-grænsefladen giver dig mulighed for at få den aktuelle tid ved hjælp af metoderne getSeconds (),  getMinutes () og  getHours ().

" TotalTime- grænsefladen lader dig få det antal sekunder, der er gået fra midnat til det aktuelle øjeblik."

"Hvad skal vi gøre, hvis vi har et TotalTime- objekt, men vi har brug for et Time- objekt eller omvendt?"

"Vi kan skrive adapterklasser til dette. For eksempel:"

Eksempel
 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
 }
}
 
Brug
TotalTime totalTime = TimeManager.getCurrentTime();
Time time = new TotalTimeAdapter(totalTime);
System.out.println(time.getHours() + " : " + time.getMinutes () + " : " +time.getSeconds());

"Og en adapter i den anden retning:"

Eksempel
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();
 }
}
Brug
Time time = new Time();
TotalTime totalTime = new TimeAdapter(time);
System.out.println(time.getTotalSeconds());

"Ah. Jeg kan godt lide det. Men er der nogle eksempler?"

"Selvfølgelig! Som et eksempel er InputStreamReader en klassisk adapter. Den konverterer en InputStream til en Reader."

"Nogle gange kaldes dette mønster også for en indpakning, fordi den nye klasse 'ombryder' et andet objekt."

"Du kan læse nogle andre interessante ting her ."

Proxy mønster

"Proxy-mønsteret minder lidt om wrapper-mønsteret. Men dets formål er ikke at konvertere grænseflader, men at kontrollere adgangen til det originale objekt, der er gemt inde i proxy-klassen. Desuden har både den originale klasse og proxyen normalt den samme grænseflade, hvilket gør det nemmere at erstatte et objekt af den originale klasse med et proxy-objekt."

"For eksempel:"

Den rigtige klasses grænseflade
interface Bank
{
 public void setUserMoney(User user, double money);
 public int getUserMoney(User user);
}
Implementering af den oprindelige klasse
class CitiBank implements Bank
{
 public void setUserMoney(User user, double money)
 {
  UserDAO.updateMoney(user, money);
 }

 public int getUserMoney(User user)
 {
  return UserDAO.getMoney(user);
 }
}
Implementering af proxy-klassen
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);
 }
}

"I eksemplet ovenfor beskrev vi Bank- grænsefladen og CitiBank- klassen, en implementering af denne grænseflade."

"Grænsefladen giver dig mulighed for at få eller ændre en brugers kontosaldo."

Og så skabte vi BankSecurityProxy , som også implementerer Bank- grænsefladen og gemmer en reference til en anden Bank-grænseflade. Metoderne i denne klasse kontrollerer, om brugeren er kontoejeren eller en bankchef. Hvis det ikke er det, bliver der kastet en SecurityException."

"Sådan fungerer det i praksis:"

Kode uden sikkerhedstjek:
User user = AuthManager.authorize(login, password);
Bank bank = BankFactory.createUserBank(user);
bank.setUserMoney(user, 1000000);
Kode med sikkerhedstjek:
User user = AuthManager.authorize(login, password);
Bank bank = BankFactory.createUserBank(user);
bank = new BankSecurityProxy(bank);
bank.setUserMoney(user, 1000000);

"I det første eksempel opretter vi et bankobjekt og kalder dets setUserMoney- metode.

"I det andet eksempel pakker vi det originale bankobjekt ind i et BankSecurityProxy- objekt. De har samme grænseflade, så den efterfølgende kode fortsætter med at fungere, som den gjorde. Men nu vil der blive udført et sikkerhedstjek, hver gang en metode kaldes."

"Fedt nok!"

"Jep. Du kan have mange sådanne fuldmagter. For eksempel kan du tilføje en anden fuldmagt, der kontrollerer, om kontosaldoen er for stor. Bankchefen kan beslutte at sætte en masse penge på sin egen konto og stikke af til Cuba med midlerne ."

"Hvad mere er... Oprettelsen af ​​alle disse kæder af objekter kan placeres i en BankFactory- klasse, hvor du kan aktivere/deaktivere dem, du har brug for."

" BufferedReader arbejder efter et lignende princip. Det er en Reader , men det udfører yderligere arbejde."

"Denne tilgang lader dig "samle" et objekt med den nødvendige funktionalitet fra forskellige "stykker"."

"Åh, det glemte jeg næsten. Proxies bruges meget mere udbredt end det, jeg lige har vist dig. Du kan læse om andre anvendelser her ."

Bromønster

Mønstre: Adapter, Proxy, Bridge - 2

"Nogle gange, mens et program kører, er det nødvendigt at ændre et objekts funktionalitet markant. Antag for eksempel, at du har et spil med en æselfigur, som senere bliver forvandlet til en drage af en magiker. Dragen har en helt anden adfærd og egenskaber, men det er samme genstand!"

"Kan vi ikke bare lave et nyt objekt og være færdige med det?"

"Ikke altid. Antag, at dit æsel er venner med en masse karakterer, eller måske var det under indflydelse af flere besværgelser, eller det var involveret i visse quests. Med andre ord kan objektet allerede være i brug mange steder - og knyttet til mange andre objekter. Så i dette tilfælde er det ikke en mulighed blot at oprette et nyt objekt."

"Jamen, hvad kan man så gøre?"

"Bromønsteret er en af ​​de mest succesrige løsninger."

"Dette mønster indebærer opdeling af et objekt i to objekter: et «grænsefladeobjekt» og et «implementeringsobjekt».

"Hvad er forskellen mellem grænsefladen og den klasse, der implementerer den?"

"Med en grænseflade og en klasse ender vi med ét objekt. Men her - vi har to. Se på dette eksempel:"

Eksempel
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()
 {
 }
}

"Og så kan du erklære flere underklasser af UserImpl, for eksempel UserDonkey (æsel) og UserDragon (drage)."

"Alligevel forstår jeg ikke rigtig, hvordan det her vil fungere."

"Nå, sådan noget her:"

Eksempel
class User
{
 private UserImpl realUser;

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

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

 public void transformToDragon()
 {
  realUser = new UserDragonImpl();
 }
}
Hvordan det virker
User user = new User(new UserDonkey()); // Internally, we're a donkey
user.transformToDragon(); // Now we're a dragon internally

"Så det er noget som en proxy."

"Ja, men i en proxy kunne hovedobjektet lagres et sted separat, og koden fungerer med proxyer i stedet for. Her siger vi, at alle arbejder med hovedobjektet, men dets dele ændrer sig internt."

"Ah. Tak. Vil du give mig et link til at læse mere om det?"

"Selvfølgelig, Amigo, min ven. Her går du: Bromønster ."