개요

  Proxy(대리인) 패턴은 어떤 업무를 처리 할 때 대리인이 대신 처리할 수 있는 업무는 대리인이 처리하고 직접 처리해야 할 업무만 본인이 직접 처리하도록 하는 패턴이다.

 업무 요청은 늘 대리인이 받고 대리인이 처리할 수 있는 업무는 처리하고 본인이 직접 처리해야 할 경우만 본인을 호출해 업무를 전달한다. 여기서 본인은 대리인의 존재 조차도 몰라도 된다.

 

역할

Subject 역할

 Proxy 역할과 RealSubject 역할을 동일시 하기 위한 인터페이스. 사용자는 Subject 역할을 통해 Proxy역할과 RealSubject 역할을 구분하지 않고 동일시 사용할 수 있다. 예제에서는 Printable 인터페이스.

 

Proxy 역할

 Client의 요청을 자신(대리인)이 가능한 범위만큼 처리하고, 자신(대리인)이 처리할 수 없다면 본인에게 처리를 넘기는 역할을 한다. 예제에서는 PrinterProxy 클래스.

 

RealSubject 역할

 Proxy역할(대리인)이 해결할 수 없는 요청을 넘겨받아 처리하는 본인 역할. 예제에서는 Printer 클래스.

 

Client 역할

 Proxy 패턴을 이용하는 역할. 업무를 Proxy에게 요청한다. 예제에서는 ProxyTester 클래스.

 

 

이득 및 유의사항

 프로그램에서 많은 자원이 소요되는 무거운 작업은 사실 뻔한데, 대표적인 것이 프로그램 기동과 그에 필요한 초기화 작업이다. Proxy패턴을 적용해서 기동시 실제로 바로 필요한 초기화 작업만 수행하면, 빠른 기동으로 프로그램 사용자의 불편을 줄일 수 있다.

 대리인과 본인을 굳이 분리해야 할 필요가 있을까 생각할 수도 있다. 지연평가(요청시 인스턴스 생성) 기능을 사용한다면 굳이 분리할 필요가 없지 않을까 싶을 수 있다. 하지만 RealSubject 역할과 Proxy 역할을 분리해서 프로그램을 구성하면 각 기능에 따라 모듈화가 되고 개별적인 수정과 변경이 더 자유로워 진다.

 흔히 Proxy라는 단어를 쉽게 접하는 것이 http proxy 서버일 텐데, 그 원리도 proxy패턴과 다르지 않다. 어떤 일을 클라이언트가 요청할 때 이를 실제로 먼저 받는 것은 proxy서버이고 자신이 처리할 수 있는(캐싱되어 있는) 요청이면 자신이 처리하고 아닐 경우만 외부에 요청을 전달하기 때문에 proxy서버는 proxy 역할, 외부 서버는 RealSubject 역할이 되는 것이다.

 

 

예제 코드

//Subject역할
package proxy;

public interface Printable {
	public abstract void setPrinterName(String name);
	public abstract String getPrinterName();
	public abstract void print(String string);
}


//Proxy 역할
package proxy;

public class PrinterProxy implements Printable{
	private String name;
	private Printer real;
	public PrinterProxy() {
		
	}
	
	public PrinterProxy(String name) {
		this.name = name;
	}
	
	

	@Override
	public synchronized void setPrinterName(String name) {
		if(real != null) {
			real.setPrinterName(name);
		}
		this.name = name;
	}

	@Override
	public String getPrinterName() {
		return name;
	}

	@Override
	public void print(String string) {
		realize();
		real.print(string);
	}
	
	private synchronized void realize() {
		if(real == null) {
			real = new Printer(name);
		}
	}
	
	
}


//RealSubject 역할
package proxy;

public class Printer implements Printable{

	private String name;
	
	public Printer() {
		heavyJob("Printer 인스턴스 생성 중");
	}
	
	public Printer(String name) {
		this.name = name;
		heavyJob("Printer 인스턴스 생성 중");
	}
	
