'전체 글'에 해당되는 글 678건

  1. 2011.08.13 [펌] The Interpreter Pattern
  2. 2011.08.13 [펌] The History of Design Patterns
  3. 2011.08.13 [펌] The Flyweight Pattern
  4. 2011.08.13 [펌] The Factory Pattern

The Interpreter Pattern

    몇몇 프로그램들은 그것들이 수행할 수 있는 연산자들을 설명하는 언어를  갖는 것으로부터 이점을 얻는다. Interpreter 패턴은 일반적으로 언어에 대한 문법 정의를 묘사하고 언어에서 문장들을 해석하는 문법 사용을 묘사한다.

Motivation

프로그램이  다른 것들을 나타내지만 몇 가지는 유사한 경우가 있을 때 Interpreter 패턴은 이러한 경우들을 묘사하는 간단한 언어를 이용할 수 있는 이점이 있고, 언어를 해석하는 프로그램을 갖는다.

    문제중의 하나는 우리가 다루어야 할 것이 언어가 도움을 줄 수 있을 때를 어떻게 인식하는가 이다. 매크로 언어의 리코더는 단순히 후에 재생되기 위한 메뉴와 키스트로크 연산자들을 기록한다.

Applicability

Interpreter 가 어디에서 도움을 줄 수 있는지를 인식하는 것은 어려운 문제이고, 프로그래머들은 형식적인 언어/컴파일러 연습 없이 종종 이러한 접근을 간과한다. Interpreter 패턴이 사용되는 경우가 많지는 않지만, 언어들이 이용할 수 있는 두 가지의 일반적인 경우가 있다.
  1. 프로그램이 대수적인 문자열을 해석해야만 할 때. 이 경우는 명확한 경우이다. 프로그램은 사용자가 입력한 어떤 부류의 식이 있는 계산에 기인한 연산자들을 수행하도록 요청된다. 이것은 종종 수리적인 그래픽 프로그램에서 발생한다.
     
  2. 프로그램이 다양한 종류의 결과를 만들어 내야 할 때. 이 경우는 약간 명확하지 않지만, 좀더 유용하다. 정렬되지 않는 데이터의 열들을 나타내고 다양하게 정렬할 수 있는 프로그램을 고려해 보자. 이러한 프로그램은 종종 보고서 생성기로 참조되어지고, 원래 데이터는 관계 형 데이터베이스에 저장되어 있다. 보고서 작성 프로그램에 대한 사용자 인터페이스는 일반적으로 SQL 보다 훨씬 간단하다. 사실 몇 가지 경우에서 간단한 보고서 언어는 보고서 프로그램으로 해석되어 질 수 있고 SQL로 변환될 수 있다.

Sample Code

테이블에서 5개의 데이터 열을  연산하고 이러한 데이터로 다양한 보고서들을 반환하는 보고서 생성기를 단순화하여 고려해 보자. 우리가 어떤 수영 대회에서 다음의 정렬된 결과를 가지고 있다고 해보자. 
 
First Name Last Name Age Club Time
Amanda McCarthy 12 WCA 29.28
Jamie Falco 12 HNHS 29.80
Meaghan O'Donnell 12 EDST 30.00
Greer Gibbs 12 CDEV 30.04
Rhiannon Jeffrey 11 WYW 30.04
Sophie Connolly 12 WAC 30.05
Dana Helyer 12 ARAC 30.18
51명 수영선수들의 결과를 얻었다면 우리는 클럽, 성이나 나이에 따라 결과를 정렬하는 것이 편리할지도 모른다는 것을 인식할 것이다.

    우리는 정렬의 재귀적이지 않는 문법을 정의할 것이다.

Print lname frname club time sortby club thenby time

    이 예제에서는 아래의 단어들처럼 3개의 동사를 정의하였다.

Print
Sortby
Thenby

    그리고 5개의 열 이름이 나열되었다.
Frname
Lname
Age
Club
Time

    편리성을 위하여 우리는 언어는 사건에 둔감하다고 가정하자. 또 이 언어의 간단한 문법은 구두점이 없다는 것이다. 그리고 결과적으로 다음처럼 된다.

    Print var[var] [sortby var [thenby var]]

마지막으로 유일한 주 동사가 있고 반면에 각각의 문장이 선언되어진다. 이 문법에서는 문장 할당이나 계산 능력이 없다.

Interpreting the Language

인터프리팅 언어는 3개의 단계로 이루어진다.
  1. 토큰(token)으로 언어의 기호를 해석한다.
  2. 토큰들을 액션(action)으로 분해한다.
  3. 액션을 실행한다.

