7 분 소요

Composite pattern

파일과 폴더의 관계
  • 파일과 폴더의 관계는 composite pattern으로 표현된다.
  • 위의 두가지 방법 모두 파일과 폴더의 관계를 나타내는 방법이다.
  • 두번째 도식에서는 공통점을 뽑아내 인터페이스로 표현하였다.
  • 인터페이스 대신 추상클래스도 가능하다.
  • 폴더는 인터페이스를 가지고 있고, 이는 인터페이스(혹은 추상 클래스)의 (구체 클래스)자식들이 폴더에 포함될 수 있다는 의미이다.
Composite pattern

출처: https://medium.com/@achillesmoraites/design-pattern-composite-a77e570e9105

  • Java IO의 decorator pattern은 composite pattern과 유사하다.
  • Folder는 FileComponent를 가진다.
  • FileComponent는 인터페이스나 추상클래스이므로 FileComponent는 인스턴스가 될 수 없다.
  • 이를 구현하거나 상속받는 파일이나 폴더가 인스턴스가 될 수 있다. 이 구현하거나 상속받은 파일이나 폴더를 Folder가 가지는 것이다.

예제

public abstract class Node {
    private String name;

    public Node(String name) {
        tihs.name = name;
    }

    public String getName() { return name; }

    public void setName(String name) { this.name = name; }

    public abstract long getSize();
    public abstract boolean isFolder();
}
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; }
}
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 = total + nodes.get(i).getSize();
        }
    }
}
  • Composite Pattern
  • 공통 요소인 Node를 추상클래스/인터페이스로 둠으로써 File과 Folder를 Node로 취급하여 관리할 수 있다.
public class CompositePatternDemo {
    public static void main(String[] args) {
        File f1 = new File("file1", 10L);
        File f2 = new File("file2", 20L);
        File f3 = new File("file3", 30L);

        Folder folder1 = new Folder("folder1");
        Folder folder2 = new Folder("folder2");
        folder1.add(f1);
        folder1.add(folder2);
        folder2.add(f2);
        folder2.add(f3);j

        System.out.println(folder1.getSize()); // 60
    }
}

Decorator pattern

Decorator 패턴
  • 이제 다시 Java IO의 decorator pattern을 살펴보자.
  • Decorator pattern에서 윗부분은 앞서 살펴 본 decorator pattern과 동일하다.

예제

Decorator 패턴
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();
}
public abstract class Shape {
    public abstract void draw();
}
public class Circle extends Shape {
    @Override
    public void draw() { System.out.println("Shape: Circle"); }
}
public class Rectangle extends Shape {
    @Override
    public void draw() { System.out.println("Shape: Rectangle"); }
}
public class ShpaeDecorator extends Shape {
    protected Shape decoreatedShape; // Shape 를 가질 수 있음

    public ShapeDecorator(Shape decoratedShape) { this.decoratedShape = decoratedShape; }

    public void draw() { decoratedShape.draw(); }
}
public class RedShapeDecorator extends ShapeDecorator {
    public RedShapeDecorator(Shape decoratedShape) {
        super(decoratedShape);
    }

    @Override
    public void draw() {
        decoratedShape.draw();
        setRedBorder(decoratedShape);
    }

    private void setRedBorder(Shape decoratedShape) {
        System.out.println("Red ================= Start");
        decoratedShape.draw();
        System.out.println("Red ================= End");
    }
}
public class RedShapeDecorator extends ShapeDecorator {
    public RedShapeDecorator(Shape decoratedShape) {
        super(decoratedShape);
    }

    @Override
    public void draw() {
        decoratedShape.draw();
        setGreenBorder(decoratedShape);
    }

    private void setGreenBorder(Shape decoratedShape) {
        System.out.println("Green ***************** Start");
        decoratedShape.draw();
        System.out.println("Green ***************** End");
    }
}
public class DecoratorPatternDemo {
    public static void main(String[] args) {
        Circle circle = new Circle();

        RedShapeDecorator redShapeDecorator = new RedShapeDecorator(circle);
        redShapeDecorator.draw();
        
        GreenShapeDecorator greenShapeDecorator = new GreenShapeDecorator(redShapeDecorator);
        greenShapeDecorator.draw();

        Shape shape = new GreenShapeDecorator(new RedShapeDecorator(new Rectangular()));
        shape.draw();

        // Java IO에 적용해보자.
        InputStream in = new DataInputStream(new FileInputStream("a.txt"));
    }
}
  • Shape => InputStream (추상 클래스)
  • Rectangle => FileInputStream
  • RedShapeDecorator => DataInputStream
  • 재강조: 주인공과 장식을 구분할 수 있어야 한다.
  • 따라서 Java IO API를 읽어보아야 한다.
  • 연습을 통해 익숙해진다.

예제

BufferedReader

키보드로부터 한 줄 씩 읽는 프로그램

