본문 바로가기
Web

[Spring] Observer Pattern

by DuncanKim 2022. 7. 24.
728x90

[Spring] Observer Pattern

한 없이 유약한 공격력 0의 유닛이다. 그렇지만 프로토스에게 중반 이후 클로킹을 쓰는 유닛에 대처하기 위해 필수적으로 생산해야 하는 유닛이다. 옵저버는 관찰자이다. 공격을 하지 않고 들키면 뚜드려 맞아야 하고, 가려진 맵과 클로킹 된 유닛을 보여주기만 한다.

 

그렇다면 옵저버 패턴은 무엇일까?

옵서버 패턴(observer pattern)은 객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴이다. 주로 분산 이벤트 핸들링 시스템을 구현하는 데 사용된다. 발행/구독 모델로 알려져 있기도 하다.

 

Observer Pattern도 옵저버와 비슷한 역할을 수행한다. 관찰할 객체들을 목록에 등록해놓고, 상태 변화가 있을 때마다 메서드를 통해서 알려준다. 아래의 코드들을 통해서 살펴보자.

 

 

1) 코드1. Button이 옵저버 역할을 한다.

/*
문제 : 사람 객체의 `안녕`이 `button.click();`에 의해 실행되도록 해주세요.
조건 : Button에는 `사람` 이라는 언급이 없어야 합니다.
*/
class Main {
	public static void main(String[] args) {
		Button button = new Button();
		button.setListener(new 사람());
		button.click();
	}
}
class Button {
	private Object aListener;
	public void setListener(Object aListener) {
		this.aListener = aListener;
	}
	public void click() {
		((사람)aListener).안녕();
	}
}
class 사람 {
	public void 안녕() {
		System.out.println("Main::안녕!");
	}
}

click()이라는 이벤트가 일어나면, Button 클래스 안의 click 메서드는 사람 클래스의 안녕이라는 메서드를 호출한다.

버튼 클래스 안에는 aListener라는 Object 타입의 객체 참조 변수가 존재하며, setListener를 통해 사람 클래스의 인스턴스를 매개변수로 받아서 함수를 호출하게 된다.

 

클릭이라는 이벤트가 있을 때, 사람 메서드에게 무엇인가를 알려주는 역할을 Button 클래스가 하고 있는 것이다.

 

 

2) 코드2. fireClick() 메서드를 instanceof로 분기 처리 하기

class Main {
	public static void main(String[] args) {
		Button aButton = new Button();
		aButton.setClickEventListener(new 고양이());
		aButton.fireClick();
		// 나(고양이)는 방금 버튼이 클릭되었다는 사실을 전달 받았습니다.
		
		aButton.setClickEventListener(new 개());
		aButton.fireClick();
		// 나(개)는 방금 버튼이 클릭되었다는 사실을 전달 받았습니다.
		
		aButton.setClickEventListener(new 부엉이());
		aButton.fireClick();
        // 나(부엉이)는 방금 버튼이 클릭되었다는 사실을 전달 받았습니다.
	}
}
class Button {
	private Object aListener;
	
	public void setClickEventListener(Object aListener) {
		this.aListener = aListener;
	}
	
	public void fireClick() {
		if ( aListener instanceof 고양이 ) {
			((고양이)aListener).클릭이벤트에응답();
		}
		else if ( aListener instanceof 개 ) {
			((개)aListener).응답();
		}
		else if ( aListener instanceof 부엉이 ) {
			((부엉이)aListener).onClick();
		}
	}
}
class 고양이 {
	public void 클릭이벤트에응답() {
		System.out.println("나(고양이)는 방금 버튼이 클릭되었다는 사실을 전달 받았습니다.");
	}
}

class 개 {
	public void 응답() {
		System.out.println("나(개)는 방금 버튼이 클릭되었다는 사실을 전달 받았습니다.");
	}
}

class 부엉이 {
	public void onClick() {
		System.out.println("나(부엉이)는 방금 버튼이 클릭되었다는 사실을 전달 받았습니다.");
	}
}

 

Button 클래스를 통해서 고양이, 개, 부엉이 객체들이 각각 요청에 따른 응답들을 해내는 것을 볼 수 있다.

fireClick() 메서드 안에서 instanceof를 통해서 각각의 객체가 어떤 타입인지를 분석해서 각 객체의 메서드들을 호출하고 있다.

 

 

3) 코드 3. if로 분기하지 않고 인터페이스를 통해 분기하기

class Main {
	public static void main(String[] args) {
		Button aButton = new Button();
		aButton.setClickEventListener(new 고양이());
		aButton.fireClick();
		// 나(고양이)는 방금 버튼이 클릭되었다는 사실을 전달 받았습니다.
		
		aButton.setClickEventListener(new 개());
		aButton.fireClick();
		// 나(개)는 방금 버튼이 클릭되었다는 사실을 전달 받았습니다.
		
		aButton.setClickEventListener(new 부엉이());
		// 나(부엉이)는 방금 버튼이 클릭되었다는 사실을 전달 받았습니다.
		
		aButton.fireClick();
	}
}
class Button {
	private 버튼구독자 aListener;
	
	public void setClickEventListener(버튼구독자 aListener) {
		this.aListener = aListener;
	}
	
	public void fireClick() {
		aListener.onClick();
	}
}
class 고양이 implements 버튼구독자 {
	public void 클릭이벤트에응답() {
		System.out.println("나(고양이)는 방금 버튼이 클릭되었다는 사실을 전달 받았습니다.");
	}
	
	public void onClick() {
		클릭이벤트에응답();
	}
}

class 개 implements 버튼구독자 {
	public void 응답() {
		System.out.println("나(개)는 방금 버튼이 클릭되었다는 사실을 전달 받았습니다.");
	}
	
	public void onClick() {
		응답();
	}
}

class 부엉이 implements 버튼구독자 {
	public void onClick() {
		System.out.println("나(부엉이)는 방금 버튼이 클릭되었다는 사실을 전달 받았습니다.");
	}
}

interface 버튼구독자 {
	public void onClick();
}

버튼구독자라는 인터페이스를 추가해서 고양이, 개, 부엉이 클래스에 구현을 하였다.

또한 Button 클래스 안의 필드 aListener도 인터페이스 타입으로 선언을 하였다. 이렇게 될 경우, 버튼 구독자를 구현한 클래스들을 모두 객체로 받을 수 있다는 다형성을 활용한 코드라고 할 수 있다.

 

onClick() 메서드 안에서 각각의 응답() 메서드를 호출하게 하였는데, 코드 2보다 더 객체지향적으로 설계를 했다고 할 수 있다. 

onClick() 메서드에 맞추어서 다른 클래스를 구현한다고 하면, Button 클래스의 fireClick() 메서드를 코드 2처럼 구현할 이유가 전혀 없어지게 된다. Button의 fireClick() 메서드는 그대로 두고, 추가하고 싶은 클래스가 있다면 버튼 구독자 인터페이스를 구현하도록 해서 onClick()을 직접 구현하도록 해서 유지보수를 할 수 있는 이점이 있는 것이다.

 

 

 

728x90

'Web' 카테고리의 다른 글

[Spring] MVC Pattern  (0) 2022.07.26
[Web] Web Request, Response(웹의 요청과 응답)  (0) 2022.07.25
[Spring] Factory-Method Pattern  (0) 2022.07.23
[Spring] Singleton Pattern  (0) 2022.07.22
[Java] equals()와 ==의 차이점  (0) 2022.07.19

댓글