우리는 StringTokenizer를 가지고 각각의 문장을 읽고 각각의 단어에 대해 어떤 수로 대체해서 언어를 토큰으로 해석한다. 일반적으로 파서들은 분해된 토큰을 하나의 스택에 넣는다. 우리는 Stack 클래스를 벡터를 사용하여 구현하였다. Stack 클래스는 스택 내용을 조사하고 쉽게 다루기 위해 push, pop, top 그리고 nextTop 메소드를 가지고 있다.

    파싱후에 우리의 스택은 아래와 같이 보여질 수 있다:

Type Token    
Var Time ← top of Stack
Verb Thenby
var Club
Verb Sortby
Var Time
Var Frname
Verb Lname

     그러나, 우리는 "verb" thenby 는 다른 것보다 실제적인 의미가 없다는 것을  알 수 있다. 우리의 초기 스택은 다음처럼 보여진다.

Time
Club
Sortby
Time
Club
Frname
Lname
Print

Objects Used in Parsing

실제적으로 우리는 스택에 수리적인 토큰은 넣지 않지만, ParseObject 타입과 값을 갖는다 

public class ParseObject {
	public static final int VERB=1000, VAR = 1010, MULTVAR = 1020;
	protected int value;
	protected int type;
	
	public int getValue() {return value;}
	public int getType() {return type;}
}
이러한 객체들은 VERB 나 VAR 타입을 가질 수 있다. 그리고 나서 우리는 이 객체를 ParseVerb 와 ParseVar 객체로 확장한다.

    여기에 간단한 계층구조가 있다:

        ##########0*

    파싱 프로세서는 StringTokenizer 와 파서 객체들을 이용하여 아래처럼 코드를 작성할 수 있다.

public Parser(String line) {
	stk = new Stack();
	actionList = new Vector();
	
	StringTokenizer tok = new StringTokenizer(line);
	
	while(tok.hasMoreElements()) {
		ParseObject token = tokenize(tok.nextToken());
		if(token != null)
			stk.push(token);
	}
}           

//----------------------------------------
private ParseObject tokenize(String s) {
	ParseObject obj = getVerb(s);
	if (obj == null)
		obj = getVar(s);
	return obj;
}

//----------------------------------------
private ParseVerb getVerb(String s) {
	ParseVerb v;
	v = new ParseVerb(s);
	if (v.isLegal()) 
		return v.getVerb(s);
	else
		return null;
}

//----------------------------------------
private ParseVar getVar(String s) {
	ParseVar v;
	v = new ParseVar(s);
	if (v.isLegal()) 
		return v;
	else
		return null;
}   
ParseVerb 와 ParseVar 클래스는 단어가 인식되었다면 'true'가 되어 객체들을 반환한다.

public class ParseVerb extends ParseObject {
	static public final int PRINT=100, SORTBY=110, THENBY=120;
	protected Vector args;
	   
	public ParseVerb(String s) {
		args = new Vector();
		s = s.toLowerCase();
		value = -1;
		type = VERB;
		if (s.equals("print")) value = PRINT;
		if (s.equals("sortby")) value = SORTBY;
	}

Reducing the Parsed Stack

    스택에 대한 토큰은 다음과 같은 형식을 갖는다.

Var
Var
Verb
Var
Var
Var
Var
Verb


    우리는 아규먼트들이 동사 객체를 포함할 때 까지 MultVar 클래스로 연속적인 변수들을 포개어서 동시에 토큰에 대한 스택을 줄일 수 있다.

##########1*

스택이 동사로 축소되었을 때, 이 동사와 그것의 아규먼트는 액션 리스트에 있다; 액션이 실행되었을 때 스택이 비게 된다.

    이 전체적인 프로세스는 사용자 인터페이스에서 Go 버튼을 눌렀을 때 실행하는 Command 객체가 있는 Parse 클래스를 생성하여 수행할 수 있다 :

	public void actionPerformed(ActionEvent e) {
		Parser p =  new Parser(tx.getText());
		p.setData(kdata, ptable);
		p.Execute();
	}
파서 자체가 위에서 본 것처럼 토큰들을 축소한다. 그것은 스택에서 여러 가지 토큰의 쌍을 체크하고, 각각 다른 다섯 가지의 경우에 대해 각 쌍을 하나의 쌍으로 축소한다.

