레이블이 Java designPattern인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Java designPattern인 게시물을 표시합니다. 모든 게시물 표시

2015년 11월 16일 월요일

클래스 디자인 패턴 0.2 Design Pattern 설계 구상(문서 편집기 설계)

ps. 글 작성시 참고한 사항
   알라딘 책 설명
http://www.aladin.co.kr/shop/wproduct.aspx?ISBN=8945072144?partner=googlek&EKAMS=realclick.757.2659.141.1406787890725.2242158&trackingDays=1&gclid=CIKkusPPgskCFYGbvAod4U8NPg



1. 서두
   만약 문서 편집기를 구현해야 한다면 어떤 방식으로
     설계를 고민해야 할까.
   GoF 에서는 Lexi 라는 문서편집기를 예제로 설명함.
     여기서도 이를 따름
   아래 내용은 0장 에서 설명한 내용대로 구현을
      고민한 결과임.

2. 문서 편집기가 꼭 가져야 할 기능
  1) 문서 구조
  2) 서식 설정
  3) 사용자 인터페이스 꾸미기
  4) 여러 룩앤필 표준 지원
  5) 여러 윈도우 시스템 지원
  6) 사용자 조작(command 의 이동 관련)
  7) 철자 검사 및 붙임표 처리


3. 구현 방향은?
  1) 문서 구조
     - 문자, 그림, 은 각각 객체에 담겨야 되고 이 객체는
        차별없이 편집기 안의 행, 열에 담겨야 된다.
     - 이를 위해서는 행, 열 에 대한 정의가 있어야 되고
     - 객체는 모두 인터페이스나 추상 클래스로 통합되어야 한다.
   결론 : 문서 구조를 위해서는 복잡채 패턴(Composite) 이 적용가능함

  2) 서식 설정
     - 문서는 각각 서식을 지닐 수 있으나 서식은 문서 구조와는
         독립적임
   결론 : 전략(Stratege ) 패턴의 적용을 고려가능함

  3) 사용자 인터페이스 장식
     - 화면안의 사각형 및 메뉴를 보여줘야 되는데 이는
        프로그램 실행히 항상 보여줘야 되므로
        문서 구조가 이를 상속받도록 하되 해당
        내용이 다치지 않도록 장식자 패턴을 고려할 만함
    결론 : 장식자 패턴(Decorator)을 이용

4) 다양한 룩앤룩 표준 지원
     - 버튼, 메뉴, 스크롤바 등의 객체를 같은 방식으로
        지원 할 수 있도록 추상 팩토리 패턴을 이용할 수 있다.
    결론 : 추상 팩토리(Abstract Factory) 패턴을 이용

5) 다중 윈도우 시스템 지원
     - 다양한 룩앤룩 표준은 시스템마다 다를수 있으나
         웹에 보여지는 기능은 비슷하기 때문에
         이를 셋팅하여 해당 OS 마다 차별 지원되도록 고려해야함
    결론 : 가교 (Bridge) 패턴을 이용

  6) 사용자 조작
     - 각 메뉴별, 사용자 입력에 대한 기능이 있을것이며,
        또한 뒤로 가기 버튼이나 기능을 통해 이전 상태로
        돌리는 기능을 포함하여야 함
    결론 : 명령(Command) 패턴을 사용 가능함

  7) 철자 검사 및 붙임표 연결
     - 글을 작성한 문서 전체를 스캔하여 문자, 단어를 구별하고
        이를 사전에 대입할수 있어야 함 이를 위해서
        파일 또는 객체에 접근 후 순회 하고 이를 가공해
        작업할 수 있어야 함
    결론 : 방문자(Visitor) 패턴을 활용가능


   


3.

2015년 11월 9일 월요일

클래스 디자인 패턴 0. Design Pattern 서두 (GoF 디자인 패턴 1장 내용)

ps. 글 작성시 참고한 사항
   GoF 디자인 패턴 의 서두 부분
https://ko.wikipedia.org/wiki/%EB%94%94%EC%9E%90%EC%9D%B8_%ED%8C%A8%ED%84%B4_(%EC%B1%85)

   알라딘 책 설명
http://www.aladin.co.kr/shop/wproduct.aspx?ISBN=8945072144?partner=googlek&EKAMS=realclick.757.2659.141.1406787890725.2242158&trackingDays=1&gclid=CIKkusPPgskCFYGbvAod4U8NPg


1. 의미
    프로그램 작성시 재사용이 가능한 경험을 정리 한것


2. 이론
  1) 디자인 패턴이 포함해야 할 사항들
    디자인 패턴의 이름
    특정 상황에서 자주 발생하는 문제 정의
    이를 해결하는 방법 및 과정
    해결 결과

  2) 작성 목적
    디자인 패턴은 생성, 구조, 행동 의 목적을 지니고 작성된다.

  3) 프로그램
    프로그램은 객체(또는 객체들)로 만들어진다.

  4) 객체
    객체는 데이터와 프로시저(method, 기능) 의 집합이며
      이 객체는 사용자 요청에 의해 데이터 내용이나 프로시저를
      사용자에게 제공하게 된다.

  5) 프로그램 설계시 전반적인 측면에서 고려할 사항
    캡슐화
    크기 정하기
    종속성
    유연성
    성능
    변경 가능성 혹은 진화
    재사용성

  6) 타입
    특정 인터페이스를 나타낼 때 사용함
    A 타입은 A interface에 정의한 method 를 다 처리가능함을 나타냄
    (1) 서브타입 : 다른 인터페이스를 포함하는 인터페이스
    (2) 슈퍼타입 : 서브타입의 반대개념

  7) 동적 바인딩
     요청 + 요청에 대한 처리 객체를 뭘로 연결할지
      실행하는 시점에서 결정하는 것
      이게 되는걸 가지고 다형성 이라는 표현을 쓴다.

  8) 클래스
     구현을 명세(정의) 해 놓은것
     객체의 내부 데이터, 표현 방법, 연산을 정의함

  9) 객체
     클래스를 인스턴스로 만듬으로서 생성됨
     = 클래스의 인스턴스
      객체의 내부 데이터에 대해서 메모리내 공간을 할당하고
      상태 및 연산을 처리할 수 있게 함

  10) 클래스 상속
    다른 기존 클래스를 기반으로하는 클래스를 생성가능함
    (1) 부모클래스 : 기존 클래스
    (2) 서브클래스 : 기존 클래스를 기반으로 생성한 클래스

  11) 추상 클래스
    일부는 interface 일부는 구현함
      다만 미구현 부분이 있기 때문에 인스턴스 생성이
      불가한 클래스
   
  12) 구체 클래스 : 추상 클래스의 반대개념

  13) 오버라이드
    서브 클래스에서 상속받은 연산을 재정의 하는 경우
      이를 오버라이드 라고 함

  14) 클래스 상속과 인터페이스 상속
    (1) 클래스 상속
      미리 정의된 객체를 바탕으로 작성
      클래스를 그대로 쓰고자 할때 좋다.
      재구현을 할 필요가 없다.
      부모 클래스 변경시 작식 클래스도 같이 변경된다.
    (2) 인터페이스 상속
      어떤 객체가 다른 객체 대신에 사용할 수 있는지 지정
      기능이 다른 경우에도 호환이 되야 되는경우 사용
      재구현을 해야되지만 변경에 대해 클래스 상속보다 자유롭다.

  15) 내가 사용해야 할 디자인 패턴을 고르는 방법
    (1) 패턴이 문제를 해결하는 과정을 파악
    (2) 패턴의 의도를 확인
    (3) 패턴들간 관련성을 확인
    (4) 비슷한 패턴을 학습
    (5) 내가 작성중인 프로그램 재설계의 원인은?
    (6) 이후 필요한 변경(가변성)은 무엇인지 확인


