"안녕하세요, 아미고! 오늘은 BufferedInputStream 클래스에 대한 몇 가지 흥미로운 점을 말씀드리지만 « 래퍼 »와 « 설탕 한 봉지 »부터 시작하겠습니다."

""포장지"와 "설탕 봉지"가 무슨 뜻인가요?"

"이것들은 은유입니다. 들어보세요. 그래서…"

«래퍼»(또는 «데코레이터») 디자인 패턴은 상속을 사용하지 않고 개체 기능을 확장하기 위한 매우 간단하고 편리한 메커니즘입니다.

BufferedInputStream - 1

getName과 setName이라는 두 가지 메서드가 있는 Cat 클래스가 있다고 가정합니다.

자바 코드 설명
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());
}
어떻게 사용될 수 있는지에 대한 예입니다.

«Oscar»가 콘솔에 표시됩니다.

cat 개체에 대한 메서드 호출을 가로채고 약간의 변경을 수행해야 한다고 가정합니다 . 이를 위해 자체 래퍼 클래스로 래핑 해야 합니다 .

일부 개체에 대한 메서드 호출 주위에 자체 코드를 "래핑" 하려면 다음을 수행해야 합니다.

1) 자체 래퍼 클래스를 만들고 래핑할 개체와 동일한 클래스/인터페이스에서 상속합니다.

2) 래핑할 객체를 클래스의 생성자에 전달합니다.

3) 새 클래스의 모든 메서드를 재정의합니다. 재정의된 각 메서드 내에서 래핑된 개체의 메서드를 호출합니다.

4) 원하는 대로 변경합니다. 메서드 호출이 수행하는 작업을 변경하고 매개 변수를 변경하거나 다른 작업을 수행합니다.

아래 예제에서는 Cat 개체의 getName 메서드에 대한 호출을 가로채 반환 값을 약간 변경합니다.

자바 코드 설명
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);
 }
}
래퍼 클래스. 클래스는 원본 객체에 대한 참조를 제외한 어떤 데이터도 저장하지 않습니다.
클래스는 생성자에게 전달된 원래 객체(setName)에 대한 호출을 "던질" 수 있습니다. 또한 이러한 호출을 "잡아서" 해당 매개변수 및/또는 결과를 수정할 수 있습니다 .
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());
}
어떻게 사용될 수 있는지에 대한 예입니다.

«오스카라는 이름의 고양이».
콘솔에 표시됩니다

즉, 각 원래 개체를 원래 개체에 대한 링크를 받는 래퍼 개체로 조용히 대체합니다. 래퍼의 모든 메서드 호출은 원래 개체로 전달되며 모든 것이 시계처럼 실행됩니다.

"좋아요. 솔루션은 간단하고 기능적입니다."

"또한 «설탕 주머니»에 대해서도 말씀드리겠습니다. 이것은 디자인 패턴이라기보다는 은유입니다. 버퍼와 버퍼링이라는 단어에 대한 은유입니다. 버퍼링이란 무엇이며 왜 필요한가요?"

BufferedInputStream - 2

오늘은 Rishi가 요리할 차례이고 당신이 그를 돕고 있다고 가정해 봅시다. Rishi는 아직 여기에 없지만 차를 마시고 싶습니다. 나는 당신에게 설탕 한 스푼을 가져다달라고 부탁합니다. 지하실로 가서 설탕 한 봉지를 찾으십시오. 가방 전체를 가져오셔도 되지만 저는 가방이 필요하지 않습니다. 한 스푼만 있으면 됩니다. 그런 다음 착한 로봇처럼 한 숟가락 떠서 나에게 가져다준다. 나는 그것을 차에 첨가하지만 여전히 충분히 달지 않습니다. 그리고 하나 더 가져다 달라고 합니다. 당신은 다시 지하실로 가서 한 숟가락 더 가져옵니다. 그런 다음 Ellie가 와서 그녀를 위해 설탕을 가져 오라고 요청합니다 ... 이 모든 것은 너무 오래 걸리고 비효율적입니다.

Rishi가 와서이 모든 것을보고 설탕이 가득 찬 설탕 그릇을 가져다달라고 부탁합니다. 그런 다음 Ellie와 나는 Rishi에게 설탕을 요구하기 시작합니다. 그는 단순히 설탕 그릇에서 그것을 우리에게 제공합니다. 그게 전부입니다.

Rishi가 나타난 후에 일어난 일을 버퍼링 이라고 합니다 . 설탕 그릇이 버퍼입니다. 버퍼링 덕분에 "클라이언트"는 버퍼에서 데이터를 작은 부분 으로 읽을 수 있으며 버퍼는 시간과 노력을 절약하기 위해 소스에서 많은 부분 을 읽습니다 .

"멋진 예야, Kim. 나는 완벽하게 이해한다. 설탕 한 스푼을 요구하는 것은 스트림에서 한 바이트를 읽는 것과 같다."

"정확합니다. BufferedInputStream 클래스는 버퍼링된 래퍼의 전형적인 예입니다. InputStream 클래스를 래핑합니다. 원래 InputStream에서 큰 블록의 데이터를 버퍼로 읽은 다음 우리가 버퍼에서 조각별로 가져옵니다. 그것부터 읽어봐."

"아주 좋습니다. 모두 명확합니다. 쓰기용 버퍼가 있습니까?"

"그렇지."

"예를 들어?"

"쓰레기통을 상상해 보세요. 쓰레기를 매번 소각로에 넣기 위해 밖에 나가지 않고 그냥 쓰레기통에 버리면 됩니다. 그런 다음 Bubba는 2주에 한 번씩 캔을 밖으로 가져갑니다. 고전적인 버퍼입니다."

"얼마나 흥미롭군! 그런데 설탕 한 봉지보다 훨씬 더 선명해."

"그리고 flush() 메서드는 쓰레기를 바로 버리는 것과 같습니다. 손님이 도착하기 전에 사용할 수 있습니다."