	//executes parse of command line
	public void Execute() {
		while(stk.hasMoreElements()) {
			if(topStack(ParseObject.VAR, ParseObject.VAR)) {
				//reduce (Var Var) to Multvar
				ParseVar v = (ParseVar)stk.pop();
				ParseVar v1 = (ParseVar)stk.pop();
				MultVar mv = new MultVar(v1, v);
				stk.push(mv);
			}
	
			//reduce MULTVAR VAR to MULTVAR
			if(topStack(ParseObject.MULTVAR, ParseObject.VAR)) {
				MultVar mv =  new MultVar();
				MultVar mvo = (MultVar)stk.pop();
				ParseVar v = (ParseVar)stk.pop();
				mv.add(v);
				Vector mvec = mvo.getVector();
				for (int i = 0; i< mvec.size(); i++)
				mv.add((ParseVar)mvec.elementAt(i));
				stk.push(mv);
			}
			
			//reduce (Multvar Var) to Multvar
			if(topStack(ParseObject.VAR, ParseObject.MULTVAR)) {
				ParseVar v = (ParseVar)stk.pop();
				MultVar mv = (MultVar)stk.pop();
				mv.add(v);
				stk.push(mv);
			}
			
			//reduce Verb Var to Verb containing vars
			if (topStack(ParseObject.VAR, ParseObject.VERB)) {
				addArgsToVerb();
			}
			
			//reduce Verb MultVar to Verb containing vars
			if (topStack(ParseObject.MULTVAR, ParseObject.VERB)) {
				addArgsToVerb();
			}
			
			//move top verb to action list
			if(stk.top().getType() == ParseObject.VERB) {
				actionList.addElement(stk.pop());
			}
			
		}//while
		
		//now execute the verbs
		for (int i =0; i< actionList.size() ; i++) {
			Verb v = (Verb)actionList.elementAt(i);
			v.setData(data, ptable);
			v.Execute();
		}
	}
우리는 또한 Print 와 Sort Verb 클래스들을 Command 객체들을 만들어 열거된 액션 목록을 하나씩 실행한다.

    최종적인 프로그램은 아래 그림과 같다.

        ##########2*

Consequences of the Interpreter Pattern

    프로그램에서 인터프리터를 소개할 때마다 사용자가 언어에서 명령들을 입력하는 프로그램을 위해 간단한 방법을 제공할 필요가 있다. 앞에서 언급했던 것처럼 Macro를 기록하는 방법이 있고 위의 프로그램에서처럼 텍스트 필드를 만들어 편집할 수 있게 하는 방법이 있다.

    그러나 언어와 그에 동반된 문법을 수용하는 것은 또한 잘못 입력된 항목이나 문법적으로 잘못 놓여진 원소에 대한 꾀나 광대한 에러 체크를 요하게 된다. 사용자가 이런 에러들을 인식하기 위한 좀더 효율적인 방법은 설계하고 구현하기가 쉽지 않다.

    위의 인터프리터 예제에서는 유일한 에러 핸들링은 인식되지 않는 키워드는  ParseObject로 변환되지 않는다는 것이고 스택에 추가하지 않는다는 것이다. 그러므로, 아무일도 발생하지 않는데 결과 스택 순서가 성공적으로 해석될 수 없기 때문이다.

    또, 라디오 버튼이나 명령버튼 과 리스트 박스의 사용자 인터페이스에서 자동으로 언어가 생성되는 것을 고려해야 한다. 반면에 그러한 인터페이스를 사용하여 언어에 대한 필요성이 없어지는 것처럼 보일 수 있지만 같은 시퀀스의 요구나 계산은 여전히 적용된다. 우리가 순차적인 연산의 순서를 정하는 방법을 가지고 있어야만 할 때 그 언어가 사용자 인터페이스로부터 생성된다 할 지라도 언어는 그렇게 하는 좋은 방법이다.

    인터프리터 패턴은 일단 일반적인 파싱과 리덕션 도구들을 만들었으면  쉽게 확장하거나 문법을 수정할 수 있는 이점을 가지고 있다.일단 기초가 구성되면 쉽게 새로운 동사나 변수를 추가할 수 도 있다. 

    Parse 클래스에서 보았던 해석 구조에서 단지 6가지 경우만 고려했고 문장 자체가 간단했다. 우리가 좀더 확장을 할 때 디자인 패턴은 그것들 각각에 대한 클래스를 생성할 것을 제안했다. 이것은 다시 언어가 쉽게 확장될 수 있게 하지만 유사한 작은 클래스들의 양을 증가시키는 단점을 가지고 있다.

    마지막으로 문법이 더욱 복잡해져 감에 따라 프로그램을 유지하는 것이 어려워 질 수 있다.

