[java] 싱글톤 패턴과 옵저버 패턴

변준영·2022년 11월 16일
0

1. 싱글톤 패턴(Singleton pattern)

1) 싱글톤 패턴이란?

  • 클래스의 인스턴스화를 단일 인스턴스로 제한하는 소프트웨어 설계 패턴
    -> 생성자가 여러 차례 호출되더라도 실제로 생성되는 객체는 하나이고, 최초 생성 이후에 호출된 생성자는 최초의 생성자가 생성한 객체를 리턴하는 디자인 유형
  • 객체지향 소프트웨어에서 반복적인 문제를 해결하는 방법을 설명하는 Gang of Four 설계 패턴 중 하나
  • 시스템 전체에서 동작을 조정하기 위해 정확히 하나의 객체가 필요할 때 유용
  • 싱글톤 패턴은 Global namespace를 오염시키지 않아 전역변수(global variable)보다 선호함
  • 추상 팩토리, 팩토리 메소드, 빌더, 프로토타입 패턴과 같은 다른 설계 패턴의 기초로 사용 됨

2) 싱글톤 패턴 구현

  1. 클래스의 모든 생성자를 private로 선언
    -> 다른 객체에 의해 인스턴스화 되지 않도록 하기 위함
  2. 인스턴스를 참조하는 Static 메소드를 사용
  3. 인스턴스는 일반적으로 private static 변수로 저장되며, static 메소드가 처음 호출되기 전에 변수가 초기화 될때 인스턴스 생성
public class Coin {
    private static final int ADD_MORE_COIN = 10;
    private int coin;
    private static Coin instance = new Coin(); // 3. private static 변수로 저장

    private Coin() { // 1. 생성자를 private 로 선언
      
    }

    public static Coin getInstance() { // 2. 참조할 수 있도록 static 메소드 사용
        return instance;
    }

 	// 이하 Coin 기능
    public int getCoin() {
        return coin;
    }

    public void addMoreCoin() {
        coin += ADD_MORE_COIN;
    }

    public void deductCoin() {
        coin--;
    }
}

2. 옵저버 패턴(Observer pattern)

1) 옵저버 패턴이란?

  • 소프트웨어 설계 및 엔지니어링에서 observer 패턴은 'subject'라는 객체가 observer라고 불리는 '종속 된 것들의 목록'(list of its dependents)을 유지 및 관리하고, 메서드 중 하나를 호출하여 상태 변화를 자동으로 통지하는 소프트웨어 설계 패턴[3]
    -> 객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴[2]
  • 분산 이벤트 핸들링 시스템을 구현하는 데 사용

2) 옵저버 패턴 구현

[2]
1. 옵저버 또는 리스너라 불리는 하나 이상의 객체를 관찰 대상이 되는 객체(Subject)에 등록
2. 각각의 옵저버들은 관찰 대상인 객체가 발생시키는 이벤트를 받아 처리
-> 관찰 대상인 객체는 "이벤트를 발생시키는 주체"라는 의미에서 Subject로 표시
3. 이벤트 발생 시 각 옵저버는 callback 받음
-> notify()는 관찰 대상이 발행한 메시지 이외에 옵저버 자신이 생성한 인자 값을 전달 할 수도 있음.
4. Sunject에는 일반적으로 등록(register), 제거(unregister) 메소드가 있는데, 전자는 새로운 옵저버를 목록에 등록하고 후자는 목록에서 옵저버를 제거
-> 이외에도, 임시로 작동을 멈추거나 재개하는 메서드를 이용해 이벤트가 계속해서 있을 때 홍수같이 발생하는 요청을 제어 가능
5. 옵저버 패턴이 많이 쓰인 시스템에서는 순환 실행을 막는 메카니즘이 필요

  • EX) 이벤트 X가 발생하면 옵저버A가 옵저버B를 갱신
    -> 옵저버B가 이 처리를 위해 옵저버A를 갱신
    -> 이는 다시 A로 하여금 이벤트 X를 발생
    => 이같은 상황을 막기 위해 이벤트 X가 한번 처리된 후에는 A가 이벤트 X를 다시 발생시키지 않는 방법이 요구됨

예시

[4]

  • Student들은 Teacher수업을 듣는다.
    - 등록된 학생들만 수업을 들을 수 있다. : register()
    - 등록을 해지할 수 있다. : drop()
    - 강의내용을 전달한다. : teach()

위의 내용으로 인터페이스를 작성하면 다음과 같다.
Teacher.java