3. 패턴의 이름, 의미, 분류
-----------생성에 관련된 패턴 Creational Patterns ------------
  1) 추상 팩토리 Abstract Factory
     구체적인 클래스 지정하지 않고 작성,
       이때 관련성있는 객체를 묶거나 서로 독립적이긴 하나
       업무적으로 필요한 객체를 묶도록 인터페이스를 제공하는 패턴
http://magmajjame.blogspot.kr/2015/10/15-abstract-factory-pattern.html

  2) 빌더 Builder
     객체 표현과 생성을 분리하여
       같은 생성 방법으로 때에 따라 다른 객체를
       생성하도록 제어하는 패턴
http://magmajjame.blogspot.kr/2015/10/14-builder.html

  3) 팩토리 메서드 Factory Method
    인터페이스는 미리 정리해 놓되 실제 구현 클래스는
      서브 클래스에서 결정하도록 생성을 위임하는 방법
http://magmajjame.blogspot.kr/2015/08/4-factory-pattern.html

  4) 원형 Prototype
    객체 생성이 복잡한 경우, 또는 해당 객체의 초기값이
      계속 사용되어야 하는 경우 이를 처음 객체를 생성해 놓고
      이를 복사한 새로운 객체를 프로그램에서
      사용하도록 제어하는 패턴
http://magmajjame.blogspot.kr/2015/10/13-prototype-pattern.html

  5) 단일체 Singleton
     객체를 하나만을 가지고 여러 자원에서 활용해야하는경우
       해당 객체 생성은 1번만 하고 이 인스턴스를
       여러 지점에서 접근할 수 있도록 접촉점을 제공하는 패턴
http://magmajjame.blogspot.kr/2015/08/5-singletone-pattern.html


-----------구조에 관련된 패턴 Structural Patterns ------------
  6) 적응자 Adapter
    다른 호환되지 않는 클래스를 사용자가 원하는
      형태의 객체로 동작하도록 도와주는 패턴
http://magmajjame.blogspot.kr/2015/09/7.html

  7) 가교 Bridge
    구현부의 추상층을 분리하여 각자 독립적이거나 다른
      구현 및 변형이 가능도록 하는 패턴
http://magmajjame.blogspot.kr/2015/10/16-bridge-pattern.html

  8) 복합체 Composite
    객체는 트리 구조로 구성되어 있는 상태에서 사용자가
      단일, 복합 객체 모두 접근할 수 있도록 작성한 패턴
http://magmajjame.blogspot.kr/2015/10/17-composite-pattern.html

  9) 장식자 Decorator
    상황, 용도를 기존 객체에 추가해 다시 만드는 방법으로
      객체에 기능을 확장하는 패턴
http://magmajjame.blogspot.kr/2015/06/3.html

  10) 퍼사드 Facade
    서브 시스템의 자원을 통합한 상위 단위의 인퍼테이스를
      제공하는 패턴
http://magmajjame.blogspot.kr/2015/10/7-2-facade-pattern.html

  11) 플라이급 flyweight
     객체를 공유하여 메모리 소모를 줄이는 패턴
http://magmajjame.blogspot.kr/2015/11/22-flyweight-pattern.html

  12) 프록시 Proxy
     직접 객체로 접근하는것을 방지하고 다른 경로로
       통제된 상태로 해당 객체의 허가된 자원을
       접근할 수 있도록 하는 패턴
http://magmajjame.blogspot.kr/2015/09/11-proxy-pattern.html

-----------행동에 관련된 패턴 Behavioral Patterns ------------
  13) 책임 연쇄 Chain of Responsibility
    요청이 어느 객체에 해당되는지에 대한 판단을 해서
      해당 요청을 처리하는 책임연쇄 기능을 작성하여
      하나의 요청으로 여러 요청을 처리할 수 있게 작성하는 패턴
http://magmajjame.blogspot.kr/2015/10/19-chain-of-responsibility-pattern.html

  14) 명령 Command
    요청을 객체 형태로 캡슐화 하고 몇가지 요청, 로깅, 연산 취소 등의
      기능을 추가함
http://magmajjame.blogspot.kr/2015/09/6-command-pattern.html

  15) 해석자 Interpreter
    정의 언어 표현수단과 이를 해석하여 실행하는 해석기
      정의를 포함한 패턴
http://magmajjame.blogspot.kr/2015/11/23-interpreter-pattern.html

  16) 반복자 Iterator
    어떤 객체의 원소(데이터나 인스턴스집합)을
      순차적으로 접근할 수 있게 작성하는 패턴
http://magmajjame.blogspot.kr/2015/09/9-iterator-pattern.html

  17) 중재자 Mediator
    서로 참조해야 하는 또는 한 집합의 객체를 직접 참조하지
      않도록 중간에 중재자를 정의하여 공유 객체에서
      이를 중재하도록 하는 패턴
http://magmajjame.blogspot.kr/2015/11/20-mediater-pattern.html

  18) 메멘토 Memento
    뒤로가기 기능을 정의하는 패턴
http://magmajjame.blogspot.kr/2015/11/21-memento-pattern.html

  19) 감시자 Observer
    공유해야 할 데이터를 한곳에서 수정하고
      내용을 필요로 하는 다른 여러곳으로 공유하는 패턴
http://magmajjame.blogspot.kr/2015/06/2-observer-pattern.html

  20) 상태 State
    객체 상태 값에 따라 다른 응답이나 요청을 실행할 수 있도록
      설정한 패턴
http://magmajjame.blogspot.kr/2015/09/10-state-pattern.html

  21) 전략 Strategy
    비슷하지만 다른 결과나 동작을 보이는 내용을 묶어서
      요청하는 쪽의 상태나 속성에 따라 다른 인스턴스를
      생성하여 다른 행동을 진행하도록 묶어주는 패턴
http://magmajjame.blogspot.kr/2015/05/1-strategy-pattern.html

  22) 템플릿 메서드 Template Method
    연산에 대한 뼈대를 상위에서 정의하고 구체적인 행동은
      서브 클래스에 미루는 패턴 이는 각 서브클래스에서 재정의하여
      목적에 맞는 행동을 각각 구현하는 방법
http://magmajjame.blogspot.kr/2015/09/8-template-method-pattern.html

  23) 방문자 Visitor
    객체 구조를 이루는 원소에 대해 수행할 연산을 표현하는 패턴
http://magmajjame.blogspot.kr/2015/10/18-visitor-pattern.html

2015년 11월 6일 금요일

클래스 디자인 패턴 23. Interpreter Pattern

1. 개념
  1) 의미
     목적상 변경이 이루어질때 기존 코드를 수정하지 않고
       통역 역할을 하는 프로그램을 만들어 실행하는 패턴입니다.


2. 소스

  1) Node 추상 클래스 작성

public abstract class Node {
    public abstract void parse(Context context) throws ParseException;
}
     

     이를 상속하여 각 문법 하나하나에 대응하는
       객체들을 생성한다.

// <program> ::= program <command list>
public class ProgramNode extends Node {
    private Node commandListNode;
    public void parse(Context context) throws ParseException {
        context.skipToken("program");
        commandListNode = new CommandListNode();
        commandListNode.parse(context);
    }
    public String toString() {
        return "[program " + commandListNode + "]";
    }
}