    반면에 인터프리터들은 일반적인 프로그래밍 문제들을 푸는데 있어서 공통적인 것은 아니다. 다음 장에 나오는 Iterator 패턴이 사용할 수 있는 가장 공통적인 것이다.

'Development > 패턴자료' 카테고리의 다른 글

[펌] The Mediator Pattern  (0) 2011.08.13
[펌] The Iterator Pattern  (0) 2011.08.13
[펌] The History of Design Patterns  (0) 2011.08.13
[펌] The Flyweight Pattern  (0) 2011.08.13
[펌] The Factory Pattern  (0) 2011.08.13
안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,

The History of Design Patterns

소프트웨어에 대한 디자인 패턴의 아이디어는 건축 분야에서 나왔다. 1977년과 1979년에 건축가인 크리스토퍼 알렉산더(Christopher Alexander)는 “A Pattern Language:Towns, Buildings, Construction”라는 제목의 책과 “The Timeless Way of Building”이라는 제목의 책 두 권을 발표했다.  이 두 권의 책이 담고 있는 기본 아이디어는 건축물의 설계에 빈번하게 발생하는 동일 설계 내용이 있으며 따라서, 이런 것들을 하나의 패턴으로 보고 다른 건축물 설계에 재사용하는 것이 여러 가지 면에서 이득을 가져 다 준다는 것이다.  두 권의 책이 담고 있는 이런 기본 아이디어는 건축 영역을 벗어난 소프트웨어와  같은 다른 영역에서도 적용할 수 있었다. 

1987년 워드 커닝험(Ward Cunningham)과 켄트 벡(Kent Beck)은 알렉산더의 아이디어를 사용해서 사용자 인터페이스(User interface)에 대한 다섯 가지의 패턴을 만들었다. 그리고 이 내용은 “Using Pattern Languages for Object-Oriented Programs”라는 제목으로 정리되어 객체 지향에 관한 세계적인 컨퍼런스(conference)인 OOPSLA-87(Object-Oriented Programming, Systems, Languages & Applications .. 87)에 논문으로 발표되어 디자인 패턴에 대해서 학계에 공식적으로 알리는 계기가 되었다. 

1990년대 초에는 에릭 감마(Erich Gamma), 리차드 헬름(Richard Helm), 존 블리자이드(John Vlissides), 랄프 존슨(Ralph Johnson)이 90년대 가장 영향을 주었던 컴퓨터 책의 한 가지인 “Design Patterns: Elements of Reusable Object-Oriented Software”이란 책 제작을 시작 했다. 이 책은 1994년에 발표되어 디자인 패턴에 대한 아이디어를 널리 알리는 계기를 만들었으며, 이 책은 책의 제목이 긴 이유로 인해서 'GoF(Gang of Four) book'라는 별칭으로 불리고 있다. 

Defining Desing Patterns

  • 디자인 패턴은 개개의 클래스, 인스턴스 , 컴포넌트들의 상위 단계인 추상 개념을 확인하고 특정 짓는다.(Gamma, et al., 1993)
  • 디자인 패턴은 소프트웨어 개발의 범위에서 어떤 일을 어떻게 완수할 수 있는 가에 대한 규칙들로 이루어 진다.(Pree, 1994)
  • 디자인 패턴은 반복되는 구조의 디자인 주제의 재사용성에 좀더 초점을 둔다.(Coplien & Schmidt, 1995)

Definition: 자주 발생하는 문제들에 대한 "재사용 가능한 해결책"이다.

'Development > 패턴자료' 카테고리의 다른 글

[펌] The Iterator Pattern  (0) 2011.08.13
[펌] The Interpreter Pattern  (0) 2011.08.13
[펌] The Flyweight Pattern  (0) 2011.08.13
[펌] The Factory Pattern  (0) 2011.08.13
[펌] The Facade Pattern  (0) 2011.08.13
안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,

The Flyweight Pattern

    데이터를 표현하기 위한 작은 클래스의 인스턴스들을 많이 생성해야 할 경우가 있다.
종종, 만약 몇개의  파라미터에 대한 것만 제외하고 기본적으로 같은 같은 인스턴스들이다라는 것을 인식할 수 있다면 인스턴스를 만들 필요가 있는 서로 다른 클래스들의 수를 크게 줄일 수 있다. 만약 그러한 변수들을 클래스 인스턴스의 외부로 옮길 수 있고 메소드가 호출하는 부분에서 그것들을 건내 줄수 있다면 나뉘어진 인스턴스의 수는 크게 줄일 수 있다. 