	@Override
	public void setPrinterName(String name) {
		this.name = name;
	}

	@Override
	public String getPrinterName() {
		return name;
	}

	@Override
	public void print(String string) {
		System.out.println("===" + name + "===");
		System.out.println(string);
	}
	
	private void heavyJob(String msg) {
		System.out.println(msg);
		for(int i=0; i<5; i++) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
			}
			System.out.println(".");
		}
		System.out.println("완료.");
	}
		
}

//Client 역할
package proxy;

public class ProxyTester {
	public static void main(String[] args) {
		Printable p = new PrinterProxy("Alice");
		System.out.println("현재 이름은 "+p.getPrinterName()+"입니다.");
		p.setPrinterName("BoB");
		System.out.println("현재 이름은 " + p.getPrinterName()+"입니다.");
		p.print("Hello, world");
	}
}

'디자인 패턴' 카테고리의 다른 글

23. Interpreter 패턴  (0) 2021.02.15
20. Flyweight 패턴  (0) 2021.01.05
18. Memento 패턴  (0) 2020.12.30
17. Observer 패턴  (0) 2020.12.29
16. Mediator 패턴  (0) 2020.12.28

 오라클을 설치하고 나면 기본적으로 DBA 롤이 부여된 SYS, SYSTEM 계정이 생성되어있다. 

 패스워드를 설정하고 사용하면 되는데 두 계정의 권한차이가 무엇인지 정리해본다.

 

계정

1. SYS

 Oracle DB 관리자로 Super user이다. Oracle 시스템의 기반이 되는 Data dictionary 소유자이며 DB 생성과 삭제도 가능하다.

 사실 계정이라기 보단 Oracle 시스템의 총 관리자라고 보는게 맞고 아래서 설명할 SYSDBA 권한을 갖는다.

 

2. SYSTEM

 SYS와 유사한 권한을 가지고 있지만 DB 생성과 삭제는 불가능하다. 운영을 위한 권한을 갖는다 보면 된다. 아래서 설명할 SYSOPER의 권한을 갖는다.

 

 얼핏보면 SYS와 SYSTEM 계정이 별차이가 없는 것 아닌가 싶을 수도 있지만 SYS는 Oracle 시스템을 유지, 관리, 생성하기 위한 모든 권한을 갖는 계정이고, SYSTEM은 생성된 DB를 운영, 관리하기 위한 관리자 계정이라고 보면 된다.

 

권한

 SYSOPER

 데이터베이스를 운영 관리하기 위한 권한으로 SYSTEM 계정이 갖는다. 아래 권한 외에 데이터베이스 자체를 생성 삭제하거나, 다른 유저 소유의 데이터에는 접근할 수 없다.

 - 인스턴스와 데이터베이스에 대한 startup, mount, open shutdown, dismount, close 권한

 - 데이터베이스 백업, 로그파일을 이용한 복구 등에 대한  database backup, archive log, recover 권한

 

 SYSDBA

  SYSOPER의 권한 뿐 아니라 데이터베이스 생성과 삭제, Oracle 시스템에서 모든 권한을 갖는다. SYS 계정이 갖는 권한.

 

'ORACLE > ORACLE 기초' 카테고리의 다른 글

오라클 시노님(Synonym)  (0) 2021.01.09
12. PL/SQL - PROCEDURE, User-defined FUNCTION, TRIGGER, CURSOR  (0) 2020.09.12
11. Sequence, Index  (0) 2020.09.12
11. View  (0) 2020.09.12
10. Transaction, Lock  (0) 2020.09.12

오라클 객체(테이블, 뷰, 시퀀스, 프로시져)에 대한 대체이름을 말한다. 대체이름을 통해 실적 객체에 간접적으로 참조할 수 있다.