import java.util.Vector;

// <command list> ::= <command>* end
public class CommandListNode extends Node {
    private Vector list = new Vector();
    public void parse(Context context) throws ParseException {
        while (true) {
            if (context.currentToken() == null) {
                throw new ParseException("Missing 'end'");
            } else if (context.currentToken().equals("end")) {
                context.skipToken("end");
                break;
            } else {
                Node commandNode = new CommandNode();
                commandNode.parse(context);
                list.add(commandNode);
            }
        }
    }
    public String toString() {
        return "" + list;
    }
}


// <command> ::= <repeat command> | <primitive command>
public class CommandNode extends Node {
    private Node node;
    public void parse(Context context) throws ParseException {
        if (context.currentToken().equals("repeat")) {
            node = new RepeatCommandNode();
            node.parse(context);
        } else {
            node = new PrimitiveCommandNode();
            node.parse(context);
        }
    }
    public String toString() {
        return node.toString();
    }
}


// <repeat command> ::= repeat <number> <command list>
public class RepeatCommandNode extends Node {
    private int number;
    private Node commandListNode;
    public void parse(Context context) throws ParseException {
        context.skipToken("repeat");
        number = context.currentNumber();
        context.nextToken();
        commandListNode = new CommandListNode();
        commandListNode.parse(context);
    }
    public String toString() {
        return "[repeat " + number + " " + commandListNode + "]";
    }
}


// <primitive command> ::= go | right | left
public class PrimitiveCommandNode extends Node {
    private String name;
    public void parse(Context context) throws ParseException {
        name = context.currentToken();
        context.skipToken(name);
        if (!name.equals("go") && !name.equals("right") && !name.equals("left")) {
            throw new ParseException(name + " is undefined");
        }
    }
    public String toString() {
        return name;
    }
}


  2) 구문 해석에 필요한 Context 클래스를 작성합니다.
      그 하단은 exception 처리 클래스 입니다.

import java.util.*;

public class Context {
    private StringTokenizer tokenizer;
    private String currentToken;
    public Context(String text) {
        tokenizer = new StringTokenizer(text);
        nextToken();
    }
    public String nextToken() {
        if (tokenizer.hasMoreTokens()) {
            currentToken = tokenizer.nextToken();
        } else {
            currentToken = null;
        }
        return currentToken;
    }
    public String currentToken() {
        return currentToken;
    }
    public void skipToken(String token) throws ParseException {
        if (!token.equals(currentToken)) {
            throw new ParseException("Warning: " + token + " is expected, but " + currentToken + " is found.");
        }
        nextToken();
    }
    public int currentNumber() throws ParseException {
        int number = 0;
        try {
            number = Integer.parseInt(currentToken);
        } catch (NumberFormatException e) {
            throw new ParseException("Warning: " + e);
        }
        return number;
    }
}


public class ParseException extends Exception {
    public ParseException(String msg) {
        super(msg);
    }
}


  3) 테스트 코드 입니다.

import java.util.*;
import java.io.*;

public class Main {
    public static void main(String[] args) {
        try {
            BufferedReader reader = new BufferedReader(new FileReader("program.txt"));
            String text;
            while ((text = reader.readLine()) != null) {
                System.out.println("text = \"" + text + "\"");
                Node node = new ProgramNode();
                node.parse(new Context(text));
                System.out.println("node = " + node);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


결과 : text는 실제 내용 node는 해석 결과
node = [program []]
text = "program go end"
node = [program [go]]
text = "program go right go right go right go right end"
node = [program [go, right, go, right, go, right, go, right]]
text = "program repeat 4 go right end end"
node = [program [[repeat 4 [go, right]]]]
text = "program repeat 4 repeat 3 go right go left end right end end"
node = [program [[repeat 4 [[repeat 3 [go, right, go, left]], right]]]]

3. 다이어그램
http://zetawiki.com/wiki/Interpreter_%ED%8C%A8%ED%84%B4


4. 관련패턴
  1)

클래스 디자인 패턴 22. Flyweight Pattern

1. 개념
  1) 의미
     객체를 가볍게 하기 위한 패턴 메모리를
        좀더 적게 사용하여 객체를 표현하는 방법을 다룹니다.


2. 소스

  1)  txt 파일로 0~9까지 큰 숫자를 작성합니다.



  2) 숫자를 char로 받아서 해당 txt 파일을 확인하는 BigChar  클래스를
     작성합니다.

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

public class BigChar {
    // 문자의 이름
    private char charname;
    // 큰 문자를 표현하는 문자열('#' '.' '\n'의 열)
    private String fontdata;
    // 생성자
    public BigChar(char charname) {
        this.charname = charname;
        try {
        //System.out.println("big"+charname +".txt");
            BufferedReader reader = new BufferedReader(
                new FileReader("big" + charname + ".txt")
            );
            String line;
            StringBuffer buf = new StringBuffer();
            while ((line = reader.readLine()) != null) {
                buf.append(line);
                buf.append("\n");
            }
            reader.close();
            this.fontdata = buf.toString();
        } catch (IOException e) {
            this.fontdata = charname + "?";
        }
    }
    // 큰 문자를 표시한다.
    public void print() {
        System.out.print(fontdata);
    }
}




  3) BigCharFactory 클래스를 작성합니다. 이 클래스는
      bigchar 클래스의 인스턴스를 공유, 및 생성합니다.

package a;
import java.util.Hashtable;

public class BigCharFactory {
    // 이미 만들어진 BigChar의 인스턴스를 관리
    private Hashtable pool = new Hashtable();
    // Singleton 패턴
    private static BigCharFactory singleton = new BigCharFactory();
    // 생성자
    private BigCharFactory() {
    }
    // 유일한 하나의 인스턴스를 얻는다.
    public static BigCharFactory getInstance() {
        return singleton;
    }
    // BigChar의 인스턴스 생성(공유)
    public synchronized BigChar getBigChar(char charname) {
        BigChar bc = (BigChar)pool.get("" + charname);
        if (bc == null) {
            bc = new BigChar(charname); // 여기에서 BigChar의 인스턴스를 생성
            pool.put("" + charname, bc);
        }
        return bc;
    }
}


  4) BigString 클래스에서는 큰 숫자 인스턴스를 이용해
      화면에 뿌려줍니다.

package a;
public class BigString {
    // "큰 문자"의 배열
    private BigChar[] bigchars;
    // 생성자
    public BigString(String string) {
        bigchars = new BigChar[string.length()];
        BigCharFactory factory = BigCharFactory.getInstance();
        for (int i = 0; i < bigchars.length; i++) {
            bigchars[i] = factory.getBigChar(string.charAt(i));
        }
    }
    // 표시
    public void print() {
        for (int i = 0; i < bigchars.length; i++) {
            bigchars[i].print();
        }
    }
}


  5) 테스트 를 위한 클래스입니다.
      argument 에 숫자를 넣으면 결과에 큰숫자를 보여주게끔
     작성합니다.

package a;
public class Main {
    public static void main(String[] args) {
        if (args.length == 0) {
            System.out.println("Usage: java Main digits");
            System.out.println("Example: java Main 1212123");
            System.exit(0);
        }

        BigString bs = new BigString(args[0]);
        bs.print();
    }
}



