2018년 8월 19일 일요일

[java] 변경 가능성이 다분한 부분을 다루자 -01 (비지터패턴이용하기)

출처 : 하스켈로 배우는 함수형 프로그래밍

비지터 패턴으로 변경 가능성이 높은 부분을 개발해보자.

파싱트리를 만드는 것에 대해서 배운 적은 없다. 제대로 만들어 본 적이 없기 때문에 한 번쯤은 제대로 배워보고 싶은 생각이 드는데 어디서 부터 공부를 해야 할지 아직도 잘 모르겠다.
일단 우리는 산술식을 다양한 방법으로 표현할 것이다. 이 식을 표현식이라고 부르자.
이 표현식으로 쪼개고 쪼개서 토큰화 시키고 그 수식들을 그대로 문자열로 보이도록 하거나, 계산을 해서 답을 내도록 해보자.
표현식은 BNF로 표현했다고 하자.


비지터패턴으로 만들 것이다.

1. 비지터 인터페이스 생성.

interface Visitor {
    R plus(Expr e1, Expr e2); // 덧셈 식을 위한 메소드
    R square(Expr e); // 제곱 식을 위한 메소드
    R number(N n); // 숫자를 위한 메소드
}

2. 표현식 인터페이스 생성 및 구현


interface Expr {
     R accept(Visitor v);

class Plus implements Expr {
    Expr e1;
    Expr e2;
    public Plus(Expr e1, Expr e2) {
        this.e1 = e1;
        this.e2 = e2;
    }
    @Override public  R accept(Visitor v) { return v.plus(e1, e2); }
}

class Square implements Expr {
    Expr e;
    public Square(Expr e) { this.e = e; }
    @Override  public  R accept(Visitor v) { return v.square(e); }
}

class Number implements Expr {
    N n;
    public Number(N n) { this.n = n; }
    @Override  public  R accept(Visitor v) { return v.number(n); }
}

3. 비지터 패턴을 구현


// 식의 평가를 실시하는 Visitor
class Eval implements Visitor {
    @Override
    public Integer plus(Expr e1, Expr e2) {
        return e1.accept(this) + e2.accept(this);
    }

    @Override
    public Integer square(Expr e) {
        Integer x = e.accept(this);
        return x;
    }

    @Override
    public Integer number(Integer n) {
        return n;
    }
}

// 식을 문자열로 하는 Visitor
class Show implements Visitor {
    @Override
    public String plus(Expr e1, Expr e2) {
        return e1.accept(this) + " + " + e2.accept(this);
    }

    @Override
    public String square(Expr e) {
        return "(" + e.accept(this) + ")^2";
    }

    @Override
    public String number(Integer n) {
        return n + "";
    }
}

이제 실행해보자.
public class TestVisitor {
    public static void main(String[] args) {
        //  e = 1 + (2 + 3) ^ 2
        // 실제로는 구문 해석 등에 의해서 좀 더 크고 복잡한 것을 가정
        Expr e = new Plus(new Number(1), new Square(new Plus(new Number(2)
                                                                ,new Number(3))));
        System.out.println(e.accept(new Show()));
        System.out.println(e.accept(new Eval()));
    }
}

이 프로그램과 설계에는 문제가 없음.
변경에 대한 유연성에 대해서도 다음과 같은 경우에 따라 각각 가산해서 대응할 수 있으므로 Visitor 패턴을 사용하는 것은
객체지향 프레임워크로서는 더할 나위 없는 방법이라고 말할 수 있다.
- 식의 증가에 대해 (뺄셈의 추가)
a. Visitor의 메소드를 늘린다.
b. 식의 인터페이스를 계승하는 클래스를 늘린다.
- 식에 대한 처리 종류의 증가에 대해 (JSON으로 변경)
a. Visitor를 계승하는 클래스를 늘린다.

이런식으로 계속 계산식 혹은 처리방식을 추가할 때, 객체지향적인 방법으로 추가를 할 수 있게 된다.

댓글 없음:

댓글 쓰기