public class IOExam01 {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String line = br.readLine();
        System.out.println(line);
        br.close();
    }
}
  • 키보드로부터 한 줄 씩 입력받는다.
  • BufferedReader의 readLine()을 이용하여 한 줄 씩 입력받을 수 있다.
  • BufferedReader는 장식 역할: Reader를 인자로 받는다.
  • 키보드: System.in: 주인공 역할, InputStream

DataInputStream, DataOutputStream

  • 문제, 이름, 국어/영어/수학 점수를 파일에 저장하시오.
  • 파일은 tmp/score.dat
public class IOExam02 {
    public static void main(String[] args) throws Exception {
        String name = "kim";
        int kor = 90;
        int eng = 50;
        int math = 70;
        double total = kor + eng + math;
        double avg = total / 3.0;

        DataOutputStream out = new DataOutputStream(new FileOutputStream("tmp/score.dat"));
        out.writeUTF(name);
        out.writeInt(kor);
        out.writeInt(eng);
        out.writeInt(math);
        out.writeDouble(total);
        out.writeDouble(avg);
        out.close();
    }
}
  • 파일에서 다양한 정보 읽어오기
public class IOExam03 {
    public static void main(String[] args) throws Exception {
        DataInputStream in = new DataInputStream(new FileInputStream("tmp/score.dat"));
        String name = in.readUTF();
        int kor = in.readInt();
        int eng = in.readInt();
        int math = in.readInt();
        double total = in.readDouble();
        double avg = in.readDouble();
        in.close();

        System.out.println(name);
        System.out.println(kor);
        System.out.println(eng);
        System.out.println(math);
        System.out.println(total);
        System.out.println(avg);
    }
}
  • 순서대로 읽어온다!
  • 메서드 추출을 통해 in 으로 정해진 파일 포멧을 읽어오도록 만들 수 있다.
public class IOExam03 {
    public static void main(String[] args) throws Exception {
        DataInputStream in = new DataInputStream(new FileInputStream("tmp/score.dat"));

        printStudent(in);
        printStudent(in);

        in.close();
    }

    private static void printStudent(DataInpuStream in) throws IOException {
        String name = in.readUTF();
        int kor = in.readInt();
        int eng = in.readInt();
        int math = in.readInt();
        double total = in.readDouble();
        double avg = in.readDouble();

        System.out.println(name);
        System.out.println(kor);
        System.out.println(eng);
        System.out.println(math);
        System.out.println(total);
        System.out.println(avg);
    }
}

ByteArrayInputStream, ByteArrayOutputStream

byte[]에 데이터 읽고 쓰기

public class IOExam04 {
    public static void main(String[] args) throws Exception {
        int data1 = 1;
        int data2 = 2;

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        out.write(data1); // data1의 마지막 한 바이트만 저장한다.
        out.write(data2);
        out.close();

        byte[] array = out.toByteArray();
        System.out.println(array.length);
        System.out.println(array[0]);
        System.out.println(array[1]);
    }
}
  • ByteArrayOutputStream이 내부적으로 메모리를 가지고 있음.
  • write후 close를 하게 되면 ByteArrayOutputStream에 데이터가 차곡차곡 저장된다.
  • toByteArray(): 저장한 바이트를 가져올 수 있다.
public class IOExam05 {
    public static void main(String[] args) throws Exception {
        byte[] array = new byte[2];
        array[0] = (byte)1;
        array[1] = (byte)2;

        ByteArrayInputStream in = new ByteArrayInputStream(array); // byte array 를 받는다.
        int read1 = in.read(); // 1바이트를 읽어 리턴
        int read2 = in.read();
        int read3 = in.read(); // -1

        in.close();

        System.out.prinlnt(read1);
        System.out.prinlnt(read2);
        System.out.prinlnt(read3);
    }
}
  • 실제 사용해보기 전까지는 활용하기는 어려울 수 있음.
  • 그러나 알아두면 필요할 때 사용할 수 있을 것.

CharArrayReader, CharArrayWriter

char[]에 데이터 읽고 쓰기

StringReader, StringWriter

문자열 읽고 쓰기

public class IOExam06 {
    public static void main(String[] args) throws Exception {
    StringWriter out = new StringWriter();
    out.write("hello");
    out.wirte("world");
    out.write("!!!");
    out.close();

    String str = out.toString();
    System.out.println(str);
    }
}

앞선 예시처럼 생성자에 아무것도 없는 경우는 메모리에 쓰는 것으로 이해하면 될 것 같다.

public class IOExam07 {
    public static void main(String[] args) throws Exception {
    StringReader in = new StringReader("HelloWorld!!!");

    int ch = -1;
    while((ch = in.read()) != -1) {
        System.out.println((char)ch); // <-
    }
    in.close();

    }
}

마지막 두 바이트만 글자일 것이므로 형변환 해주어야 한다.

댓글남기기