결과 : (블로그라 좀 찌그러져 보입니다.
....######....
....##......##....
...............##....
.........####.....
...............##.....
....##......##....
....######....
.........................

3. 다이어그램
https://ko.wikipedia.org/wiki/%ED%94%8C%EB%9D%BC%EC%9D%B4%EC%9B%A8%EC%9D%B4%ED%8A%B8_%ED%8C%A8%ED%84%B4


4. 관련패턴
  1) Proxy 패턴
       인스턴스 생성에 시간이 거리는 경우 인스턴스를 공유하여
        처리 속도를 향상시킬 수 있습니다.
       Proxy 에서는 대리인을 내세워 처리 속도를 올립니다.

  2) Composite 패턴
       Composite의 Leaf 역활을 담당할 수 있습니다.

  3) Singleton 패턴
       Singleton 패턴이 Factory 역할을 겸할 수 있습니다.
       하나의 인스턴스를 가지는 Singleton이 여러곳에서
       공유되는 점이 비슷합니다.

2015년 11월 2일 월요일

클래스 디자인 패턴 21. Memento Pattern

1. 개념
  1) 의미
     문서 편집이나 그림 작업시 이전 상태로 되돌리기 위해서는
       이전 상태에 대한 정보가 남아 있어야 됩니다.
     Memento 패턴은 이전 상태를 담아두되
       인스턴트 상태를 나타내는 역할을 부여해 보존, 복원이
       실행되도록 하는 패턴입니다.


2. 소스

  1) Memento class
       현재 상태를 표현하는 클래스입니다.
package game;
import java.util.Vector;

public class Memento {
    int money;                  
         // 돈
    Vector fruits;            
        // 과일
    public int getMoney() {
       // 돈을 얻는다.(narrow interface)
        return money;
    }
    Memento(int money) {
       // 생성자(wide interface)
        this.money = money;
        this.fruits = new Vector();
    }
    void addFruit(String fruit) {
       // 과일을 추가한다.(wide interface)
        fruits.add(fruit);
    }
}


  2) Gamer 클래스
      현재 뭔가 작업이 이루어지는 클래스
      여기에서는 난수 및 시간을 딜레이 하는 기능을 이용해
      일정 시간별로 한 사이클씩 작업이 이루어집니다.

package game;
import java.util.Vector;
import java.util.Iterator;
import java.util.Random;

public class Gamer {
    private int money;                          // 소유한 돈
    private Vector fruits = new Vector();       // 과일
    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) {                            // 1이 나온다...돈이 증가한다.
            money += 100;
            System.out.println("돈이 증가했습니다.");
        } else if (dice == 2) {                     // 2가 나온다...돈이 반으로 준다.
            money /= 2;
            System.out.println("돈이 반으로 줄었습니다.");
        } else if (dice == 6) {                     // 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) {       // undo를 실행한다.
        this.money = memento.money;
        this.fruits = memento.fruits;
    }
    public String toString() {                      // 문자열 표현
        return "[money = " + money + ", fruits = " + fruits + "]";
    }
    private String getFruit() {                     // 과일을 1개 얻는다.
        String prefix = "";
        if (random.nextBoolean()) {
            prefix = "맛있다.";
        }
        return prefix + fruitsname[random.nextInt(fruitsname.length)];
    }
}



  3) 테스트 메인소스를 작성합니다.
      여기서는 작업이 진행된 상태를 사이클이 지날때마다
      보여주는 작업도 같이 합니다.
import game.Memento;
import game.Gamer;

public class Main {
    public static void main(String[] args) {
        Gamer gamer = new Gamer(100);               // 처음의 돈은 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() + "원이 되었습니다.");

            // Memento의 취급 결정
            if (gamer.getMoney() > memento.getMoney()) {
                System.out.println("    (많이 증가했으니 현재의 상태를 보존해두자)");
                memento = gamer.createMemento();
            } else if (gamer.getMoney() < memento.getMoney() / 2) {
                System.out.println("    (많이 줄었으니 이전의 상태로 복귀하자)");
                gamer.restoreMemento(memento);
            }

            // 시간을 기다림
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
            System.out.println("");
        }
    }
}


결과 :
==== 0
현 상태:[money = 100, fruits = []]
아무일도 일어나지 않았습니다.
돈은100원이 되었습니다.

==== 1
현 상태:[money = 100, fruits = []]
아무일도 일어나지 않았습니다.
돈은100원이 되었습니다.

==== 2
현 상태:[money = 100, fruits = []]
아무일도 일어나지 않았습니다.
돈은100원이 되었습니다.

==== 3
현 상태:[money = 100, fruits = []]
아무일도 일어나지 않았습니다.
돈은100원이 되었습니다.
1
==== 4
현 상태:[money = 100, fruits = []]
돈이 증가했습니다.
돈은200원이 되었습니다.
    (많이 증가했으니 현재의 상태를 보존해두자)

==== 5
현 상태:[money = 200, fruits = []]
과일(맛있다.포도)을 받았습닏다.
돈은200원이 되었습니다.

==== 6
현 상태:[money = 200, fruits = [맛있다.포도]]
과일(귤)을 받았습닏다.
돈은200원이 되었습니다.

........


3. 다이어그램

https://en.wikipedia.org/wiki/Memento_pattern

http://scotty83.tistory.com/entry/Memento-Pattern

4. 관련패턴
  1) Command 패턴
      undo, redo를 Memento 패턴을 사용해서 구현가능합니다.

  2) Prototype 패턴
    스냅샷 및 undo 기능을 추가하기 위해 Memento 패턴을
      넣을 수 있습니다.

  3) State 패턴
    상태를 포현한다는 점에선 같으나 State는 클래스 자체가 상태를
      표현하며 Memento는 인스턴스가 상태를 표현합니다.

클래스 디자인 패턴 20. Mediater Pattern

1. 개념
  1) 의미
     공동작업시 공유된 자원을 조정하는 조정자 역할을 하는 패턴
       상호간에 통신이 필요할때 꼭 조정자로 지정한 객체를 통해서
       통신하도록 정보를 집중하는 패턴
       GUI 에서 많이 사용함


2. 소스

  1) Mediattor 인터페이스
      해당 인터페이스는 조정자 역할을 담당할 클래스의
      interface를 정의합니다.
     이를 구현한 구현체는 LoginFrame이라는 클래스로 구현합니다.

public interface Mediator {
    public abstract void createColleagues();
    public abstract void colleagueChanged(Colleague colleague);
}


