- 클래스의 인스턴스화를 단일 인스턴스로 제한하는 소프트웨어 설계 패턴
-> 생성자가 여러 차례 호출되더라도 실제로 생성되는 객체는 하나이고, 최초 생성 이후에 호출된 생성자는 최초의 생성자가 생성한 객체를 리턴하는 디자인 유형- 객체지향 소프트웨어에서 반복적인 문제를 해결하는 방법을 설명하는 Gang of Four 설계 패턴 중 하나
- 시스템 전체에서 동작을 조정하기 위해 정확히 하나의 객체가 필요할 때 유용
- 싱글톤 패턴은 Global namespace를 오염시키지 않아 전역변수(global variable)보다 선호함
- 추상 팩토리, 팩토리 메소드, 빌더, 프로토타입 패턴과 같은 다른 설계 패턴의 기초로 사용 됨
- 클래스의 모든 생성자를 private로 선언
-> 다른 객체에 의해 인스턴스화 되지 않도록 하기 위함- 인스턴스를 참조하는 Static 메소드를 사용
- 인스턴스는 일반적으로 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--; } }
- 소프트웨어 설계 및 엔지니어링에서 observer 패턴은 'subject'라는 객체가 observer라고 불리는 '종속 된 것들의 목록'(list of its dependents)을 유지 및 관리하고, 메서드 중 하나를 호출하여 상태 변화를 자동으로 통지하는 소프트웨어 설계 패턴[3]
-> 객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴[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.javapublic 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.javapublic 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.javapublic 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.javapublic 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