    Flyweight 디자인 패턴은 그러한 클래스들을 조절하는 거에 대해서 방법을 제시한다. 인스턴스가 유일하도록 인스턴스의 고유한 데이터를 참조하고, 아규먼트로 전달되는 고유하지 않는 데이터를 참조하게 한다. Flyweight는 각각의 문자들이나 스크린사의 아이콘과 같은 작고, 자잘 자잘한 클래스들에 대해  어울린다.  예를 들어, 사람이나 데이터에 대해 각각 표현을 하는 폴더 윈도우에서 화면상의 아이콘의 시리즈를 추출한다고 하면 ,사람의 이름이나 화면상의 아이콘의 위치 를 기억하는 것에 대해 각각의 클래스 인스턴스를 만드는 것은 의미가 없다. 전형적으로 그러한 아이콘들은 몇몇 유사한 이미지들중의 하나이고,추출되는 위치는 어떤 경우에서든지 윈도우의 크기에 따라 동적으로 계산되어 진다. 

    디자인 패턴에서의 다른 예는 폰트에서 각각의 문자는 문자 클래스의 하나의 인스턴스로 표현되어지지만, 글자들이 스크린 상에서 그려지는 위치는 문자의 각각의 외양에 대한 것 보다는 각 문자의 하나의 인스턴스만 필요하게 하는 외부 데이터로서 유지된다.       

Discussion

    Flyweight들은 클래스의 인스턴스를 공유할 수 있다. 그것은 Singleton 클래스처럼 보일 수도 있지만 사실 적은 수의 인스턴스들이 있을 수 있다. 할당된 인스턴스의 수는 클래스 인스턴스가 필요할 때 결정되어야 하며, 이것은 Flyweight 클래스로 이루어 진다. 이 팩토리 클래스는 특별한 인스턴스가 생성되었는지 아닌지를 추적해야 하기 때문에 보통 싱글턴이다. 특수한 인스턴스가 이미 생성되어 있으면 새로운 인스턴스나 레퍼런스를 반환한다.    
    프로그램의 일부분이 Flyweight를 사용할 후보인지를 결정하기 위하여 클래스로부터 몇몇 데이터를 제거하고 그것을 외부의 변수로 만들어도 가능한가를 고려해 본다. 프로그램을 유지하는데 필요한 다른 클래스 인스턴스의 수를 크게 줄이는게 가능하도록 한다면 Flyweight 가 도울 수 있는 경우이다.  

Example Code

    우리가 하나의 조직에서 각 사람에 대해서 작은 폴더 아이콘 아래 이름을 가지는 작은 폴더 아이콘을 그리고 싶다고 해보자. 만약 이것이 큰 조직이라면 그러한 아이콘의 수는 커지게 되겠지만, 그것들은 실제적으로 작은 그래픽 이미지들이다. 우리가 두개의 아이콘을 가지고 있다고 하더라도 하는 선택되지 않은 거에 대한 것이고 다른 하나는 선택것에 대한 아이콘이다. 각 사람에 대한 아이콘 객체와 그 것들 자체의 좌표, 이름과 선택된 상태를 갖는 시스템은 자원의 낭비이다. 

    대신, 우리는 선택되거나 선택되지 않은 것을 나타내는 drawing 클래스를 반환하는 FolderFactory를 생성하겠지만, 이미 생성된 각각의 인스턴스에 대하여 추가적인  인스턴스 생성은 하지 않을 것이다. 
	
class FolderFactory {
	Folder unSelected, Selected;
	public FolderFactory() {
		Color brown = new Color(0x5f5f1c);
		Selected =  new Folder(brown);
		unSelected = new Folder(Color.yellow);
	}
	//-------------------------------  
	public Folder getFolder(boolean isSelected) {
		if (isSelected) 
			return Selected;
		else
			return unSelected;
	}
}
    더 이상의 인스턴스들이 존재할 수 있는 경우들에 대해서, factory는 이미 생성되었던 인스턴스에 대한 표를 유지할 수 있고 오직 테이블에서 이미 존재하지 않을 때에만 새로운 인스턴스를 생성한다. 