import java.awt.Frame;
import java.awt.Label;
import java.awt.Color;
import java.awt.CheckboxGroup;
import java.awt.GridLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class LoginFrame extends Frame implements ActionListener, Mediator {
    private ColleagueCheckbox checkGuest;
    private ColleagueCheckbox checkLogin;
    private ColleagueTextField textUser;
    private ColleagueTextField textPass;
    private ColleagueButton buttonOk;
    private ColleagueButton buttonCancel;

    // 생성자
    // Colleague들을 생성해서 배치한 후에 표시를 실행한다.
    public LoginFrame(String title) {
        super(title);
        setBackground(Color.lightGray);
        // 레이아웃 매니저를 사용해서 4*2의 그리드를 만든다.
        setLayout(new GridLayout(4, 2));
        // Colleague들의 생성
        createColleagues();
        // 배치
        add(checkGuest);
        add(checkLogin);
        add(new Label("Username:"));
        add(textUser);
        add(new Label("Password:"));
        add(textPass);
        add(buttonOk);
        add(buttonCancel);
        // 유효/무효의 초기지정
        colleagueChanged(checkGuest);
        // 표시
        pack();
        show();
    }

    // Colleague들을 생성한다.
    public void createColleagues() {
        // 생성
        CheckboxGroup g = new CheckboxGroup();
        checkGuest = new ColleagueCheckbox("Guest", g, true);
        checkLogin = new ColleagueCheckbox("Login", g, false);
        textUser = new ColleagueTextField("", 10);
        textPass = new ColleagueTextField("", 10);
        textPass.setEchoChar('*');
        buttonOk = new ColleagueButton("OK");
        buttonCancel = new ColleagueButton("Cancel");
        // Mediator의 세트
        checkGuest.setMediator(this);
        checkLogin.setMediator(this);
        textUser.setMediator(this);
        textPass.setMediator(this);
        buttonOk.setMediator(this);
        buttonCancel.setMediator(this);
        // Listener의 세트
        checkGuest.addItemListener(checkGuest);
        checkLogin.addItemListener(checkLogin);
        textUser.addTextListener(textUser);
        textPass.addTextListener(textPass);
        buttonOk.addActionListener(this);
        buttonCancel.addActionListener(this);
    }

    // Colleague로부터의 통지로 각 Colleague의 유효/무효를 판정한다.
    public void colleagueChanged(Colleague c) {
        if (c == checkGuest || c == checkLogin) {
            if (checkGuest.getState()) {             // Guest 모드
                textUser.setColleagueEnabled(false);
                textPass.setColleagueEnabled(false);
                buttonOk.setColleagueEnabled(true);
            } else {                                 // Login 모드
                textUser.setColleagueEnabled(true);
                userpassChanged();
            }
        } else if (c == textUser || c == textPass) {
            userpassChanged();
        } else {
            System.out.println("colleagueChanged:unknown colleague = " + c);
        }
    }
    // textUser 또는 textPass의 변경이 있었다.
    // 각 Colleague의 유효/무효를 판정한다.
    private void userpassChanged() {
        if (textUser.getText().length() > 0) {
            textPass.setColleagueEnabled(true);
            if (textPass.getText().length() > 0) {
                buttonOk.setColleagueEnabled(true);
            } else {
                buttonOk.setColleagueEnabled(false);
            }
        } else {
            textPass.setColleagueEnabled(false);
            buttonOk.setColleagueEnabled(false);
        }
    }
    public void actionPerformed(ActionEvent e) {
        System.out.println("" + e);
        System.exit(0);
    }
}


  2) Colleague 인터페이스 및 구현체들
      해당 구현체는 Mediator에게 뭔가 정보를 요구하거나
      정보를 요청하는 사용자들입니다.

public interface Colleague {
    public abstract void setMediator(Mediator mediator);
    public abstract void setColleagueEnabled(boolean enabled);
}


import java.awt.Button;

public class ColleagueButton extends Button implements Colleague {
    private Mediator mediator;
    public ColleagueButton(String caption) {
        super(caption);
    }
    public void setMediator(Mediator mediator) {            // Mediator를 보관
        this.mediator = mediator;
    }
    public void setColleagueEnabled(boolean enabled) {      // Mediator가 유효/무효를 지시한다.
        setEnabled(enabled);
    }
}


import java.awt.Checkbox;
import java.awt.CheckboxGroup;
import java.awt.event.ItemListener;
import java.awt.event.ItemEvent;

public class ColleagueCheckbox extends Checkbox implements ItemListener, Colleague {
    private Mediator mediator;
    public ColleagueCheckbox(String caption, CheckboxGroup group, boolean state) {  // 생성자
        super(caption, group, state);
    }
    public void setMediator(Mediator mediator) {            // Mediator를 보관
        this.mediator = mediator;
    }
    public void setColleagueEnabled(boolean enabled) {      // Mediator가 유효/무효를 지시한다.
        setEnabled(enabled);
    }
    public void itemStateChanged(ItemEvent e) {             // 상태가 변하면 Mediator에게 통지
        mediator.colleagueChanged(this);
    }
}


import java.awt.TextField;
import java.awt.Color;
import java.awt.event.TextListener;
import java.awt.event.TextEvent;

public class ColleagueTextField extends TextField implements TextListener, Colleague {
    private Mediator mediator;
    public ColleagueTextField(String text, int columns) {
        // 생성자
        super(text, columns);
    }
    public void setMediator(Mediator mediator) {
       // Mediator를 보관
        this.mediator = mediator;
    }
    public void setColleagueEnabled(boolean enabled) {
      // Mediator가 유효/무효를 지시한다.
        setEnabled(enabled);
        setBackground(enabled ? Color.white : Color.lightGray);
    }
    public void textValueChanged(TextEvent e) {
     // 문자열이 변하면 Mediator에게 통지
        mediator.colleagueChanged(this);
    }
}


  3) 테스트 메인소스를 작성합니다.
public class Main {
    static public void main(String args[]) {
        new LoginFrame("Mediator Sample");
    }
}



결과 :



3. 다이어그램

https://en.wikipedia.org/wiki/Mediator_pattern

http://copynull.tistory.com/145

4. 관련패턴
  1) Facade 패턴
      Mediatro 가 양방향 중재자로서 인터페이스를 제공했다면
      Facade는 단방향으로 정보를 제공합니다.

  2) Observer 패턴
      통신 자체는 Observer 패턴을 이용해서 수행되는 경우가 있습니다.

2015년 10월 25일 일요일

클래스 디자인 패턴 19. Chain of Responsibility Pattern

1. 개념
  1) 의미
     같은 입력부분의 내용에 따라 출력에 대해 다른 객체
       로 작업하여 보내야 하는 경우, 해당 패턴을 이용해서
       매칭되는 객체를 찾아서 결과를 낼수 있는 방법입니다.
     이때 사슬처럼 쭉 연결해 놓고 적절성 유무를 판단후 다음 객체로
       넘기기 때문에 체인이라 이름붙었습니다.

     좋은점은 입력시에 결과에 어떤 객체를 사용할지 생각하지 않고
       쓸수 있습니다.
      단점은 속도 문제는 있을수 있습니다.


2. 소스

  1) Trouble는 발생한 문제를 표현합니다.

public class Trouble {
    private int number;             // 트러블 번호
    public Trouble(int number) {    // 트러블의 생성
        this.number = number;
    }
    public int getNumber() {        // 트러블 번호를 얻는다.
        return number;
    }
    public String toString() {      // 트러블의 문자열 표현
        return "[Trouble " + number + "]";
    }
}


  2) Support 클래스는 발생한 문제에 대한 해결을 위해
       구현할 클래스입니다.

public abstract class Support {
    private String name;                    // 트러블 해결자의 이름
    private Support next;                   // 떠넘기는 곳
    public Support(String name) {           // 트러블 해결자의 생성
        this.name = name;
    }
    public Support setNext(Support next) {  // 떠넘길 곳을 설정
        this.next = next;
        return next;
    }
    public final void support(Trouble trouble) {  // 트러블 해결 순서
        if (resolve(trouble)) {
            done(trouble);
        } else if (next != null) {
            next.support(trouble);
        } else {
            fail(trouble);
        }
    }
    public String toString() {              // 문자열 표현
        return "[" + name + "]";
    }
    protected abstract boolean resolve(Trouble trouble); // 해결용 메소드
    protected void done(Trouble trouble) {  // 해결
        System.out.println(trouble + " is resolved by " + this + ".");
    }
    protected void fail(Trouble trouble) {  // 미해결
        System.out.println(trouble + " cannot be resolved.");
    }
}


  3) 서포트를 상속하여 연결 대상이 되는 클래스를
       작성합니다.