시노님을 사용함으로써 얻을 수 있는 이점은 아래와 같다.

 

  • 데이터베이스의 투명성을 높여준다. 다른 유저의 객체를 참조할 수 있게 해준다.
  • 시노님을 사용함으로써 차후 오라클 객체가 교체되더라도 시노님만 이전 명으로 정의해주면 응용단에서는 SQL을 수정하거나 추가적인 작업을 하지 않아도 된다.
  • 객체의 이름이 길고 복잡할 때 시노님을 사용함으로써 코드를 단순화 할 수 있다.
  • 객체 정보를 숨길 수 있어서 보안성이 높아진다. 시노님이 가르키는 객체의 소유자, 이름, 서버이름 등을 몰라도 된다.

시노님은 Private(default), Public 두 가지가 있다. Private는 특정 사용자만, Public은 모든 사용자가 사용이 가능하다.

시노님 사용법은 VIEW와 같이 테이블처럼 호출이 가능하다.

//Synonym 생성
CREATE [PUBLIC] SYNONYM synonym_name FOR object_name

//Synonym 삭제
DROP SYNONYM synonym_name

'ORACLE > ORACLE 기초' 카테고리의 다른 글

오라클 SYS, SYSTEM, SYSDBA, SYSOPER 계정  (0) 2021.01.09
12. PL/SQL - PROCEDURE, User-defined FUNCTION, TRIGGER, CURSOR  (0) 2020.09.12
11. Sequence, Index  (0) 2020.09.12
11. View  (0) 2020.09.12
10. Transaction, Lock  (0) 2020.09.12

 Tomcat과 Oracle을 함께 사용해서 개발을 해보면 기본적으로 사용하는 port가 8080이기 때문에 포트 충돌이 발생하는걸 볼 수 있다.

 

 문제 해결을 위해서 Oracle http port를 변경해본다.

 

1. sqlplus나 sql developer 등 편한 툴로 관리자(SYSTEM) 계정에 접속한다.

 

2. 다음 명령어로 현재 port 번호를 확인한다. (기본 8080)

select dbms_xdb.gethttpport() from dual;

 

3. 다음 명령어로 http port 번호를 원하는 번호로 변경해준다.(아래 코드에선 9090으로 지정)

exec dbms_xdb.sethttpport(9090);

 

Spring 환경에서 작업을 하다보면 다들 한번쯤은 문자가 깨지는걸 경험해 봤을 거라 생각한다. 그러면 당연히 어딘가 Encoding 설정이 안되어 있는 것일텐데, 어디를 손대야 하는지 정리해본다.

 

Eclipse(or STS)의 workspace Encoding 설정

 

JSP file의 Encoding 설정

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

 

web.xml 의 Encoding filter 설정

    <filter>
        <filter-name>setCharacterEncodingFilter</filter-name>
        <filter-class>org.apache.catalina.filters.SetCharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
	<filter-mapping>
        <filter-name>setCharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

 

DB(여기선 Oracle)의 Encoding 설정

  sqlplus을 켜고 다음 명령어를 순서대로 실행해서 DATABASE의 encoding 설정을 utf-8로 변경한다.

conn system/패스워드 as sysdba

update sys.props$ set value$='AL32UTF8' where name='NLS_CHARACTERSET';

update sys.props$ set value$='AL16UTF16' where name='NLS_NCHAR_CHARACTERSET';

commit;

shutdown immediate;

startup mount;

alter system enable restricted session;

alter system set JOB_QUEUE_PROCESSES=0;

alter database open;

alter database character set INTERNAL_USE AL32UTF8;

alter database national character set INTERNAL_USE AL16UTF16;

shutdown immediate;

startup

개요

 메모리 사용을 최소화 해서 가볍게 하기 위한 패턴. 인스턴스를 가능한 공유해 쓸데없이 new(인스턴스 생성)을 하지 않도록 하는게 Flyweight패턴의 핵심이다. 

 만들어진 인스턴스를 Factory 인스턴스에 저장해놓고 저장된 인스턴스에 대응하는 인스턴스가 필요할 땐 새롭게 인스턴스를 생성하는 것이 아니라 저장된 인스턴스를 사용한다. 

 

 

