„Здравей, Амиго! Днес ще ти кажа няколко интересни неща за класа BufferedInputStream, но нека започнем с « обвивки » и « торба захар ».“

„Какво имаш предвид под „опаковка“ и „торба със захар“?“

„Това са метафори. Слушай. Така че…“

Шаблонът за проектиране „обвивка“ (or „декоратор“) е доста прост и удобен механизъм за разширяване на функционалността на обекта без използване на наследяване.

BufferedInputStream - 1

Да предположим, че имаме клас Cat с два метода: getName и setName:

Java code Описание
class Cat
{
 private String name;
 public Cat(String name)
 {
  this.name = name;
 }
 public String getName()
 {
  return this.name;
 }
 public void setName(String name)
 {
  this.name = name;
 }
}
Класът Cat има два метода: getName и setName
public static void main(String[] args)
{
 Cat cat = new Cat("Oscar");

 printName(cat);
}

public static void printName(Cat cat)
{
 System.out.println(cat.getName());
}
Пример за това How може да се използва.

На конзолата ще се покаже «Оскар».

Да предположим, че трябва да прихванем извикване на метод на котка обект и може би да направим някои малки промени. За целта трябва да го обвием в неговия собствен клас обвивка.

Ако искаме да "увием" нашия собствен code около извикванията на метода на няHowъв обект, тогава трябва да:

1) Създайте наш собствен клас обвивка и наследете от същия клас/интерфейс като обекта, който ще бъде обвит.

2) Предавайте обекта, който ще бъде обвит, към конструктора на нашия клас.

3) Заменете всички методи в нашия нов клас. Извикване на методите на обвития обект във всеки от заменените методи.

4) Направете Howвито искате промени: променете Howво правят извикванията на метода, променете техните параметри и/or направете нещо друго.

В примера по-долу ние прихващаме извиквания към метода getName на обект Cat и леко променяме неговата върната стойност.

Java code Описание
class Cat
{
 private String name;
 public Cat(String name)
 {
  this.name = name;
 }
 public String getName()
 {
  return this.name;
 }
 public void setName(String name)
 {
  this.name = name;
 }
}
Класът Cat съдържа два метода: getName и setName.
class CatWrapper extends Cat
{
 private Cat original;
 public CatWrapper (Cat cat)
 {
  super(cat.getName());
  this.original = cat;
 }

 public String getName()
 {
  return "A cat named " + original.getName();
 }

 public void setName(String name)
 {
  original.setName(name);
 }
}
Класът на обвивката. Класът не съхранява ниHowви данни, освен препратка към оригиналния обект.
Класът може да "хвърля" извиквания към оригиналния обект (setName), предаден на конструктора. Той може също така да "улови" тези повиквания и да промени техните параметри и/or резултати .
public static void main(String[] args)
{
 Cat cat = new Cat("Oscar");
 Cat catWrap = new CatWrapper (cat);
 printName(catWrap);
}

public static void printName(Cat named)
{
 System.out.println(named.getName());
}
Пример за това How може да се използва.

„Котка на име Оскар“.
ще се покаже на конзолата

С други думи, ние тихо заместваме всеки оригинален обект с обвиващ обект, който получава връзка към оригиналния обект. Всички извиквания на метода на обвивката се препращат към оригиналния обект и всичко работи като часовник.

"Харесва ми. Решението е просто и функционално."

„Ще ви разкажа също за „торба със захар“. Това е по-скоро метафора, отколкото модел на проектиране. Метафора за думата буфер и буфериране. Какво е буфериране и защо имаме нужда от него?“

BufferedInputStream - 2

Да кажем, че днес е ред на Риши да готви и вие му помагате. Риши още не е тук, но искам да пия чай. Моля те да ми донесеш лъжица захар. Отивате в мазето и намирате торба със захар. Можете да ми донесете цялата чанта, но не ми трябва чантата. Трябва ми само една лъжица. После като добър робот взимаш една лъжица и ми я носиш. Добавям го към чая, но пак не е достатъчно сладък. И те моля да ми донесеш още един. Отново отиваш в мазето и носиш друга лъжица. Тогава идва Ели и те моля да й донесеш захар... Всичко това отнема твърде много време и е неефективно.

Риши идва, вижда всичко това и те моли да му донесеш купа със захар, пълна със захар. Тогава Ели и аз започваме да молим Риши за захар. Просто ни го сервира от захарницата и това е всичко.

Това, което се случи, след като Риши се появи, се нарича буфериране : захарницата е буфер. Благодарение на буферирането, "клиентите" могат да четат данни от буфер на малки порции , докато буферът, за да спести време и усorя, ги чете от източника на големи порции .

"Това е готин пример, Ким. Разбирам перфектно. Искането за лъжица захар е като да прочетеш един byte от поток."

„Точно така. Класът BufferedInputStream е класически пример за буферирана обвивка. Той обвива класа InputStream. Той чете данни от оригиналния InputStream на големи блокове в буфер и след това ги изтегля от буфера част по част, докато ние прочетете от него."

"Много добре. Всичко е ясно. Има ли буфери за писане?"

"Разбира се."

— Може би пример?

„Представете си кофа за боклук. Вместо всеки път да излизате навън, за да поставяте боклук в пещ за изгаряне, вие просто го хвърляте в кофата за боклук. След това Буба изнася кофата навън веднъж на две седмици. Класически буфер.“

„Колко интересно! И между другото много по-прозрачен от торба захар.“

„А методът flush() е като да изхвърлите боклука веднага. Можете да го използвате, преди да пристигнат гостите.“