public class NoSupport extends Support {
    public NoSupport(String name) {
        super(name);
    }
    protected boolean resolve(Trouble trouble) {     // 해결용 메소드
        return false; // 자신은 아무 처리도 하지 않는다.
    }
}


public class LimitSupport extends Support {
    private int limit;                              // 이 번호 미만이면 해결 할수 있다.
    public LimitSupport(String name, int limit) {   // 생성자
        super(name);
        this.limit = limit;
    }
    protected boolean resolve(Trouble trouble) {         // 해결용 메소드
        if (trouble.getNumber() < limit) {
            return true;
        } else {
            return false;
        }
    }
}


public class OddSupport extends Support {
    public OddSupport(String name) {                // 생성자
        super(name);
    }
    protected boolean resolve(Trouble trouble) {    // 해결용 메소드
        if (trouble.getNumber() % 2 == 1) {
            return true;
        } else {
            return false;
        }
    }
}


public class SpecialSupport extends Support {
    private int number;                                 // 이 번호만 해결할 수 있다.
    public SpecialSupport(String name, int number) {    // 생성자
        super(name);
        this.number = number;
    }
    protected boolean resolve(Trouble trouble) {     // 해결용 메소드 
        if (trouble.getNumber() == number) {
            return true;
        } else {
            return false;
        }
    }
}


  4) 테스트 메인소스를 작성합니다.
public class Main {
    public static void main(String[] args) {
        Support alice   = new NoSupport("Alice");
        Support bob     = new LimitSupport("Bob", 100);
        Support charlie = new SpecialSupport("Charlie", 429);
        Support diana   = new LimitSupport("Diana", 200);
        Support elmo    = new OddSupport("Elmo");
        Support fred    = new LimitSupport("Fred", 300);
        // 연쇄의 형성
        alice.setNext(bob).setNext(charlie).setNext(diana).setNext(elmo).setNext(fred);
        // 다양한 트러블 발생
        for (int i = 0; i < 500; i += 33) {
            alice.support(new Trouble(i));
        }
    }
}


결과 :
[Trouble 0] is resolved by [Bob].
[Trouble 33] is resolved by [Bob].
[Trouble 66] is resolved by [Bob].
[Trouble 99] is resolved by [Bob].
[Trouble 132] is resolved by [Diana].
[Trouble 165] is resolved by [Diana].
[Trouble 198] is resolved by [Diana].
[Trouble 231] is resolved by [Elmo].
[Trouble 264] is resolved by [Fred].
[Trouble 297] is resolved by [Elmo].
[Trouble 330] cannot be resolved.
[Trouble 363] is resolved by [Elmo].
[Trouble 396] cannot be resolved.
[Trouble 429] is resolved by [Charlie].
[Trouble 462] cannot be resolved.
[Trouble 495] is resolved by [Elmo].


3. 다이어그램
https://ko.wikipedia.org/wiki/Chain_of_responsibility_%ED%8C%A8%ED%84%B4

https://sourcemaking.com/design_patterns/chain_of_responsibility


4. 관련패턴
  1) Composite pattern

  2) Command Pattern

클래스 디자인 패턴 18. Visitor Pattern

1. 개념
  1) 의미
     방문자 는 데이터 구조와 처리를 분리하여 처리부분을
       방문자 라고 불리우는 형태의 구현부분에 집중하는
       형태입니다.
     예제는 Composite 패턴을 기본으로 확장합니다.


2. 소스

  1) Visitor 및 Acceptor 인터페이스

public abstract class Visitor {
    public abstract void visit(File file);
    public abstract void visit(Directory directory);
}


public interface Acceptor {
    public abstract void accept(Visitor v);
}



  2) Entry는 Acceptor를 implement 합니다.
      이외에는 기존과 같습니다.

import java.util.Iterator;

public abstract class Entry implements Acceptor {
    public abstract String getName();                                   // 이름을 얻는다.
    public abstract int getSize();                                      // 사이즈를 얻는다.
    public Entry add(Entry entry) throws FileTreatmentException {       // 엔트리를 추가
        throw new FileTreatmentException();
    }
    public Iterator iterator() throws FileTreatmentException {    // Iterator의 생성
        throw new FileTreatmentException();
    }
    public String toString() {                                          // 문자열 표현
        return getName() + " (" + getSize() + ")";
    }
}



  3) Entry를 상속하여 구현한 구현체 2개 클래스를 작성하는데
      기존에 비해 Accept 를 구현해주면됩니다.
public class File extends Entry {
    private String name;
    private int size;
    public File(String name, int size) {
        this.name = name;
        this.size = size;
    }
    public String getName() {
        return name;
    }
    public int getSize() {
        return size;
    }
    public void accept(Visitor v) {
        v.visit(this);
    }
}


import java.util.Iterator;
import java.util.Vector;

public class Directory extends Entry {
    private String name;                    // 디렉토리의 이름
    private Vector dir = new Vector();      // 디렉토리 엔트리의 집합
    public Directory(String name) {         // 생성자
        this.name = name;
    }
    public String getName() {               // 이름을 얻는다.
        return name;
    }
    public int getSize() {                  // 사이즈를 얻는다.
        int size = 0;
        Iterator it = dir.iterator();
        while (it.hasNext()) {
            Entry entry = (Entry)it.next();
            size += entry.getSize();
        }
        return size;
    }
    public Entry add(Entry entry) {         // 엔트리의 추가
        dir.add(entry);
        return this;
    }
    public Iterator iterator() {      // Iterator의 생성
        return dir.iterator();
    }
    public void accept(Visitor v) {         // 방문자를 받아들임
        v.visit(this);
    }
}



  4) Visitor 를 상속한 ListVisitor를 구현합니다.
import java.util.Iterator;

public class ListVisitor extends Visitor {
    private String currentdir = "";                         // 현재 주목하고 있는 디렉토리명
    public void visit(File file) {                  // 파일을 방문했을 때 호출된다.
        System.out.println(currentdir + "/" + file);
    }
    public void visit(Directory directory) {   // 디렉토리를 방문했을 때 호출된다.
        System.out.println(currentdir + "/" + directory);
        String savedir = currentdir;
        currentdir = currentdir + "/" + directory.getName();
        Iterator it = directory.iterator();
        while (it.hasNext()) {
            Entry entry = (Entry)it.next();
            entry.accept(this);
        }
        currentdir = savedir;
    }
}


5) 예외처리 클래스와 테스트 클래스를 작성합니다.

public class FileTreatmentException extends RuntimeException {
    public FileTreatmentException() {
    }
    public FileTreatmentException(String msg) {
        super(msg);
    }
}


public class Main {
    public static void main(String[] args) {
        try {
            System.out.println("Making root entries...");
            Directory rootdir = new Directory("root");
            Directory bindir = new Directory("bin");
            Directory tmpdir = new Directory("tmp");
            Directory usrdir = new Directory("usr");
            rootdir.add(bindir);
            rootdir.add(tmpdir);
            rootdir.add(usrdir);
            bindir.add(new File("vi", 10000));
            bindir.add(new File("latex", 20000));
            rootdir.accept(new ListVisitor());              

            System.out.println("");
            System.out.println("Making user entries...");
            Directory Kim = new Directory("Kim");
            Directory Lee = new Directory("Lee");
            Directory Kang = new Directory("Kang");
            usrdir.add(Kim);
            usrdir.add(Lee);
            usrdir.add(Kang);
            Kim.add(new File("diary.html", 100));
            Kim.add(new File("Composite.java", 200));
            Lee.add(new File("memo.tex", 300));
            Kang.add(new File("game.doc", 400));
            Kang.add(new File("junk.mail", 500));
            rootdir.accept(new ListVisitor());             
        } catch (FileTreatmentException e) {
            e.printStackTrace();
        }
    }
}