역할

Flyweight 역할

 취급에 프로그램이 무거워지는 대상으로 인스턴스 공유를 통해 프로그램을 가볍게 하기 좋은 대상. 예제에서는 BigChar 클래스.

 

FlyweightFactory 역할

 Flyweight 인스턴스를 생성하고 요청에 따라 인스턴스를 내어주는 공장 역할. 예제에서는 BigCharFactory 클래스.

 

Client 역할

 FlyweightFactory를 이용해서 Flyweight 역할의 인스턴스를 사용하는 역할. 예제에서는 BigString 클래스

 

 

이득 및 유의사항

 인스턴스를 공유함으로써 얻을 수 있는 이점은 역시 개요에 작성한대로 인스턴스 생성에 필요한 자원을 아낄 수 있다는 점이다. 인스턴스 생성에 많은 자원이 소모되는 인스턴스 일수록 Flyweight 패턴을 적용함으로써 프로그램을 가볍게 만들 수 있다.

 

 가장 주의해야 할 점은 공유로 인한 일관성의 문제이다. 모든 공유 요소를 포함하는 프로그래밍에서 해결해야 할 문제인데 사용자(Client)가 사용하는 인스턴스가 다른 사용자도 사용이 가능하기 때문에 공유하고 있는 인스턴스를 다른 사용자가 조작하면 내가 기대한 결과가 나오지 않을 수 있는 점이다.  때문에 Flywieght 역할에게 제공해서 공유할 정보를 신중하게 선택해야 한다. 어떤 정보를 기준으로 Flyweight 인스턴스를 생성하고 구분할지는 이를 사용할 목적에 따라 선택해야 한다.

 

 

예제 코드

// Flywieght 역할
package flyweight;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BigChar {

	private char charname;
	private String fontdata;
	
	public BigChar(char charname) {
		this.charname = charname;
		
		try {
			BufferedReader reader = new BufferedReader(new FileReader("big"+ charname + ".txt"));
			String line;
			StringBuffer buffer = new StringBuffer();
			
			while((line = reader.readLine())!=null) {
				buffer.append(line);
				buffer.append("\n");
				
			}
			
			reader.close();
			this.fontdata = buffer.toString();
			
		} catch(IOException e) {
			this.fontdata = charname +"?";
		}
	}
	
	public void print() {
		System.out.print(fontdata);
	}
	
	
}


// FlywieghtFactory 역할
package flyweight;

import java.util.HashMap;

public class BigCharFactory {

	private HashMap<Character, BigChar> pool = new HashMap<>();
	
	private static BigCharFactory singleton = new BigCharFactory();
	
	private BigCharFactory() {
		
	}
	
	public static BigCharFactory getInstance() {
		return singleton;
	}
	
	public synchronized BigChar getBigChar(char charname) {
		BigChar bc = pool.get(charname);
		
		if(bc == null) {
			bc = new BigChar(charname);
			pool.put(charname, bc);
		}
		return bc;
	}
}



// Clinet 역할
package flyweight;

public class BigString {
	private BigChar[] bigchars;
	
	public BigString(String str) {
		bigchars = new BigChar[str.length()];
		BigCharFactory factory = BigCharFactory.getInstance();
		for(int i=0; i<bigchars.length; i++) {
			bigchars[i] = factory.getBigChar(str.charAt(i));
		}
	}
	
	public void print() {
		for(int i=0; i<bigchars.length; i++) {
			bigchars[i].print();
		}
	}
}



// Tester
package flyweight;

public class FlywieghtTester {
	public static void main(String[] args) {
		if(args.length == 0) {
			System.out.println("Usage: java Main digits");
			System.out.println("Example : java Main 1212");
			System.exit(0);
		}
		
		BigString bs = new BigString(args[0]);
		bs.print();
	}
}

'디자인 패턴' 카테고리의 다른 글

