Composite Pattern 과 Decorator Pattern
폴더와 파일을 다이어그램으로 표현해보자
폴더와 파일의 공통점으로 FileComponent로 뽑아낸다. 여기서 핵심은 폴더는 파일 컴포넌트를 가진다. 즉, 폴더는 파일 컴포넌트를 구현하는 폴더나 파일을 가질 수 있다는 소리이다. (일체화). 이런 형식을 Composite Pattern이라고 한다.
이 내용을 아래 코드로 작성할 수 있다.
package theory.composite;
public abstract class Node {
private String name; // 폴더와 파일의 이름
public Node(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract long getSize();
public abstract boolean isFolder();
}
package theory.composite;
import java.util.ArrayList;
import java.util.List;
public class Folder extends Node {
private List<Node> nodes; // 폴더는 노드를 여러 개 가질 수 있다.
public Folder(String name) {
super(name);
nodes = new ArrayList<>();
}
public void add(File file) {
nodes.add(file);
}
public void add(Folder folder) {
nodes.add(folder);
}
@Override
public long getSize() {
long total = 0L;
for (int i = 0; i < nodes.size(); i++) {
total += nodes.get(i).getSize(); // 폴더면 폴더의 getSize. 즉, 현재 메서드 반복.
}
return total;
}
@Override
public boolean isFolder() {
return true;
}
}
package theory.composite;
public class File extends Node {
private long size;
public File(String name, long size) {
super(name);
this.size = size;
}
@Override
public long getSize() {
return this.size;
}
@Override
public boolean isFolder() {
return false;
}
}
이런 식으로 폴더와 파일을 노드라는 부모 클래스를 둠으로써 파일과 폴더를 노드 취급하는 패턴을 Composite Pattern이라고 한다. (일체화)
Decorator Pattern
위 패턴을 보면 위에 있는 3가지 패턴을 보면 Composite Pattern과 똑같다. 이때, Decorator는 Component를 가질 수 있고, 이 말은 즉 Component를 상속받고 있는 것들을 가질 수 있다는 말이다.
위 그림을 코드로 구현하면 아래와 같다. 우선 추상클래스 Shape와 이를 상속받는 Circle, Rectangle 클래스가 있다.
package theory.decorator;
public abstract class Shape {
public abstract void draw();
}
package theory.decorator;
public class Circle extends Shape{
@Override
public void draw() {
System.out.println("Shape: Circle");
}
}
package theory.decorator;
public class Rectangle extends Shape{
@Override
public void draw() {
System.out.println("Shape: Rectangle");
}
}
그리고 Shape를 상속받는 장식용 추상 클래스 ShapeDecorator가 있고, 이는 필드로 Shape를 상속받고 있는 모든 것들을 가질 수 있다. (Circle, ShapeDecorator, RedShapeDecorator). 즉, 내가 장식할 대상을 생성자로 받아들여 필드를 초기화하는 것을 알 수 있다. 하지만 추상 클래스 이기 때문에, 인스턴스를 생성할 수는 없다.
package theory.decorator;
public abstract class ShapeDecorator extends Shape{
protected Shape decoratedShape;
public ShapeDecorator(Shape decoratedShape) {
this.decoratedShape = decoratedShape;
}
@Override
public void draw() {
decoratedShape.draw();
}
}
그리고 이를 상속받는 RedShapeDecorator를 보면 생성자에서 내가 장식할 대상인 Shape를 받아들여서 super 생성자에 넘겨준다.
package theory.decorator;
public class RedShapeDecorator extends ShapeDecorator{
public RedShapeDecorator(Shape decoratedShape) {
super(decoratedShape);
}
@Override
public void draw() {
System.out.println("RED ================ START");
decoratedShape.draw();
System.out.println("RED ================ END");
}
}
그리고 이를 사용할 때, 장식할 대상인 Circle을 받아들여 장식해주는 RedShapeDecorator를 코드로 쓰면 다음과 같다.
Circle circle = new Circle();
RedShapeDecorator redShapeDecorator = new RedShapeDecorator(circle);
redShapeDecorator.draw();
그 결과는 다음과 같다.
RED ================ START
Shape: Circle
RED ================ END
이 것을 또 GreenShapeDecorator로 감싸주면 다음과 같은 코드로 할 수 있고,
Circle circle = new Circle();
RedShapeDecorator redShapeDecorator = new RedShapeDecorator(circle);
GreenShapeDecorator greenShapeDecorator = new GreenShapeDecorator(redShapeDecorator);
greenShapeDecorator.draw();
그 결과는 다음과 같다.
GREEN **************** START
RED ================ START
Shape: Circle
RED ================ END
GREEN **************** END
이 것을 같은 코드인데 다음과 같이 작성할 수 있다.
Shape shape = new GreenShapeDecorator(new RedShapeDecorator(new Circle()));
shape.draw();
그러면 아까와 같은 결과를 확인할 수 있다.
GREEN **************** START
RED ================ START
Shape: Circle
RED ================ END
GREEN **************** END
그렇다면 다음 코드를 보자.
InputStream in = new DataInputStream(new FileInputStream("a.txt"));
위 코드에서 볼 수 있듯이 자바 IO에서는 Shape 역할을 하는 것이 InputStream이다.(추상 클래스)
그리고 Circle 역할을 수행하는 것은 FileInputStream이고, RedShapeDecorator 역할을 수행하는 것이 DataInputStream이다.
이 시점에서 아래 그림을 기점으로 Decorator Pattern을 다시한번 정리하면, Shape를 가지는 ShapeDecorator, RedShapeDecorator는 장식 역할을 하는 것이고, Shape을 가지지 않고 상속하는 Circle, Rectangle은 주인공 역할을 하는 것이다. 즉, 장식할 대상이 되는 것이다.
자바 IO는 Decorator으로 만들어졌다. 따라서 이를 잘 사용하려면 주인공과 장식을 구별할 수 있어야 하는 것이다.
다음 자바 IO의 클래스 상속도를 보면 Shape 역할을 하는 추상 클래스가 InputStream, OutputStream, Reader, Writer인 것을 알 수 있다. 그리고 이를 상속 받는 클래스들 중에서 주인공, 장식 역할을 하는 클래스가 있다는 것이다.
또한 장식만을 가지고는 읽고 쓸 수가 없다. 주인공 역할을 수행하는 클래스가 있어야 입출력이 가능하다. 즉, 주인공은 어디로부터 읽어들일지, 어디로 쓸지를 결정한다.
키보드로부터 한줄씩 입력받아 출력하는 예제를 보자. BufferedReader의 readLine()을 이용하면 한줄씩 입력받을 수 있고, 이때 BufferedReader는 장식 역할을 수행한다. 따라서 파라미터로 Reader를 전달해줘야 한다.
그런데 이때, 키보드로부터 한줄씩 입력받아야 하는데, 키보드를 나타내는 것은 System.in 이다. (주인공 역할) -> InputStream
따라서 new InputStreamReader()를 사용하면 파라미터로 InputStream을 받아들이므로, System.in이 InputStream이니까 이를 받아들이면 된다.
그러면 주인공인 System.in을 감싸는 Reader를 상속받는 InputStreamReader를 BufferedReader에 넣어줌으로써 이를 사용할 수 있게 되는 것이다. 그래서 다음과 같은 코드가 나올 수 있는 것이다.
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = br.readLine();
System.out.println("line = " + line);
br.close();
인용
https://youtu.be/Dc8MiWPRHX4?si=ZLPNDxyJLVlZXzX6
'Language > Java' 카테고리의 다른 글
Context Switching, Thread (0) | 2023.09.20 |
---|---|
I/O - 다양한 IO객체들, 객체 직렬화 (0) | 2023.09.18 |
Java I/O - IO Stream (0) | 2023.09.18 |
I/O - File 클래스 (0) | 2023.09.18 |
I/O (Input, Output) (0) | 2023.09.17 |