## 30. 입출력 기능 확장에 상속 대신 Decorator 패턴을 적용하기
- 상속 vs Decorator 패턴(GoF)
- 기존 코드를 손대지 않고 기능 확장하는 방법
- 상속: 기능 확장 용이
- Decorator: 기능 확장 및 기능 제거 용이
- BufferedDataInputStream 분해
- BufferedInputStream, DataInputStream, FileInputStream
- BufferedDataOutputStream 분해
- BufferedOutputStream, DataOutputStream, FileOutputStream
## 31. Java Stream API 로 교체하기
- 입출력 관련 클래스를 자바 스트림 클래스로 교체
- java.io.* 패키지의 클래스 사용
java 17 api 참고
입출력 기능 확장에 상속 대신 Decorator 패턴을 적용하기
DataInputStream
DataInputStream.java
ㄴ FileInputStream -> InputStream 으로 변경
=> java 17 api 문서에서 확인 시 FileInputStream 은 InputStream 을 상속함
DataInputStream.java
ㄴ original 변수 : 기존 InputStream 객체에 대한 참조를 저장하는 데 사용
=> 저장된 original 객체는 DataInputStream 객체 내에서 다양한 작업을 수행하는 데 사용될 수 있음
ㄴ 예를 들어, DataInputStream 클래스에서 InputStream 객체의 메서드를 호출하거나 추가적인 기능을 구현할 수 있음
=> DataInputStream 클래스는 InputStream 객체의 동작을 확장하거나 재정의할 수 있음
ㄴ 생성자는 InputStream 객체를 매개변수로 받아 original 변수에 할당
ㄴ InputStream 의 메서드
=> 추상 메서드로 선언되어 있으므로 구현 필요
DataInputStream.java
=>
DataInputStream.java
ㄴ read() 메서드를 재정의하여 original.read()를 호출하여 데이터를 읽도록 구현
=> DataInputStream의 read() 메서드를 호출하면 실제로 original 객체의 read() 메서드가 실행되어 데이터를 읽게 됨
ㄴ DataInputStream은 InputStream 클래스의 read() 메서드를 그대로 상속받아 사용
=> DataInputStream은 InputStream 클래스의 기본 동작을 그대로 유지하면서, 추가적인 기능을 구현하거나 수정할 수 있음
ㄴ InputStream 의 메서드
DataInputStream.java
=>
DataInputStream.java
ㄴ DataInputStream의 read(byte[] b) 메서드를 호출하면 실제로 original 객체의 read(byte[] b) 메서드가 실행되어 데이터를 b 배열에 읽게 됨
ㄴ DataInputStream은 InputStream 클래스의 read(byte[] b) 메서드를 그대로 상속받아 사용
=> DataInputStream은 InputStream 클래스의 기본 동작을 그대로 유지하면서, 추가적인 기능을 구현하거나 수정할 수 있음
=> read() 메서드는 InputStream 클래스에서 추상 메서드로 선언되어 있으며, DataInputStream 클래스는 InputStream 클래스를 상속하고 있어서 DataInputStream 클래스에서는 read() 메서드를 반드시 구현해야 하지만, read(byte[] b) 메서드는 InputStream 클래스에 존재하고 추상 메서드도 아니므로 제거해도 됨
DataInputStream.java
=>
DataInputStream.java
ㄴ DataInputStream 클래스에서 close() 메서드를 오버라이딩
=> InputStream의 close() 메서드는 입력 스트림을 닫고 사용한 자원을 해제하는 역할을 함
=> 파일이나 네트워크 등의 입력 소스와의 연결을 종료하고, 관련된 자원들을 정리함
DataOutputStream
DataOutputStream.java
ㄴ original 변수 : 기존 OutputStream 객체에 대한 참조를 저장하는 데 사용
=> 저장된 original 객체는 DataOutputStream 객체 내에서 다양한 작업을 수행하는 데 사용될 수 있음
ㄴ 예를 들어, DataOutputStream 클래스에서 OutputStream 객체의 메서드를 호출하거나 추가적인 기능을 구현할 수 있음
=> DataOutputStream 클래스는 OutputStream 객체의 동작을 확장하거나 재정의할 수 있음
ㄴ 생성자는 OutputStream 객체를 매개변수로 받아 original 변수에 할당
DataOutputStream.java
=>
DataOutputStream.java
ㄴ write(int b) 메서드는 OutputStream 클래스의 추상 메서드를 재정의한 것
ㄴ int 형식의 파라미터 b를 받아서 데이터를 출력함
ㄴ 메서드 내부에서는 original.write(b)를 호출하여 original 객체를 통해 데이터를 출력하도록 구현
=> DataOutputStream 클래스는 외부로부터 전달받은 original 객체를 사용하여 데이터를 출력함
DataOutputStream.java
=>
DataOutputStream.java
ㄴ flush() 메서드는 출력 스트림에 남아있는 모든 데이터를 강제로 출력함
ㄴ 버퍼에 저장된 데이터를 출력 소스로 모두 전송하고, 버퍼를 비우는 역할을 함
=> 이를 통해 데이터의 일부가 버퍼에만 남아있는 상황을 방지하고, 데이터의 신뢰성과 일관성을 유지할 수 있음
DataOutputStream.java
=>
DataOutputStream.java
ㄴ DataInputStream 클래스에서 close() 메서드를 오버라이딩
ㄴ close() 메서드는 출력 스트림을 닫고 사용한 자원을 해제함
ㄴ flush() 메서드를 호출하여 남은 데이터를 출력한 후, 출력 소스와의 연결을 종료하고 관련된 자원을 정리함
App.java
ㄴ FileInputStream을 사용하여 member.data 파일을 읽기 위한 스트림인 in0을 생성
ㄴ 이 in0 스트림을 DataInputStream의 생성자에 전달하여 in 스트림을 생성
=> DataInputStream을 사용하여 파일에서 데이터를 읽을 수 있게 됨
App.java
ㄴ Item 도 Member 와 동일한 방식
App.java
ㄴ Board 도 Item, Member 와 동일한 방식
App.java
ㄴ FileOutputStream을 사용하여 member.data 파일에 데이터를 쓰기 위한 스트림인 out0을 생성
ㄴ 이 out0 스트림을 DataOutputStream의 생성자에 전달하여 out 스트림을 생성
=> DataOutputStream을 사용하여 데이터를 파일에 쓸 수 있게 됨
ㄴ FileOutputStream은 "member.data" 파일에 대한 출력 스트림을 생성하기 위한 클래스임
ㄴ 파일이 존재하지 않으면 새로운 파일이 생성되며, 파일이 이미 존재한다면 그 파일에 데이터를 추가하여 쓸 수 있음
ㄴ FileOutputStream은 바이트 단위로 데이터를 파일에 쓰는 기능을 제공
App.java
ㄴ FileOutputStream out0 = new FileOutputStream("member.data"); 코드는 "member.data" 파일에 대한 FileOutputStream 객체를 생성함 => 이 객체는 파일에 데이터를 출력하는 역할을 함
ㄴ DataOutputStream out = new DataOutputStream(out0); 코드는 out0에 대한 DataOutputStream 객체를 생성함
=> 이 객체는 FileOutputStream을 감싸고 있으며, 데이터를 다양한 형식으로 변환하여 out0에 출력할 수 있는 기능을 제공함
=> Member 와 동일하게 Item, Board 에도 적용해주기
App.java
ㄴ FileOutputStream out0 = new FileOutputStream("item.data"); 코드는 "item.data" 파일에 대한 FileOutputStream 객체를 생성함 => 이 객체는 파일에 데이터를 출력하는 역할을 함
ㄴ DataOutputStream out = new DataOutputStream(out0); 코드는 out0에 대한 DataOutputStream 객체를 생성함
=> 이 객체는 FileOutputStream을 감싸고 있으며, 데이터를 다양한 형식으로 변환하여 out0에 출력할 수 있는 기능을 제공함
App.java
ㄴ FileOutputStream out0 = new FileOutputStream("board.data"); 코드는 "board.data" 파일에 대한 FileOutputStream 객체를 생성함 => 이 객체는 파일에 데이터를 출력하는 역할을 함
ㄴ DataOutputStream out = new DataOutputStream(out0); 코드는 out0에 대한 DataOutputStream 객체를 생성함
=> 이 객체는 FileOutputStream을 감싸고 있으며, 데이터를 다양한 형식으로 변환하여 out0에 출력할 수 있는 기능을 제공함
App.java
ㄴ BufferedDataInputStream -> BufferedInputStream
ㄴ BufferedDataOutputStream -> BufferedOutputStream
BufferedInputStream.java
ㄴ FileInputStream -> InputStream 으로 변경
=> java 17 api 문서에서 확인 시 BufferedInputStream 은 InputStream 을 상속함
BufferedInputStream.java
ㄴ original 변수 : 기존 InputStream 객체에 대한 참조를 저장하는 데 사용
BufferedInputStream.java
ㄴ 생성자는 InputStream 객체를 매개변수로 받아 original 변수에 할당
BufferedInputStream.java
ㄴ super -> original 로 변경
BufferedInputStream.java
ㄴ read(byte[] arr) 메서드 주석처리 해두기
BufferedInputStream.java
ㄴ super -> original 로 변경
BufferedOutputStream.java
ㄴ original 변수 : 기존 InputStream 객체에 대한 참조를 저장하는 데 사용
ㄴ 생성자는 InputStream 객체를 매개변수로 받아 original 변수에 할당
BufferedOutputStream.java
ㄴ super -> original 로 변경
BufferedOutputStream.java
ㄴ super -> original 로 변경
App.java
ㄴ BufferedOutputStream 적용
ㄴBufferedOutputStream 과 DataOutputStream 모두 Decorator(장식품) 역할을 함
=> 상속과 달리 제거 및 추가가 용이함
=> saveItem(), saveBoard() 메서드에 모두 동일하게 적용해주기
=> LoadMember(), LoadItem(), LoadBoard() 메서드는 BufferedInputStream 적용해주기
Java Stream API 로 교체하기
java.io.* 패키지의 클래스 사용
ㄴ 생성했던 bitcamp.io 패키지 제거
App.java
ㄴ [command] + [shift] + O 이용하여 Java Stream API 로 교체