23. Interpreter 패턴  (0) 2021.02.15
21. Proxy 패턴  (0) 2021.01.11
18. Memento 패턴  (0) 2020.12.30
17. Observer 패턴  (0) 2020.12.29
16. Mediator 패턴  (0) 2020.12.28

개요

 MS오피스 같은 문서편집기나 다른 여러 컴퓨터 프로그램에서 자연스럽게 익숙해진 기능이 바로 되돌리기 기능이다. 객체지향 프로그램상에서 이러한 되돌리기 기능이 있다면 어떨까? 실행 이전 상태로 되돌려야 하기 때문에 인스턴스가 갖고 있는 정보를 일정기간 저장해놓아야 하고, 인스턴스 상태를 실행 이전으로 되돌려야 한다. 그런데 외부에서 인스턴스를 조작해서 상태를 조작하는 것은 객체지향 프로그래밍에서 중요한 기법인 캡슐화와는 반대되는 방법이다.

 

 캡슐화를 파괴하지 않으면서도 정보 저장과 상태 복원이 가능하도록 해주는 것이 바로 Memento(기념품) 패턴이다.

 Memento 패턴을 이용해 undo, redo, history, snapshot 등의 기능을 구현할 수 있다.

 

역할

 Originator 역할

  자신의 현재 상태를 저장하고 싶을 때 Memento 역할의 인스턴스를 만들고, 이전의 상태로 되돌리고 싶을 땐 Mememto 역할의 인스턴스를 받아서 교체함으로 상태를 되돌린다. 예제에서는 Gamer 클래스.

 

Memento 역할

  Originator의 내부 정보를 정리하서 저장하고 있으며 외부로 정보를 노출시키지 않는다. 예제에서는 Memento 클래스.

 

Caretaker 역할

  현재 상태를 저장하고 싶을 때 Originator 역할에게 저장하라는 메시지를 전달하고 Originator는 메시지를 받아 Memento 역할의 인스턴스를 생성해 Caretaker에게 전달한다. Caretaker는 백업이 필요할 때를 대비해 Memento 인스턴스를 보관하고 있는다. 예제에서는 MemetoTester 클래스.

  

 

이득 및 주의사항

 Memento  패턴에는 java의 접근제어자를 이용해서 캡슐화가 파괴되지 않게 하고 있다. Caretaker는 Memento 인스턴스를 보관하지만 getMoney() 메서드를 제외하고는 해당 인스턴스를 이용할 수 없다. 

  

 Caretaker가 보관하는 Memento 인스턴스 갯수를 늘려서 여러 시점의 상태를 저장할 수 도 있다. 문제는 Memento 인스턴스를 DB 등에 보관할 때 인데, 만약 프로그램의 버전이 변경되거나 수정되면 이전에 저장하고 있던 Memento 인스턴스를 사용할 때 모순이 발생할 수 도 있기에 주의해야 한다.

 

 Originator(예제의 Gamer 클래스)에 undo나 snapshot 기능을 포함시키면 간단하겠지만 이를 굳이 분리하는 이유는 이를 통해 다양한 버전의 undo 정보를 저장하거나 Originator의 현재 상태를 파악하는 등에 있어서 Originator 역할의 클래스를 수정할 필요가 없다는 것이다. Originator는 다양한 버전(undo를 위한 이전상태)과 상관없이 현재 정보만 가지고 있으면 되는 것이다.

 

 

예제 코드

//Originator 역할
package memento.game;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

public class Gamer {
	private int money;
	private List fruits = new ArrayList();
	private Random random = new Random();
	private static String[] fruitsname = {
			"사과", "포도", "오렌지", "귤",
	};
	
	public Gamer(int money) {
		this.money = money;
	}
	
	public int getMoney() {
		return money;
	}
	