    그러나, Flyweight를 사용하는 것에 대한 유일한 것은 우리가 폴더 아이콘을 그릴 때 좌표와 이름을 넘겨주는 것이다. 이러한 좌표들은 폴더 객체를 공유하도록 하는 외부의 데이터이고 이 경우에 오직 두 개의 인스턴스만을 생성한다. 완성된 폴더 클래스는 아래의 코드 처럼 간단하게 폴더 인스턴스를 하나의 배경색과 다른  정한 폴더를 그릴 수 있는 public Draw 메소드를 가지고 생성할 수 있다. 
	
class Folder extends JPanel {
	private Color color;
	final int W = 50, H = 30;
	public Folder(Color c) {
	   color = c;
	}
	//-------------------------------  
	public void Draw(Graphics g, int tx, int ty, String name) {
		g.setColor(Color.black);            //outline
		g.drawRect(tx, ty, W, H);
		g.drawString(name, tx, ty + H+15);  //title
		
		g.setColor(color);                  //fill rectangle 
		g.fillRect(tx+1, ty+1, W-1, H-1);
		
		g.setColor(Color.lightGray);        //bend line   
		g.drawLine(tx+1, ty+H-5, tx+W-1, ty+H-5);
		
		g.setColor(Color.black);            //shadow lines
		g.drawLine(tx, ty+H+1, tx+W-1, ty+H+1);
		g.drawLine(tx+W+1, ty, tx+W+1, ty+H);
		
		g.setColor(Color.white);            //highlight lines
		g.drawLine(tx+1, ty+1, tx+W-1, ty+1);
		g.drawLine(tx+1, ty+1, tx+1, ty+H-1);  
	}
}
이와 같은 Flyweight 클래스를 사용하기 위해서, 메인 프로그램은 각 폴더의 위치를 계산해야 하고, 그리고 나서 좌표를 폴더 인스턴스에 건내 주어야 한다. 이것은 실제로 보다 일반적인 방법인데, 왜냐면 윈도우의 크기에 의존하는 서로 다른 레이아웃을 필요로 하기 때문이다. 대신, 우리는 paint 루틴 동안 동적으로 계산한다. 

    여기서 우리는 우리가 외부에서 폴더들의 벡터나 배열을 생성할 수 있고 각 폴더를 그리기 위해 배열을 통하여 간단히 읽어 들일 수 있다는 것을 주목해야 한다. 그러한 배열은 다른 인스턴스들의 시리즈 처럼 낭비가 아니다. 왜냐하면 그것은 실제로는 두 개의 폴더 인스턴스 중의 하나를 참조하는 배열이기 때문이다. 그러나, ㅇ뤼는 하나의 폴더가 선택된것으로 나타내고자 하고, 폴더의 상태를 동적으로 변경시키고자 하기 때문에, 우리는 바로 각 시간에 정확하게 인스턴스를 줄 수 있는 FolderFactory를 사용한다. 
	
public void paint(Graphics g) {
	Folder f;
	String name;
	
	int j = 0;      //count number in row
	int row = Top;  //start in upper left
	int x = Left;
	
	//go through all the names and folders
	for (int i = 0; i< names.size(); i++) {
		name = (String)names.elementAt(i);
		if(name.equals(selectedName))
			f = fact.getFolder(true);
		else
			f = fact.getFolder(false);
			
		//have that folder draw itself at this spot
		f.Draw(g, x, row, name);
		
		x = x + HSpace;          //change to next posn
		j++;
		
		if (j >= HCount) {        //reset for next row		
			j = 0;         
			row += VSpace;
			x = Left;
		}
	}
}

Selecting A Folder

    우리가  선택된 상태거나 선택되지 않은 상태와 관련된 두개의 폴더 인스턴스를 갖기 때문에, 우리는 그 것들 위로 마우스를 움직여서 폴더가 선택되었는지를 알 수 있기를 원한다. 위의 paint 루틴에서 우리는 간단히 선택된 폴더의 이름을 기억하고 factory가 선택된 폴더로 반환하기를 요청한다. 폴더들이 개별적인 인스턴스들을 갖지 않기 때문에 우리는 각 폴더 인스턴스 내에서 마우스의 움직임을 감지할 수 없다. 사실, 하나의 폴더내에서 감지 할 수 있다 하더라도, 우리는 선택되지 않는 폴더의 인스턴스들에게 알려줄 방법을 가지고 있어야만 한다. 

    대신, 우리는 윈도우 단계에서 마우스의 움직임을 체크하고 만ㅇ약 마우스가 사각형 내에서 발견된다면 우리는 선택된 이름과 대응되는 이름을 만들어 낼 수 있다. 이것이 우리가 다시 그리고 필요한 곳에서 선택된 폴더 인스턴스를 생성할 때 체크할 수 있도록 허용한다.  

public void mouseMoved(MouseEvent e) {

	int j = 0;      //count number in row
	int row = Top;  //start in upper left
	int x = Left;
	
	//go through all the names and folders
	for (int i = 0; i< names.size(); i++) {
		//see if this folder contains the mouse
		Rectangle r = new Rectangle(x,row,W,H);
		if (r.contains(e.getX(), e.getY())) {
			 selectedName=(String)names.elementAt(i);
			 repaint();
		}
		x = x + HSpace;          //change to next posn
		j++;
		if (j >= HCount) {        //reset for next row
			j = 0;         
			row += VSpace;
			x = Left;
		}
	}

}
10개의 이름이 있는 폴더를 나타내는 프로그램은 아래와 같다.