결과 :
Making root entries...
/root (30000)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (0)

Making user entries...
/root (31500)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (1500)
/root/usr/Kim (300)
/root/usr/Kim/diary.html (100)
/root/usr/Kim/Composite.java (200)
/root/usr/Lee (300)
/root/usr/Lee/memo.tex (300)
/root/usr/Kang (900)
/root/usr/Kang/game.doc (400)
/root/usr/Kang/junk.mail (500)


3. 다이어그램
https://ko.wikipedia.org/wiki/%EB%B9%84%EC%A7%80%ED%84%B0_%ED%8C%A8%ED%84%B4

4. 관련패턴
  1) Iterator 패턴

  2) Composite 패턴

  3) Interpreter 패턴

클래스 디자인 패턴 17. Composite Pattern

1. 개념
  1) 의미
     그릇과 내용물을 동일시하여 재귀적인 구조를 만드는 패턴


2. 소스

  1) 추상 클래스이자 디렌토리 엔트리를 표현하는
      Entry 클래스를 작성.

package A2;

public abstract class Entry {
    protected Entry parent;
    public abstract String getName();
    public abstract int getSize();
    public Entry add(Entry entry) throws FileTreatmentException {
        throw new FileTreatmentException();
    }
    public void printList() {
        printList("");
    }
    protected abstract void printList(String prefix);
    public String toString() {
        return getName() + " (" + getSize() + ")";
    }
    public String getFullName() {                
        StringBuffer fullname = new StringBuffer();
        Entry entry = this;
        do {
            fullname.insert(0, "/" + entry.getName());
            entry = entry.parent;
        } while (entry != null);
        return fullname.toString();
    }
}


  2) 파일을 표현하는 file 클래스를 작성

package A2;

public class File extends Entry {
    private String name;
    private int size;
    public File(String name, int size) {
        this.name = name;
        this.size = size;
    }
    public String getName() {
        return name;
    }
    public int getSize() {
        return size;
    }
    protected void printList(String prefix) {
        System.out.println(prefix + "/" + this);
    }
}


  3) 마찬가지로 directory 클래스를 작성
package A2;

import java.util.Iterator;
import java.util.Vector;

public class Directory extends Entry {
    private String name;
    private Vector directory = new Vector();
    public Directory(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public int getSize() {
        int size = 0;
        Iterator it = directory.iterator();
        while (it.hasNext()) {
            Entry entry = (Entry)it.next();
            size += entry.getSize();
        }
        return size;
    }
    public Entry add(Entry entry) {
        directory.add(entry);
        entry.parent = this;              
        return this;
    }
    protected void printList(String prefix) {
        System.out.println(prefix + "/" + this);
        Iterator it = directory.iterator();
        while (it.hasNext()) {
            Entry entry = (Entry)it.next();
            entry.printList(prefix + "/" + name);
        }
    }
}


  4) 예외를 담기위한 클래스 및 테스트 클래스
package A2;

public class FileTreatmentException extends RuntimeException {
    public FileTreatmentException() {
    }
    public FileTreatmentException(String msg) {
        super(msg);
    }
}


package A2;

public class Main {
    public static void main(String[] args) {
        try {
            Directory rootdir= new Directory("root");

            Directory bindir= new Directory("bin");
            rootdir.add(bindir);

            Directory tempdir= new Directory("tmp");
            bindir.add(tempdir);

            File file = new File("Composite.java", 100);
            tempdir.add(file);
            rootdir.printList();

            System.out.println("");
            System.out.println("file = " + file.getFullName());     
            System.out.println("tmp= " + tempdir.getFullName());     
        } catch (FileTreatmentException e) {
            e.printStackTrace();
        }
    }
}


결과 :
/root (100)
/root/bin (100)
/root/bin/tmp (100)
/root/bin/tmp/Composite.java (100)

file = /root/bin/tmp/Composite.java
tmp= /root/bin/tmp


3. 다이어그램
https://en.wikipedia.org/wiki/Composite_pattern

4. 관련패턴
  1) Command 패턴

  2) Visitor 패턴

  3) Decorator 패턴

2015년 10월 18일 일요일

클래스 디자인 패턴 16. Bridge Pattern

1. 개념
  1) 의미
     일정 기능을 담당하는 구조체에 특정 기능을 추가하는
        하나의 방법입니다.
     이를 좀더 확장하기 용이하게 하기 위해서 구현부분과
       추상부분을 분리합니다.  이후 이 두부분을
       이용해서 독립적으로 발전시킬 수 있습니다.


2. 소스

  1) 무언가 작업을 하는데 adapter 의 방법을 좀 인용한것
       같습니다. display 와 이를 상속하여 추가 구현할
       클래스 CountDisplay 을 작성합니다.
      display에서 에러나는 부분은 차후 작업합니다.
      display와 countdisplay는 기존 작업 내용을 포함하여
       새로운 기능을 추가합니다.

public class Display {
private DisplayImpl impl;
public Display(DisplayImpl impl){
this.impl = impl;
}
public void open(){
impl.rawOpen();
}

public void print(){
impl.rawPrint();
}

public void close(){
impl.rawClose();
}

public final void display(){
open();
print();
close();
}
}


public class CountDisplay extends Display {

public CountDisplay(DisplayImpl impl) {
super(impl);
}
public void multiDisplay(int times){
open();
for(int i=0; i<times;i++){
print();
}
close();
}
}


  2) displayimpl 을 작업하는 이 클래스는 추상클래스 입니다.
      기존 구현부를 담당하며 메소드를 규정합니다.

public class CountDisplay extends Display {

public CountDisplay(DisplayImpl impl) {
super(impl);
}

public void multiDisplay(int times){
open();
for(int i=0; i<times;i++){
print();
}
close();
}
}


  3) impl을 상속받아 실제 구현부를 작성합니다.

public class StringDisplayImpl extends DisplayImpl {
private String string;
private int width;
public StringDisplayImpl(String string){
this.string = string;
this.width = string.getBytes().length;
}
@Override
public void rawClose() {
printLine();
}

private void printLine() {
System.out.print("+");
for(int i=0; i<width; i++){
System.out.print("-");
}
System.out.println("+");
}
@Override
public void rawPrint() {
System.out.println("|"+string+"|");
}

@Override
public void rawOpen() {
printLine();
}
}


  4) 이를 테스트할 코드를 작성합니다.
      그냥 작성한 인스턴스를 불러서 생성할 수 있지만
      왜 이렇게 돌려서 작성하냐면 상속을 피하고
      인터페이스를 활용한 느슨한 연결을
      하고 싶어서 입니다.

public class MainTest {
public static void main(String[] args) {
Display d1 = new Display(new StringDisplayImpl("Hello, Korea"));
Display d2 = new CountDisplay(new StringDisplayImpl("Hello, World"));
CountDisplay d3 = new CountDisplay(new StringDisplayImpl("Hello, Universe"));

d1.display();
d2.display();
d3.display();
d3.multiDisplay(5);
}
}


결과 :
+------------+
|Hello, Korea|
+------------+
+------------+
|Hello, World|
+------------+
+---------------+
|Hello, Universe|
+---------------+
+---------------+
|Hello, Universe|
|Hello, Universe|
|Hello, Universe|
|Hello, Universe|
|Hello, Universe|
+---------------+