public interface Teacher {
		void register(Student student);
		void drop(Student student);
		void teach(String msg);
}

Student.java

public interface Student {
		void learn(String msg);
}

위의 인터페이스를 가지고 수업하는 TeacherA와 수업을 듣는 StudentA, StudentB를 작성한다.
TeacherA.java

public class TeacherA implements Teacher {
	private ArrayList<Student> students = new ArrayList<Student>();

	public void teachJava() {
		System.out.println("tchA teach Java");
		teach("This is Java");
	}

	public void teachKotlin() {
		System.out.println("tchA teach Kotlin");
		teach("This is Kotlin");
	}

	@Override
	public void register(Student student) {
		System.out.println(student + " registered\n");
		students.add(student);
	}

	@Override
	public void drop(Student student) {
		System.out.println(student + " dropped\n");
		students.remove(student);
	}

	@Override
	public void teach(String msg) {
		for (Student std : students) {
			std.learn(msg);
		}

	}

StudentA.java

public class StudentA implements Student {

	@Override
	public void learn(String msg) {
		System.out.println("stdA TIL : " + msg);
	}
	
	// print용. 임의로 추가
	@Override
	public String toString() {
		return "StudentA";
	}
}

StudentB.java

public class StudentB implements Student {

	@Override
	public void learn(String msg) {
		System.out.println("stdB TIL : " + msg);
	}
	
	// print용. 임의로 추가
	@Override
	public String toString() {
		return "StudentB";
	}
}

만든 위 객체들을 가지고 실제로 실행 할 main함수를 작성하여 확인한다.
ObserverTest.java

public class ObserverTest {

	public static void main(String[] args) {

		System.out.println("###########Start ObserverTest###########");
		TeacherA tchA = new TeacherA();
		StudentA stdA = new StudentA();
		StudentB stdB = new StudentB();

		tchA.register(stdA);
		tchA.register(stdB);
	
		System.out.println("<Start Java Class>");
		tchA.teachJava();
	
		System.out.println();
		tchA.drop(stdA);

		System.out.println("<Start Kotlin Class>");
		tchA.teachKotlin();
		
		System.out.println("###########END ObserverTest###########");
	}
}

실행결과

  • 등록된 Student(Observer)는 Teacher(Subject)의 수업내용을 배운다.
  • Teacher의 수업이 진행(event)될 때 Student들은 수업내용을 익힌다.(callback)

예시 (java)

  • java에서 제공하는 observer와 Observable를 사용하여 위의 예시를 구현하면 다음과 같다
    TeacherB.java
public class TeacherB extends Observable {

	@Override
	public void addObserver(Observer obs) { // 오버라이딩을 통한 print문 추가
		System.out.println(obs + " registered\n");
		super.addObserver(obs);
	}

	public void teachObservable() {// 이벤트
		System.out.println("tchB teach Observable");
		setChanged(); // 메소드 호출 시 객체의 상태가 바뀌었음을 세팅
		notifyObservers("This is Observable"); // 옵저버들에게 전달
	}

	// print용 임의로 추가
	@Override
	public String toString() {
		return "TeacherB";
	}
}

StudentC.java

public class StudentC implements Observer {

	@Override
	public void update(Observable subject, Object msg) { // subject의 이벤트 Callback
		System.out.println("stdC TIL : " + msg);
	}

	// print용 임의로 추가
	@Override
	public String toString() {
		return "StudentC";
	}
}

StudentB.java

public class StudentB implements Student {

	@Override
	public void learn(String msg) {
		System.out.println("stdB TIL : " + msg);
	}
	
	// print용. 임의로 추가
	@Override
	public String toString() {
		return "StudentB";
	}
}

만든 위 객체들을 가지고 실제로 실행 할 main함수

ObserverTest.java

public class ObserverTest {

	public static void main(String[] args) {

		System.out.println("###########Start ObserverTest###########");

		TeacherB tchB = new TeacherB();
		StudentC stdC = new StudentC();

		tchB.addObserver(stdC); // 옵저버 추가

		System.out.println("<Start Pattern Class>");
		tchB.teachObservable(); // Subject의 이벤트

		System.out.println("###########END ObserverTest###########");
	}
}

실행결과

참고
[1] https://en.wikipedia.org/wiki/Singleton_pattern
[2] https://ko.wikipedia.org/wiki/%EC%98%B5%EC%84%9C%EB%B2%84_%ED%8C%A8%ED%84%B4
[3] https://en.wikipedia.org/wiki/Observer_pattern
[4] https://pjh3749.tistory.com/266

0개의 댓글