    ##########0*

Flyweight Uses in Java

Flyweights는 자바에서 어프리케이션 단계에서는 자주 사용되지 않는다. 그것들은 자바보다 저수준의 단계에서 사용되는 시스템의 자원을 관리하는 기술이다. 그러나, 필요할 때 사용할 수 있기 위해서 이 기술이 존재하는 것을 알아두는 것은 유용한다. 

    Flyweights 가 사용되는 곳은 테이블과 리스트를 위해 사용되는 cell renderer 코드이다. 보통 셀 렌더러는 단지 JLabel이지만 labels의 두 세가지 타입이 있을 수 있거나 다른 색이나 폰트에 대해 렌더링을 할 수 있다. 그러나 테이블이나 리스트에서 셀보다는 렌더러가 더 적다. 

    자바언어에 있는 몇몇 객체들은 Flyweight로서 포장되어 구현될 수 있다. 예를 들어 동일한 문자들을 갖는 문자열의 인스턴스가 두개가 있다면 같은 저장 위치를 참조하게 할 수 있다. 마찬가지로 같은 값을 갖는 Interger 나 Float 객체는 Flyweight로 구현될 수 있다. Flyweights 의 부재를 증명하기 위해 다음 코드를 실행시켜 보자. 

Integer five = new Integer(5);          
Integer myfive = new Integer(5);        
System.out.println(five==myfive);
                                        
//String fred = new String("fred");     
//String fred1 = new String("fred");    
                                        
String fred ="fred";                    
String fred1 ="fred";                   
                                        
System.out.println(fred==fred1);        
    두 가지 모두 "false" 를 출력할 것이다. 그러나 "==" 연산자를 이용하여 Flyweight의 두 개의 같은 인스턴스들의 문제를 처리할 때 쉽게 결정할 수 있다는 것은 유용하다. 그것은 사실 같은가에 대한 비교가 아니라 레퍼런스(메모리 주소)를 비교하는 것이다. 

'Development > 패턴자료' 카테고리의 다른 글

[펌] The Interpreter Pattern  (0) 2011.08.13
[펌] The History of Design Patterns  (0) 2011.08.13
[펌] The Factory Pattern  (0) 2011.08.13
[펌] The Facade Pattern  (0) 2011.08.13
[펌] The Decorator Pattern  (0) 2011.08.13
안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,

The Factory Pattern

객체 지향 프로그램에서 계속해서 볼 수 있는 패턴 중의 하나는 Factory 패턴 또는 클래스이다. Factory 패턴은 제공된 데이터에 의존하는 여러 가능한 클래스 중의 하나를 반환하는 것이다. 일반적으로 반환되는 모든 클래스는 공통적인 부모 클래스와 공통적인 메소들을 가지지만, 그것들은  각각은 다른 일을 하고, 다른 종류의 데이터를 위해 최적화 되어진다.

How a Factory Works

아래의 Factory 다이어 그램은 Factory 패턴의 이해를 돕기 위한 것이다.

##########0*

그림에서 x는 상위 클래스이고 xy와 xz는 x로 부터 파생되었다. Factory는 입력된 인자에 의존하여 하위 클래스를 반환을 결정하는 클래스이다. 오른쪽에서 어떤 abc라는 값이 주어질 때의 getClass 메소드를 정의하였고, 이 메소드는 클래스 x의 인스턴스를 반환한다. 모두 같은 메소드를 가지고 있기 때문에 반환되는 getClass가 어떤 값을 반환하는가는 문제가 되지 않지만, 구현은 다르다. 어떻게 반환할 것인가를 결정하는 것은 전반적으로 Factroy에 의해서 이다. 

Sample Code

    Factory 클래스를 사용할 수 있는 간단한 경우를 고려해 보자.
우리가 어떤 가입 양식을 가지고 있고, 가입자가 그의 이름을 "이름 성" 또는 "성, 이름" 모두 가능하게 하고자 한다고 하자. 우리는 이름과 성 사이의 컴마(,)가 있는지 없는지를 고려하여 항상 이름의 순서를  결정할 수 있도록 좀더 가정을 단순화 시킬 것이다. 
class Namer {

	// a simple class to take a string apart into two names
	
