Java/Design Pattern

[Design Pattern] Chain of Responsibility Pattern

예은파파 2020. 11. 23. 23:05

책임 연쇄 패턴( Chain of Responsibility Pattern )

  • 명령 객체와 일련의 처리 객체를 포함하는 패턴
  • 각각의 처리 객체는 명령 객체를 처리할 수 있는 연산의 집합이며 체인 안의 처리 객체가 핸들링 할 수 없는 명령은 다음 처리 객체로 넘겨진다.
  • 체인에 들어가는 객체를 바꾸거나 순서를 바꿈으로서 역할을 동적으로 추가/제거 및 상황에 따라 동적으로 핸들러를 추가하거나 제거 할 수 있으며 이러한 변화가 전체 구조에 아무런 영향을 주지 않도록 클래스들 간의 낮은 결합도로 구현되어있다.
  • 명령에 대한 처리가 반드시 수행된다는 보장이 안되므로 반드시 명령이 처리 될 수 있도록 적절한 체인의 순서 구현 필요

Chain of Responsibility 클래스 구조

 

※ Sample Code

public abstract class Middleware {

    private Middleware next; 
	
    /**
     * Builds chains of middleware objects.
     */
    public Middleware linkWith(Middleware next) {
    	
        this.next = next;
        return next;
    }

    /**
     * Subclasses will implement this method with concrete checks.
     */
    public abstract boolean check(String email, String password);

    /**
     * Runs check on the next object in chain or ends traversing if we're in
     * last object in chain.
     */
    protected boolean checkNext(String email, String password) {
    	
        if (next == null) {
            return true;
        }
        
        return next.check(email, password);
    }
}

▲ 추상클래스 선언 ( java 8 이상부터는 interface의 default method로 구현해도 될 듯 하다. )

public class ThrottlingMiddleware extends Middleware {

    private int requestPerMinute;
    private int request;
    private long currentTime;

    public ThrottlingMiddleware( int requestPerMinute ) {
    	
        this.requestPerMinute = requestPerMinute;
        this.currentTime = System.currentTimeMillis();
    }

    /**
     * Please, not that checkNext() call can be inserted both in the beginning
     * of this method and in the end.
     *
     * This gives much more flexibility than a simple loop over all middleware
     * objects. For instance, an element of a chain can change the order of
     * checks by running its check after all other checks.
     */
    @Override
    public boolean check(String email, String password) {
    	
        if (System.currentTimeMillis() > currentTime + 60_000) {
        	
            request = 0;
            currentTime = System.currentTimeMillis();
        }

        request++;
        
        if (request > requestPerMinute) {
        	
            System.out.println("Request limit exceeded!");
            Thread.currentThread().stop();
        }
        
        return checkNext(email, password);
    }
}

 

public class UserExistsMiddleware extends Middleware {

    private Server server;

    public UserExistsMiddleware(Server server) {
    	
        this.server = server;
    }

    @Override
    public boolean check(String email, String password) {
    	
        if (!server.hasEmail(email)) {
            System.out.println("This email is not registered!");
            return false;
        }
        
        if (!server.isValidPassword(email, password)) {
            System.out.println("Wrong password!");
            return false;
        }
        
        return checkNext(email, password);
    }
}

▲ 사용자가 입력한 email / passowrd의 인증 프로세스

public class RoleCheckMiddleware extends Middleware {

    @Override
    public boolean check(String email, String password) {
		
        if (email.equals("admin@example.com")) {
            System.out.println("Hello, admin!");
            return true;
        }
        
        System.out.println("Hello, user!");
        return checkNext(email, password);
    }
}

▲ 사용자 인증이 끝난 뒤 역할 확인 프로세스

public class Server {

    private Map<String, String> users = new HashMap<>();
    private Middleware middleware;

    /**
     * Client passes a chain of object to server. This improves flexibility and
     * makes testing the server class easier.
     */
    public void setMiddleware(Middleware middleware) {
    
        this.middleware = middleware;
    }

    /**
     * Server gets email and password from client and sends the authorization
     * request to the chain.
     */
    public boolean logIn(String email, String password) {
    
        if (middleware.check(email, password)) {
            System.out.println("Authorization have been successful!");

            // Do something useful here for authorized users.

            return true;
        }
        return false;
    }

    public void register(String email, String password) {
    
        users.put(email, password);
    }

    public boolean hasEmail(String email) {
        return users.containsKey(email);
    }

    public boolean isValidPassword(String email, String password) {
        return users.get(email).equals(password);
    }
}

▲ 사용자의 로그인 정보를 가지고 있는 클래스

public class Client {

	private static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
	private static Server server;

	private static void init() {
		
		server = new Server();
		server.register("admin@example.com", "admin_pass");
		server.register("user@example.com", "user_pass");

		// All checks are linked. Client can build various chains using the same
		// components.
		Middleware middleware = new ThrottlingMiddleware(2);
		middleware.linkWith(new UserExistsMiddleware(server)).linkWith(new RoleCheckMiddleware());

		// Server gets a chain from client code.
		server.setMiddleware(middleware);
	}

	public static void main(String[] args) throws IOException {
		
		init();

		boolean success;
		
		do {
			
			System.out.print("Enter email: ");
			String email = reader.readLine();
			System.out.print("Input password: ");
			String password = reader.readLine();
			success = server.logIn(email, password);
			
		} while (!success);
	}
}

 

▶ 실행 결과

'Java > Design Pattern' 카테고리의 다른 글

[Design Pattern] Observer Pattern  (0) 2020.10.02
[Design Pattern] Command Pattern  (0) 2020.09.26