Chain of responsibility 패턴

위키백과, 우리 모두의 백과사전.
이동: 둘러보기, 검색

객체 지향 디자인에서 chain-of-responsibility pattern은 명령 오브젝트와 일련의 프로세스 오브젝트를 포함하는 디자인 패턴이다. 각각의 프로세스 오브젝트는 명령 오브젝트를 핸들할 수 있는 연산의 집합으로 이루어지고, 체인안의 프로세스 오브젝트가 핸들할 수 없는 행해진다. 이 작동방식은 새로운 프로세스 오브젝트에서 체인의 끝까지에도 포함된다.

표준 chain-of-responsibility model이 변화하면서, 몇몇 핸들러들은 다양한 방향으로 명령을 보내 책임을 트리 형태로 바꾸는 관제사 역할을 하기도 한다. 몇몇 경우에서는, 프로세스 오브젝트가 상위의 프로세스 오브젝트와 명령을 호출하여 작은 파트의 문제를 해결하기 위해 재귀적으로 실행된다. 이 경우 재귀는 명령이 처리되거나 모든 트리가 탐색될때까지 진행되게 된다. XML(파싱되었으나 실행되지 않은)인터프리터가 알맞은 예가 된다.

이 패턴은 결합을 느슨하게 하기 위해 고안되었으며 가장 좋은 프로그래밍 사례로 꼽힌다.


[편집]

아래의 자바코드는 로그를 남겨주는 클래스를 보여준다. 각각의 로그 핸들러들은 로그 레벨과 다음 로그 핸들러로 넘겨주는 기능을 가지고 있다.

  Writing to stdout:   Entering function y.
  Writing to stdout:   Step1 completed.
  Sending via e-mail:  Step1 completed.
  Writing to stdout:   An error has occurred.
  Sending via e-mail:  An error has occurred.
  Writing to stderr:   An error has occurred.

이 예가 실제로 어떻게 로그 클래스를 남겨야 하는지에 대한 추천은 아니라는 것을 명심하라.

뿐만 아니라, 완벽한 chain of responsibility pattern을 구현하기 위해서 로거는 메시지를 넘긴 이후 하위 체인으로 메시지를 핸들링할 수 없다. 이 예에서 메시지는 핸들링이 되는 여부와 관계없이 하위 체인으로 전달된다.


abstract class Logger {
    public static int ERR = 3;
    public static int NOTICE = 5;
    public static int DEBUG = 7;
    protected int mask;
 
    // The next element in the chain of responsibility
    protected Logger next;
 
    public Logger setNext(Logger log) {
        next = log;
        return log;
    }
 
    public void message(String msg, int priority) {
        if (priority <= mask) {
            writeMessage(msg);
        }
        if (next != null) {
            next.message(msg, priority);
        }
    }
 
    abstract protected void writeMessage(String msg);
}
 
class StdoutLogger extends Logger {
    public StdoutLogger(int mask) { 
        this.mask = mask;
    }
 
    protected void writeMessage(String msg) {
        System.out.println("Writing to stdout: " + msg);
    }
}
 
 
class EmailLogger extends Logger {
    public EmailLogger(int mask) {
        this.mask = mask;
    }
 
    protected void writeMessage(String msg) {
        System.out.println("Sending via email: " + msg);
    }
}
 
class StderrLogger extends Logger {
    public StderrLogger(int mask) {
        this.mask = mask;
    }
 
    protected void writeMessage(String msg) {
        System.err.println("Sending to stderr: " + msg);
    }
}
 
public class ChainOfResponsibilityExample {
    public static void main(String[] args) {
        // Build the chain of responsibility
        Logger logger, logger1;
        logger1 = logger = new StdoutLogger(Logger.DEBUG);
        logger1 = logger1.setNext(new EmailLogger(Logger.NOTICE));
        logger1 = logger1.setNext(new StderrLogger(Logger.ERR));
 
        // Handled by StdoutLogger
        logger.message("Entering function y.", Logger.DEBUG);
 
        // Handled by StdoutLogger and EmailLogger
        logger.message("Step1 completed.", Logger.NOTICE);
 
        // Handled by all three loggers
        logger.message("An error has occurred.", Logger.ERR);
    }
}


아래는 이 패턴을 Java로 구현한 다른 예제이다. 이 예에서 각각의 성공자와 제한을 각각 다르게 가지고 있다. 매번 역할에 참가하고 있는 사용자는 요청을 수신받고, 상한선과 성공자를 통과한 요청을 보내준다.

The PurchasePower abstract class with the abstract method processRequest.

abstract class PurchasePower {
    protected final double base = 500;
    protected PurchasePower successor;
 
    public void setSuccessor(PurchasePower successor) {
        this.successor = successor;
    }
 
    abstract public void processRequest(PurchaseRequest request);
}


Four implementations of the abstract class above: Manager, Director, Vice President, President

class ManagerPower extends PurchasePower {
    private final double ALLOWABLE = 10 * base;
 
    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() < ALLOWABLE) {
            System.out.println("Manager will approve $" + request.getAmount());
        } else if (successor != null) {
            successor.processRequest(request);
        }
    }
}
 
class DirectorPPower extends PurchasePower {
    private final double ALLOWABLE = 20 * base;
 
    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() < ALLOWABLE) {
            System.out.println("Director will approve $" + request.getAmount());
        } else if (successor != null) {
            successor.processRequest(request);
        }
    }
}
 
class VicePresidentPPower extends PurchasePower {
    private final double ALLOWABLE = 40 * base;
 
    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() < ALLOWABLE) {
            System.out.println("Vice President will approve $" + request.getAmount());
        } else if (successor != null) {
            successor.processRequest(request);
        }
    }
}
 
class PresidentPPower extends PurchasePower {
    private final double ALLOWABLE = 60 * base;
 
    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() < ALLOWABLE) {
            System.out.println("President will approve $" + request.getAmount());
        } else {
            System.out.println( "Your request for $" + request.getAmount() + " needs a board meeting!");
        }
    }
}


The PurchaseRequest class with its Getter methods which keeps the request data in this example.

class PurchaseRequest {
    private int number;
    private double amount;
    private String purpose;
 
    public PurchaseRequest(int number, double amount, String purpose) {
        this.number = number;
        this.amount = amount;
        this.purpose = purpose;
    }
 
    public double getAmount() {
        return amount;
    }
    public void setAmount(double amt)  {
        amount = amt;
    }
 
    public String getPurpose() {
        return purpose;
    }
    public void setPurpose(String reason) {
        purpose = reason;
    }
 
    public int getNumber(){
        return number;
    }
    public void setNumber(int num) {
        number = num;
    }   
}


And here a usage example, the successors are set like this: Manager -> Director -> Vice President -> President

class CheckAuthority {
    public static void main(String[] args) {
        ManagerPPower manager = new ManagerPPower();
        DirectorPPower director = new DirectorPPower();
        VicePresidentPPower vp = new VicePresidentPPower();
        PresidentPPower president = new PresidentPPower();
        manager.setSuccessor(director);
        director.setSuccessor(vp);
        vp.setSuccessor(president);
 
        // Press Ctrl+C to end.
        try {
            while (true) {
                System.out.println("Enter the amount to check who should approve your expenditure.");
                System.out.print(">");
                double d = Double.parseDouble(new BufferedReader(new InputStreamReader(System.in)).readLine());
                manager.processRequest(new PurchaseRequest(0, d, "General"));
           }
        } catch(Exception e) {
            System.exit(1);
        }  
    }
}

바깥 고리[편집]