	public void bet() {
		int dice = random.nextInt(6) +1;
		if(dice==1) {
			money +=100;
			System.out.println("소지금 증가");
		} else if(dice==2) {
			money /=2;
			System.out.println("소지금 반");
		} else if(dice == 6) {
			String f= getFruit();
			System.out.println("과일 (" + f + ")을 받았습니다.");
			fruits.add(f);
		} else {
			System.out.println("변함 없음");
		}
	}
	
	public Memento createMemento() {
		Memento m = new Memento(money);
		Iterator it = fruits.iterator();
		while(it.hasNext()) {
			String f = (String)it.next();
			if(f.startsWith("맛있는")) {
				m.addFruit(f);
			}
		}
		
		return m;
	}
	
	public void restoreMemento(Memento memento) {
		this.money = memento.money;
		this.fruits = memento.getFruits();
	}
	
	public String toString() {
		return "[money = " + money + ", fruits = " + fruits + "]";
	}
	
	private String getFruit() {
		String prefix = "";
		if(random.nextBoolean()) {
			prefix = "맛있는";
		}
		return prefix + fruitsname[random.nextInt(fruitsname.length)];
	}
}


//Memento 역할
package memento.game;

import java.util.ArrayList;
import java.util.List;

public class Memento {
	int money;
	ArrayList fruits;
	
	public int getMoney() {
		return money;
	}
	
	Memento(int money) {
		this.money = money;
		this.fruits = new ArrayList();
	}
	
	void addFruit(String fruit) {
		fruits.add(fruit);
	}
	
	List getFruits() {
		return (List)fruits.clone();
	}
}


//Caretaker역할
package memento;

import memento.game.Gamer;
import memento.game.Memento;

public class MementoTester {
	public static void main(String[] args) {
		Gamer gamer = new Gamer(100);
		Memento memento = gamer.createMemento();
		
		for(int i=0; i<100; i++) {
			System.out.println("====" + i);
			System.out.println("현상 : "+gamer);
			
			gamer.bet();
			
			System.out.println("소지금은 " + gamer.getMoney() + "원 입니다.");
			
			if(gamer.getMoney() > memento.getMoney()) {
				System.out.println("증가 : 현재 상태 저장");
				memento = gamer.createMemento();
			} else if (gamer.getMoney() < memento.getMoney()) {
				System.out.println("감소 : 이전 상태로 되돌리기");
				gamer.restoreMemento(memento);
			}
			
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				System.out.println("");
			}
		}
	}
}

'디자인 패턴' 카테고리의 다른 글

21. Proxy 패턴  (0) 2021.01.11
20. Flyweight 패턴  (0) 2021.01.05
17. Observer 패턴  (0) 2020.12.29
16. Mediator 패턴  (0) 2020.12.28
15. Facade 패턴  (0) 2020.12.26

개요

 Observer(관찰자) 패턴은 관찰 대상의 상태 변화를 알려주기 위한 패턴으로 어떤 대상의 상태변화에 따른 작업을 처리할 때 적합한 패턴이다.

 

역할

Subject 역할

  관찰 대상으로 Observer 역할을 자신에게 등록하고 삭제하는 메소드를 갖는다. 그리고 현재 상태를 취득하는 메서드를 선언한다. 예제에서는 NumberGenerator 클래스.

 

ConcreteSubject 역할

  구체적인 관찰 대상으로 상태가 변할 때 자신에게 등록해놓은 Observer 역할에게 이를 전달한다. 예제에서는 RandomNumberGenerator 클래스.

 

Observer 역할

  Subject가 상태가 변할 때 이를 전달받는 역할로 이를 위한 인터페이스를 선언한다. 예제에서는 Observer 클래스가 이 역할이며 상태 변화를 전달 받는 메서드는 update 메서드이다.

 

ConcreteObserver 역할

  Observer를 구체화 하는 역할로 update 메소드가 호출될 때 Subject 역할의 상태에 대한 정보를 얻는다. 예제에서는 DigitObserver, GraphObserver 클래스.

 

 

