"Hallo, Amigo! Vandaag vertel ik je een paar interessante dingen over de klasse BufferedInputStream, maar laten we beginnen met « wikkels » en een « zak suiker ».

"Wat bedoel je met "wikkel" en "zak suiker"?"

"Dit zijn metaforen. Luister. Dus..."

Het «wrapper» (of «decorator») ontwerppatroon is een vrij eenvoudig en handig mechanisme om objectfunctionaliteit uit te breiden zonder gebruik te maken van overerving.

GebufferdeInputStream - 1

Stel dat we een Cat-klasse hebben met twee methoden: getName en setName:

Java-code Beschrijving
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;
 }
}
De klasse Cat heeft twee methoden: getName en 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());
}
Een voorbeeld van hoe het gebruikt kan worden.

«Oscar» wordt weergegeven op de console.

Stel dat we een methodeaanroep op een cat- object moeten onderscheppen en misschien enkele kleine wijzigingen moeten aanbrengen. Hiervoor moeten we het in zijn eigen wrapper-klasse verpakken .

Als we onze eigen code rond de methode-aanroepen op een object willen "wikkelen" , dan moeten we:

1) Maak onze eigen wrapperklasse en erf van dezelfde klasse/interface als het object dat moet worden verpakt.

2) Geef het te verpakken object door aan de constructor van onze klasse.

3) Negeer alle methodes in onze nieuwe klasse. Roep de methoden van het ingepakte object aan binnen elk van de overschreven methoden.

4) Breng de gewenste wijzigingen aan: verander wat de methodeaanroepen doen, verander hun parameters en/of doe iets anders.

In het onderstaande voorbeeld onderscheppen we oproepen naar de getName-methode van een Cat-object en wijzigen we de retourwaarde enigszins.

Java-code Beschrijving
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;
 }
}
De klasse Cat bevat twee methoden: getName en 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);
 }
}
De wikkelklasse. De klasse slaat geen gegevens op behalve een verwijzing naar het oorspronkelijke object.
De klasse kan oproepen "gooien" naar het oorspronkelijke object (setName) dat aan de constructor is doorgegeven. Het kan deze oproepen ook "vangen" en hun parameters en/of resultaten wijzigen .
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());
}
Een voorbeeld van hoe het gebruikt kan worden.

«Een kat genaamd Oscar».
wordt weergegeven op de console

Met andere woorden, we vervangen stilletjes elk origineel object door een wrapper-object, dat een link naar het originele object ontvangt. Alle methodeaanroepen op de wrapper worden doorgestuurd naar het oorspronkelijke object en alles loopt op rolletjes.

"Ik vind het leuk. De oplossing is eenvoudig en functioneel."

"Ik zal je ook vertellen over een "zak suiker". Dit is eerder een metafoor dan een ontwerppatroon. Een metafoor voor het woord buffer en buffering. Wat is buffering en waarom hebben we het nodig?"

GebufferdeInputStream - 2

Laten we zeggen dat het vandaag Rishi's beurt is om te koken, en jij helpt hem. Rishi is er nog niet, maar ik wil thee drinken. Ik vraag je om me een lepel suiker te brengen. Je gaat naar de kelder en vindt een zak suiker. Je kunt me de hele tas brengen, maar ik heb de tas niet nodig. Ik heb maar één lepel nodig. Dan, als een goede robot, neem je een lepel en breng je die naar mij. Ik voeg het toe aan de thee, maar het is nog steeds niet zoet genoeg. En ik vraag je om me er nog een te brengen. Je gaat weer naar de kelder en brengt nog een lepel. Dan komt Ellie langs, en ik vraag je om suiker voor haar mee te nemen... Dit alles duurt te lang en is inefficiënt.

Rishi komt, ziet dit allemaal en vraagt ​​je hem een ​​suikerpot vol suiker te brengen. Dan beginnen Ellie en ik Rishi om suiker te vragen. Hij serveert het ons gewoon uit de suikerpot, en dat is alles.

Wat er gebeurde nadat Rishi kwam opdagen, wordt buffering genoemd : de suikerpot is een buffer. Dankzij buffering kunnen "clients" gegevens in kleine delen uit een buffer lezen , terwijl de buffer ze, om tijd en moeite te besparen, in grote delen uit de bron leest .

"Dat is een cool voorbeeld, Kim. Ik begrijp het perfect. Het verzoek om een ​​lepel suiker is als het lezen van een byte uit een stream."

"Precies. De BufferedInputStream- klasse is een klassiek voorbeeld van een gebufferde wrapper. Het verpakt de InputStream-klasse. Het leest gegevens van de oorspronkelijke InputStream in grote blokken in een buffer en haalt het vervolgens stuk voor stuk uit de buffer zoals we lees ervan."

"Heel goed. Het is allemaal duidelijk. Zijn er buffers om te schrijven?"

"Oh zeker."

"Misschien een voorbeeld?"

"Stel je een prullenbak voor. In plaats van elke keer naar buiten te gaan om afval in een verbrandingsoven te gooien, gooi je het gewoon in de prullenbak. Dan zet Bubba de bak eens in de twee weken buiten. Een klassieke buffer."

'Wat interessant! En trouwens een stuk overzichtelijker dan een zak suiker.'

"En de flush()-methode is alsof je meteen de vuilnis buiten zet. Je kunt het gebruiken voordat de gasten arriveren."