	protected String last; 	//store last name here
	protected String first;	//store first name here
	
	public String getFirst() {
		return first;	//return first name
	}
	public String getLast() {
		return last;	//return last name
	}	
}

상위 클래스에는 두 개의 메소드를 제외하고는 실제적으로 아무일 도 하지 않는다. 우리는 first last 에 성과 이름을 분리하여 저장할 것이다. 

The Two Derived Classes

우리는 간단하게 생성자를 이용하여 이름을 두개의 부분으로 나누는 클래스를 파생시킬 수 있다. FirstFirst 클래스에서는 공백문자 이전의 모든 것들은 이름(last name)이라 가정한다.
class FirstFirst extends Namer { 		//split first last
	public FirstFirst(String s) {
		int i = s.lastIndexOf(" "); 	//find sep space
		if( i>0) {
			//left is first name 
			first = s.substring(0,i).trim();
			//right is last name
			last = s.substring(i+1).trim();
		}
		else {
			first = ""; 	// put all in last name
			last = s;	// if no space
		}
	}
}
그리고, LastFirst 클래스에서는 컴마(,)를 구분자로 하여 컴마 이전의 것은 이름(last)에 이후는 성(fist)에 저장할 것이다. 만약 컴마가 존재하지 않는다면 이름에 저장할 것이다.
class LastFirst extends Namer {	//split last, first
	public LastFirst(String s) {
		int i = s.indexOf(",");	//find comma
		if (i>0) {
			//left is last name 
			last = s.substring(0,i).trim();
			//right is first name
			first = s.substring(i+1).trim();
		}
		else {
			last = s; 	//put all in last name
			first = "";	//if no comma
		}
	}
}

Building the Factory

Factory 클래스는 아주 간단하다. 우리는 컴마의 존재 유무를 검사한 후 상속 받은 두 클래스중 하나의 인스턴스를 반환할 것 이다.
class NameFactory {
//returns all instance of LastFirst or FirstFirst
//depending on whether a comma is found

	public Namer getNamer(String entry) {
		int i = entry.indexOf(","); //comma determines name order
		if (i>0)
			return new LastFirst(entry); 	//return one class
		else
			return new FirstFirst(entry);	//or the other
	}
}
Using the Factory
어떻게 Factory를 사용하는지 알아 보자.
간단하게 성과 이름을 입력할 수 있는 입력창과 이름과 성을 분리하도록 명령을 내리는 버튼, 그에 따라 각각 분리하여 나타낼 수 있는 두 개의 결과창을 사용자 인터페이스로 구현해 보자. 아래의 그림은 구현은 이러한 일련의 작업을 구현해 놓은 애플릿이다.

            ##########1*

이름을 입력하고 Compute 버튼을 누르면 이름과 성이 분리되어 각각의 결과를 얻을 수 있을 수 있을 것이다.
이 프로그램을 작성하기 위해 우리는 생성자에 Factory 클래스를 생성하여야 한다.

 NameFactory nfactory = new NameFactory();

그리고 나서 버튼 이벤트가 발생하였을 때 이름과 성을 분리할 수 있는 getNamer메소드를 호출 할 compteName이라는 메소드를 구현해야 한다. 

	private void computeName () {
		//send the text to the factory and get a class back
		namer = nfactory.getNamer(entryField.getText());
		
		//compute the first and last names
		//using the returned class
		txFirstName.setText(namer.getFirst());
		txLastName.setText(namer.getLast());
	}

When to Use a Factory Pattern

다음과 같은 상황에서 Factory 패턴을 고려할 수 있다.
  • 객체들에 대한 클래스의 종류를 예상 할 수 없을 때
  • 어떤 객체들이 Factory Pattern을 생성하는지를 특정 짓기 위하여 클래스의 하위 클래스를 사용할 때
  • 생성된 클래스로터 얻어진 정보 제한하고자 할 때
Factory 패턴에 대한 여러가지 변형이 있다.
  1. 기본 클래스는 추상(abstract)이고 그 패턴은 완전하게 작동하는 클래스를 반환하여야 한다
  2. 기본 클래스는 디폴트 메소드들을 포함하고 오직 하위 클래스들이 .....
##########2*

'Development > 패턴자료' 카테고리의 다른 글

[펌] The History of Design Patterns  (0) 2011.08.13
[펌] The Flyweight Pattern  (0) 2011.08.13
[펌] The Facade Pattern  (0) 2011.08.13
[펌] The Decorator Pattern  (0) 2011.08.13
[펌] The Composite Pattern  (0) 2011.08.13
안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,