이득 및 주의사항

 Subject는 자신이 어떤 ConcreteObserver 인스턴스를 가지고 있는지 몰라도 상관없다. 그저 자기가 저장하고 있는 Observer 인스턴들을 가지고 update 메서드를 호출하기만 하면 된다.

 반대로 Observer는 자신이 관찰하고 있는 Subject가 어떤 ConcreteSubject 인스턴스인지 신경쓰지 않아도 된다.

 결과적으로 이전에 학습한 디자인 패턴과 같이 실제 구체화된 인스턴스가 무엇인지 상관없이 모두 동일한 인터페이스를 가지고 있기에 구체화 하는 클래스를 필요에 따라 쉽게 교체할 수 있다.

 

 Subject 인스턴스가 가지고 있는 Observer 인스턴스들은 저장된 순서가 정해져 있다. 만약 Observer 인스턴스간의 상호 의존성이 있어 호출 순서가 지켜져야 한다면 순서에 따른 오류가 발생하지 않도록 주의해야한다. 그리고 그보다는 각 인스턴스간 독립성을 보장하도록 하는 것이 더 바람직한다.

 

 Observer가 Subject의 상태 변화에 따라 어떤 동작을 할때 Subject 인스턴스를 가지고 동작해야 한다면 무한루프에 빠지지 않도록 주의해야 한다. 다시 말하자면 Subject의 상태 변화로 인해 Observer가 동작하고 이로인해 다시 Subject의 상태가 변하면서 Observer가 동작하는 끝없는 반복 상태를 말한다.

 

 

예제 코드

//Subject 역할
package observer;

import java.util.ArrayList;
import java.util.Iterator;

public abstract class NumberGenerator {
	private ArrayList observers = new ArrayList();
	
	public void addObserver(Observer observer) {
		observers.add(observer);
	}
	
	public void deleteObserver(Observer observer) {
		observers.remove(observer);
	}
	
	public void notifyObservers() {
		Iterator it = observers.iterator();
		
		while(it.hasNext()) {
			Observer o = (Observer)it.next();
			o.update(this);
		}
	}
	
	public abstract int getNumber();
	public abstract void execute();
}


//ConcreteSubject역할
package observer;

import java.util.Random;

public class RandomNumberGenerator extends NumberGenerator{
	private Random random = new Random();
	private int number;
	
	public int getNumber() {
		return number;
	}
	
	public void execute() {
		for(int i=0; i<20; i++) {
			number = random.nextInt(50);
			notifyObservers();
		}
	}
	
}


//Observer 역할
package observer;

public interface Observer {
	public abstract void update(NumberGenerator generator);
}


//ConcreteObserver 역할1
package observer;

public class DigitObserver implements Observer{
	public void update(NumberGenerator generator) {
		System.out.println("DigitObserver : " + generator.getNumber());
		try {
			Thread.sleep(100);
		} catch(InterruptedException e) {
		}
	}
}


//ConcreteObserver 역할2
package observer;

public class GraphObserver implements Observer{
	public void update(NumberGenerator generator) {
		System.out.println("GraphObserver");
		int count = generator.getNumber();
		for(int i=0; i<count; i++) {
			System.out.print("*");
		}
		System.out.println();
		try {
			Thread.sleep(100);
		} catch(InterruptedException e) {
		}
	}
}


//Tester
package observer;

public class ObserverTester {
	public static void main(String[] args) {
		NumberGenerator generator = new RandomNumberGenerator();
		Observer observer1 = new DigitObserver();
		Observer observer2 = new GraphObserver();
		
		generator.addObserver(observer1);
		generator.addObserver(observer2);
		generator.execute();
	}
}

'디자인 패턴' 카테고리의 다른 글

20. Flyweight 패턴  (0) 2021.01.05
18. Memento 패턴  (0) 2020.12.30
16. Mediator 패턴  (0) 2020.12.28
15. Facade 패턴  (0) 2020.12.26
14. Chain of Responsibility 패턴  (0) 2020.12.22

+ Recent posts