3. 다이어그램
https://ko.wikipedia.org/wiki/%EB%B8%8C%EB%A6%AC%EC%A7%80_%ED%8C%A8%ED%84%B4

4. 관련패턴
  1) Template Method 패턴

  2) Abstract Factory

  3) Adapter 패턴

2015년 10월 16일 금요일

클래스 디자인 패턴 15. Abstract Factory Pattern

1. 개념
  1) 의미
     interface를 가져와 실제로 구현을 하여 사용하는 Factory 패턴의
      확장입니다.
 

2. 소스

  1) html 페이지를 하나 작성할건데 기존처럼 단순하게
      하는 대신에 작성하는 페이지 위에 추상 클래스를 작성합니다.
      여기서는 구현부에 들어가는 각 부분을
      추상클래스로 하나의 셋트를 작성합니다.

package factory;

public abstract class Item {
protected String caption;
public Item(String caption){
this.caption=caption;
}
public abstract String makeHTML();
}


package factory;

public abstract class Link extends Item{
protected String url;
public Link(String caption, String url){
super(caption);
this.url=url;
}
}


package factory;

import java.util.Vector;

public abstract class Tray extends Item {
protected Vector tray = new Vector();
public Tray(String caption){
super(caption);
}
public void add(Item item){
tray.add(item);
}
}

package factory;

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Vector;

public abstract class Page {
protected String title;
protected String author;
protected Vector content = new Vector<>();

public Page(String title, String author){
this.title=title;
this.author=author;
}

public void add(Item item){
content.add(item);
}
public void output(){
String filename = title +".html";
try {
Writer writer = new FileWriter(filename);
writer.write(this.makeHTML());
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(filename +"을 작성햇습니다.");
}
public abstract String makeHTML();
}

package factory;

public abstract class Factory {
public static Factory getFactory(String classname){
Factory factory = null;
try {
factory = (Factory) Class.forName(classname).newInstance();
} catch (Exception e) {
System.out.println("class is : "+classname +" 이 발견되지 않았습니다.");
}
return factory;
}
public abstract Link createLink(String cpation, String url);
public abstract Tray createTray(String caption);
public abstract Page createPage(String title, String author);
}


  2) 이를 사용해서 test를 진행할 main 클래스를 작성합니다.
       이후 에러는 나지 않지만 실행시 클래스가 없다고 나옵니다.
       현재 구현한 부분은 실행되지 않는 추상적인 부분밖에 없어서
       그렇습니다.

package noname;

import factory.Factory;
import factory.Link;
import factory.Page;
import factory.Tray;

public class MainTest {
public static void main(String[] args) {
if(args.length!=1){
System.out.println("Usage : Java main class.name.of.ConcreateFactory");
System.out.println("example 1:  java main listfacoty.ListFActory");
System.out.println("example 2:  java main tableFactory.TableFactory");
System.exit(0);
}
Factory factory = Factory.getFactory(args[0]);

Link joins = factory.createLink("중앙일보", "http://www.joins.com/");
Link hani = factory.createLink("한겨례 신문", "http://www.hani.co.kr/");
Link us_yahoo = factory.createLink("Yahoo!", "http://www.yahoo.com/");
Link kr_yahoo = factory.createLink("Yahoo!Korea", "http://www.yahoo.co.kr");
Link excite = factory.createLink("Excite", "http://www.excite.com/");
Link google = factory.createLink("google", "http://www.google.com/");

Tray traynews = factory.createTray("newspaper");
traynews.add(joins);
traynews.add(hani);

Tray trayyahoo = factory.createTray("Yahoo!");
trayyahoo.add(us_yahoo);
trayyahoo.add(kr_yahoo);

Tray traysearch = factory.createTray("서치엔진");
traysearch.add(trayyahoo);
traysearch.add(excite);
traysearch.add(google);

Page page = factory.createPage("LinkPage", "kim kmi kkim");
page.add(traynews);
page.add(traysearch);
page.output();
}
}


  3) 지금까지 작성한 추상클래스를 상속받아 아래와 같이
      구현 클래스를 작성합니다.
       ListFactory 클래스는 factory에서 지정한 함수에서
       각자 추상 클래스를 상속받은 인스턴스를 생성해서
       넘겨주는 역할을 합니다.

package listfactory;

import factory.Factory;
import factory.Link;
import factory.Page;
import factory.Tray;

public class ListFactory extends Factory {

@Override
public Link createLink(String cpation, String url) {
return new ListLink(cpation, url);
}

@Override
public Tray createTray(String caption) {
return new ListTray(caption);
}

@Override
public Page createPage(String title, String author) {
return new ListPage(title, author);
}
}




  4) 각 추상클래스를 상속받아 실제 코드를 기입한
      부분입니다.
package listfactory;

import factory.Link;

public class ListLink extends Link {

public ListLink(String caption, String url) {
super(caption, url);
}

@Override
public String makeHTML() {
return "<li><a href=\""+url+"\">"+caption+"</a></li>\n";
}
}


package listfactory;

import java.util.Iterator;

import factory.Item;
import factory.Tray;

public class ListTray extends Tray {

public ListTray(String caption) {
super(caption);
}

@Override
public String makeHTML() {
StringBuffer buffer = new StringBuffer();
buffer.append("<li>\n");
buffer.append(caption+"\n");
buffer.append("<ul>\n");
Iterator it = tray.iterator();

while(it.hasNext()){
Item item = (Item) it.next();
buffer.append(item.makeHTML());
}
buffer.append("</ul>\n>");
buffer.append("</li>\n");
return buffer.toString();
}
}


package listfactory;

import java.util.Iterator;

import factory.Item;
import factory.Page;

public class ListPage extends Page {

public ListPage(String title, String author) {
super(title, author);
}

@Override
public String makeHTML() {
StringBuffer buffer = new StringBuffer();
buffer.append("<html><head><title>"+ title +"</title></head>\n");
buffer.append("<body>\n");
buffer.append("<h1>"+ title +"</h1>\n");
buffer.append("</ul>\n");
Iterator it = content.iterator();
while(it.hasNext()){
Item item = (Item) it.next();
buffer.append(item.makeHTML());
}
buffer.append("</ul>\n");
buffer.append("<hr><address>"+author+"</address>");
buffer.append("</body></html>\n");
return buffer.toString();
}
}




결과 : 실행시 argument 에 listfactory/ListFactory 를 넣어준다.
         콘솔로 실행시에는 java listfactory/ListFactory.java
         를 넣어줘야 정상적으로 실행된다.

LinkPage.html을 작성햇습니다.


해당 파일 실행시 다음 결과를 확인 할 수 있습니다.
   여기서 중요한 점은 추상 클래스 셋트를 상속하여
   다른 구현 클래스를 추가시에 다른 소스 변동없이 다른
   페이지 구성을 할 수가 있습니다.

LinkPage

  • newspaper >
  • 서치엔진 >

  • kim kmi kkim



    3. 다이어그램


    4. 관련패턴
      1) Builder 패턴
          인터페이스가 정해져 있는 추상적인 부품을 구현해 나가는 형태이며
          Builder 패턴은 단계를 밟아나가면서 대규모의
          인스턴스를 만들어 나갑니다.

      2) Factory Method 패턴
          제품, 부품을 만드는 형태는 Factory Method와 같습니다.

      3) Composite 패턴
          만들어진 결과물은 Composite 패턴이 되는 경우가 있습니다.

      4) Singleton 패턴
          구체정인 공장은 Singleton 패턴이 되는 경우가 있습니다.