해당 포스트는 "자바 객체지향 디자인 패턴", "JAVA 언어로 배우는 디자인 패턴 입문" 책의 내용을 요약한 것이다.
※옵저버(Observer) 패턴
: 한 객체의 상태가 바뀌면 그 객체에 의존/연결 되어 있는 다른 하나 또는 다수의 객체들한테 자동으로 이벤트가 전달되어 정보가 갱신되는 패턴이다. 예를 들면, 신문을 발행하는 신문사와 신문 정기 구독자의 관계, 동아리 회원과 회원들에게 매주 공지사항을 보내는 운영진들과의 관계 등 많이 있다.
- 옵저버 패턴 구현 방법 2가지
1. JDK에서 지원하는 Observable과 Observer API를 활용하는 방법
2. API를 활용하지 않고 직접 구현하는 방법
ex) 해당 예제는 http://hyeonstorage.tistory.com에서 가져왔다.
해당 예제는 온도, 습도, 압력 데이터가 갱신되어 WeatherData 클래스로 데이터가 전송되면 WeatherData클래스는 자신과 연결되어 있는 다수의 Display 객체에 갱신 정보가 전송되어 자신의 디스플레이를 업데이트하는 예제이다.
1. 첫 번째 방법인 JDK API를 이용한 방법
<Observable>
import java.util.Observable; public class WeatherData extends Observable { private float temperature; private float humidity; private float pressure; public WeatherData(){} /** * 새로운 데이터를 전달받아 갱신하고 새로운 데이터가 왔다는 것을 연결된 객체들에게 알린다. */ public void setData(float temperature, float humidity, float pressure){ this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; dataChanged(); } /** * setChanged() : Observable 클래스 내부 changed 변수를 true로 만들어준다. * changed 변수가 true여야 연결된 객체(옵저버)에 신규 데이터가 전달된다. * notifyObservers() : 등록된 옵저버에게 데이터를 전송한다. 옵저버에서는 update() 함수가 호출된다. */ public void dataChanged() { super.setChanged(); super.notifyObservers(); } public float getTemperature() { return temperature; } public float getHumidity() { return humidity; } public float getPressure() { return pressure; } }
<Observer>
import java.util.Observable; import java.util.Observer; public interface Display { public void display(); } class KoreanDisplay implements Observer, Display{ private Observable observable; // 등록될 Observable private float temperature; private float humidity; private float pressure; public KoreanDisplay(Observable observable) { this.observable = observable; observable.addObserver(this); //this(KoreaDisplay)를 옵저버로 등록 } @Override public void display() { System.out.println("온도 : "+temperature); System.out.println("습도 : "+humidity); System.out.println("압력 : "+pressure); } /* * Observable에서 notifyObservers()를 호출할 시 해당 메소드가 호출되어 옵저버의 데이터가 갱신 */ @Override public void update(Observable arg0, Object arg1) { if(arg0 instanceof WeatherData){ WeatherData weatherData = (WeatherData)arg0; this.temperature = weatherData.getTemperature(); this.humidity = weatherData.getHumidity(); this.pressure = weatherData.getPressure(); display(); } } } class EnglishDisplay implements Observer, Display{ private Observable observable; // 등록될 Observable private float temperature; private float humidity; private float pressure; public EnglishDisplay(Observable observable) { this.observable = observable; observable.addObserver(this); //this(EnglishDisplay)를 옵저버로 등록 } @Override public void display() { System.out.println("temperature : "+temperature); System.out.println("humidity : "+humidity); System.out.println("pressure : "+pressure); } /* * Observable에서 notifyObservers()를 호출할 시 해당 메소드가 호출되어 옵저버의 데이터가 갱신 */ @Override public void update(Observable arg0, Object arg1) { if(arg0 instanceof WeatherData){ WeatherData weatherData = (WeatherData)arg0; this.temperature = weatherData.getTemperature(); this.humidity = weatherData.getHumidity(); this.pressure = weatherData.getPressure(); display(); } } }
public class Main { public static void main(String[] args) { WeatherData weatherData = new WeatherData(); Display kd = new KoreanDisplay(weatherData); Display ed = new EnglishDisplay(weatherData); weatherData.setData(20, 50, 10); System.out.println("30분 후"); weatherData.setData(30, 40, 5); } }
<결과>
예제에서 보다시피 weatherData(Observable)의 setData() 메서드를 통해서 Observer들의 데이터를 갱신한다. 하지만 Observable이 클래스이기 때문에 다중 상속이 불가능한 자바에서 사용성이 많이 떨어진다. 또한, Observable 클래스 핵심 메서드(setChanged) 같은 것들이 protected로 선언되어 있어 외부에서 호출이 불가능하다는 단점이 있다. 이에 대한 해결책으로 두 번째 방법인 직접 구현하는 방법이 있다. 그건 다음 포스트에 작성할 예정이다.
'자바 > 디자인패턴' 카테고리의 다른 글
퍼사드 패턴 (0) | 2017.06.08 |
---|---|
프로토타입 패턴 (0) | 2017.06.08 |
컴퍼지트 패턴 (0) | 2017.06.07 |
데코레이터(Decorator) 패턴 (0) | 2017.06.06 |
옵저버(Observer) 패턴(2) - 직접 구현 (0) | 2017.06.06 |
영상 입출력 프로그램 만들기(1) - 비트맵 구조
해당 포스트는 "열혈강의 영상처리 프로그래밍" 책의 내용을 요약한 것이다.
※ 영상 파일의 형식
- BMP : 압축을 하지 않고 픽셀 데이터 하나하나가 파일에 기록된다. 나머지 파일 형식은 미리 약속된 형식을 통해 인코딩되 압축된 형태로 저장되며 파일을 읽을 때 디코딩을 수행해야 한다. 이러한 인코딩과 디코딩 코드는 라이브러리를 통해 얻을 수 있다.
- JPG : 디지털 카메라에서 많이 사용
- PNG : pc나 인터넷 환경에서 사용
- gif : 웹에서 많이 사용되고 움직이는 이미지를 표현할 때 사용
여기서는 가장 간단한 영상 파일 형식인 비트맵 파일의 입출력을 다룰 예정이다.
※ 비트맵 파일의 구조
1. 비트맵 파일 헤더
//wingdi.h 헤더 파일에 정의 typedef struct tagBITMAPFILEHEADER { WORD bfType; //비트맵을 뜻하는 'BM' 알파벳이 적혀 있다. 이 변수를 보고 파일이 비트맵인지 알 수 있다. DWORD bfSize; //파일의 전체 크기 WORD bfReserved1; //사용X WORD bfReserved2; //사용X DWORD bfOffBits; //실제 픽셀 데이터의 시작 offset주소를 바이트 수로 나타냄 } BITMAPFILEHEADER
파일 자체에 대한 정보를 담는 부분
WORD는 2byte의 unsigned short형, DWORD는 4byte의 unsigned long형에 해당한다.
bfSize를 16진수로 볼 때 '36 00 24 00'일 경우 파일 전체 크기는 0x24*(2의 16승) + 0x36*(2의 0승) 바이트가 된다. 이와 같은 연산을 하는 이유는 작은 자리의 바이트를 앞에 배치하는 리틀 엔디언 방식 때문이다.
2. 영상 정보 헤더
typedef struct tagBITMAPINFOHEADER{ DWORD biSize; //영상 정보 헤더의 크기(40byte) LONG biWidth; //영상의 가로 픽셀 수 LONG biHeight; //영상의 세로 픽셀 수 WORD biPlanes; WORD biBitCount; //픽셀 당 비트 수로 회색조 영상의 경우 8, rgb 트루 컬러일 경우 24(1,4,8,16,24,32중 하나) DWORD biCompression; //압축 타입으로 비트맵의 경우 보통 BI_RGB(0)이다. DWORD biSizeImage; //영상 데이터의 크기 LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; } BITMAPINFOHEADER
파일에 저장된 영상 정보를 담는다.
3. 팔레트 정보
typedef struct tagRGBQUAD { BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved; //사용X } RGBQUAD
영상 데이터의 크기를 줄이기 위해 미리 정해진 색상의 집합이다. 팔레트를 사용하는 영상의 픽셀은 색상 값 자체를 가지는 것이 아니라 그 픽셀이 사용하는 팔레트 색상의 번호를 가지게 된다. 이러한 팔레트 정보는 위의 RGBQUAD 구조체를 이용한다. 하지만 최근에 컴퓨터의 연산속도와 메모리 증가로 회색조 영상을 제외하고는 팔레트를 사용하지 않는다. 팔레트를 사용할 경우 보통 (0,0,0,0)에서 (255,255,255,0)값을 순차적으로 가진다. 이번 영상 입출력 프로그램 만들기 예제에서 실제로 팔레트를 사용할 예정이다.
'영상처리 프로그래밍' 카테고리의 다른 글
영상 입출력 프로그램 만들기(3) - MDI에서 화면 출력 (0) | 2017.06.07 |
---|---|
영상 입출력 프로그램 만들기(2) - 비트맵 파일 읽기와 쓰기 (0) | 2017.06.06 |
영상 데이터 클래스 구현(5) (0) | 2017.06.04 |
영상 데이터 클래스 구현(4) (0) | 2017.06.04 |
영상 데이터 클래스 구현(3) (0) | 2017.06.04 |