request XSS 처리


public static String getCheckReqXSS(Map<String, String> map, String parameter, String default_value) {

String req_value = (map.get(parameter) == null ||  map.get(parameter).equals("")) ? default_value : map.get(parameter);

req_value = req_value.replaceAll("</?[a-zA-Z][0-9a-zA-Z가-\uD7A3ㄱ-ㅎ=/\"\'%;:,._()\\-# ]+>","");

req_value = req_value.replaceAll(">","");

req_value = req_value.replaceAll(">","");

return req_value;

}

'ETC' 카테고리의 다른 글

이클립스 eclipse.ini 설정  (0) 2015.09.01
[TIP] 이클립스 단축키 모음  (0) 2014.12.07
[TIP] EditPlus 단축키 모음  (0) 2014.12.07
[티스토리] 소스코드 사용법  (0) 2014.11.29

남신의주 유동 박씨봉방

어느 사이에 나는 아내도 없어지고, 또

아내와 같이 살던 집도 없어지고

그리고 살뜰한 부모며 동생들하고도 멀리 떨어져서

그 어느 바람 세인 쓸쓸한 거리 끝에 헤매이었다

바로 날도 저물어서

바람은 더욱 세게 불고, 추위는 점점 더해 오는데

나는 어느 목수네 집 헌 삿을 깐

한 방에 들어서 쥔을 붙이었다

이리하여 나는 이 습내 나는 춥고 누긋한 방에서

낮이나 밤이나 나는 나 혼자도 너무 많은 것 같이 생각하며

딜옹배기에 북덕불이라도 담겨 오면

이것을 안고 손을 쬐며 재 우에 뜻없이 글자를 쓰기도 하며

또 문 밖에 나가지도 않고 자리에 누워서

머리에 손깎지베개를 하고 굴기도 하면서

나는 내 슬픔이며 어리석음이며를 소처럼 연하게 쌔김질하는 것이었다

내 가슴이 꽉 매어올 적이며

내 눈에 뜨거운 것이 핑 괴일 적이며

또 내 스스로 화끈 낯이 붉도록 부끄러울 적이며

나는 내 슬픔과 어리석음에 눌리어 죽을 수밖에 없는 것임을 느끼는 것이었다

그러나 잠시 뒤에 나는 고개를 들어

허연 문창을 바라보든가 또 눈을 떠서 높은 천장을 쳐다보는 것인데

이때 나는 내 뜻이며 힘으로, 나를 이끌어 가는 것이 힘든 일인 것을 생각하고

이것들보다 더 크고 높은 것이 있어서, 나를 마음대로 굴려 가는 것을 생각하는 것인데

이렇게 하여 여러 날이 지나는 동안에

내 어지러운 마음에는 슬픔이며, 한탄이며, 가라앉을 것은 차츰 앙금이 되어 가라앉고

외로운 생각만이 드는 때쯤 해서는

더러 나줏손에 쌀랑쌀랑 싸락눈이 와서 문창을 치기도 하는 때가 있는데

나는 이런 저녁에는 화로를 더욱 다가 끼며, 무릎을 꿇어 보며

어느 먼 산 뒷옆에 바우섶에 따로 외로이 서서

어두워 오는데 하이야니 눈을 맞을, 그 마른 잎새에는

쌀랑쌀랑 소리도 나며 눈을 맞을

그 드물다는 굳고 정한 갈매나무라는 나무를 생각하는 것이었다


이클립스 eclipse.ini 설정

이클립스 eclipse.ini 설정


-startup

plugins/org.eclipse.equinox.launcher_1.2.0.v20110502.jar

--launcher.library

plugins/org.eclipse.equinox.launcher.win32.win32.x86_1.1.100.v20110502

-product

org.eclipse.epp.package.jee.product

--launcher.defaultAction

openFile

--launcher.XXMaxPermSize

256M


-showsplash

image/tan_helios.bmp

▶ 이클립스 실행시 이미지변경


--launcher.XXMaxPermSize

256m

--launcher.defaultAction

openFile

-vmargs

-Dosgi.requiredJavaVersion=1.5 


-Xverify:none

▶ 클래스 검사 생략. 이클립스 실행 시간이 줄어듭니다.


-XX:+UseParallelGC

▶ Parallel Collector를 사용. 병렬 가비지 컬렉션입니다.


-XX:-UseConcMarkSweepGC

▶ 병행 mark-sweepGC 기능을 수행하여 GUI 응답 속도 처리


-XX:+AggressiveOpts

▶ 컴파일러 소수점 최적화 기능


-XX:PermSize=32M

▶ 이클립스 클래스 로딩 기본 메모리


-XX:MaxPermSize=128M

▶ 이클립스 클래스 로딩 최대 메모리


-XX:MaxNewSize=32M

▶ JVM에서 새로운 객체가 생성 될때 로딩되는 최소 영역


-XX:NewSize=32M

▶ JVM에서 새로운 객체가 생성 될때 로딩되는 최대 영역


-Xms256m

▶ 이클립스 실행시 잡는 최소 메모리


-Xmx512m

이클립스 실행시 잡는 최대 메모리



'ETC' 카테고리의 다른 글

request XSS 처리  (0) 2017.05.29
[TIP] 이클립스 단축키 모음  (0) 2014.12.07
[TIP] EditPlus 단축키 모음  (0) 2014.12.07
[티스토리] 소스코드 사용법  (0) 2014.11.29

홈페이지 제작해드립니다.

안녕하세요... 블로그 운영자 입니다.


순수한 개발 레퍼런스 목적하에 만든 블로그에 광고성 글을 기재하게 되어 조심스러운 감이 있습니다.


다름이 아니라, 소규모 웹 사이트 제작할 수 있는 개발자 구하는 분들을 위해 글 하나 남깁니다.


현재 저는 대형 SI에 근무 중에 있으나, 주말이나 평일 저녁 시간을 이용하여 부업을 하나 하려 합니다.


일단 화면에 관해서는 다양한 템플릿을 보여드리겠습니다. (아래 주소를 클릭해주세요.)


https://wrapbootstrap.com/themes 


위 사이트는 외국의 전문 디자이너들이 만든 것으로 세련되고 깔끔한 디자인 템플릿을 제공해주고 있습니다.


다만, 고객의 요구사항에 맞게 커스터마이징하는 부분은 개발자의 역량에 맞기고 있습니다.


수많은 사이트를 보았지만, 퀄리티 자체는 대한민국 어느 사이트를 뒤져봐도 떨어지지가 않습니다. 다양한 테마을 검색해보고 골라주시면 됩니다. ㅎㅎㅎ


개발함에 있어 업무분담은  퍼블리싱(화면개발) ② Java/JSP 코딩 ③ 분석,설계,서버(웹 호스팅, DBA), 고객관리'로 나누어져 있으며,


이 모든 것을 저 한 사람이 모두 도맡을 수 있을 수도 있고, 부분적으로 필요한 part만 맡아서 개발도 가능합니다.


실제 위 사이트를 이용하여 웹 사이트를 구축한 경험이 있습니다.


포트폴리오를 공개하고 싶지만, 특정 회사의 상호가 명시되었으므로 블로그에서는 비공개로 하고, 요청시 URL 주소 보내드리겠습니다.


마지막으로 강조하고 싶은 말은 홈페이지 제작비용이 매우 저렴하고, 책임감 있고 성실하면서도 장인정신 개발마인드를 가진 개발자가 참여한다는 것입니다.ㅋ


자세히 살펴보시고, 연락주시길 바랍니다.


카톡아이디: hujii0711

쇼핑몰 제작 홈페이지 제작 홈페이지 만들기 i 쇼핑몰 만들기 졸작 의뢰 졸작 만들기 포트폴리오 만들기 i 포트폴리오 제작 게시판만들기 i 게시판 제작 i 사이트 제작  사이트 만들기

 사이트 유지보수 i 사이트 제작  i 웹사이트 만들기 i 웹사이트 유지보수 쇼핑몰 제작 홈페이지 제작 홈페이지 만들기 i 쇼핑몰 만들기 졸작 의뢰 졸작 만들기 포트폴리오 만들기 i 포트폴리오 제작 게시판만들기 i 게시판 제작 i 사이트 제작  사이트 만들기  사이트 유지보수 i 사이트 제작  i 웹사이트 만들기 i 웹사이트 유지보수 쇼핑몰 제작 홈페이지 제작 홈페이지 만들기 i 쇼핑몰 만들기 졸작 의뢰 졸작 만들기 포트폴리오 만들기 i 포트폴리오 제작 게시판만들기 i 게시판 제작 i 사이트 제작  사이트 만들기 사이트 유지보수 i 사이트 제작  i 웹사이트 만들기 i 웹사이트 유지보수 쇼핑몰 제작 홈페이지 제작 홈페이지 만들기 i 쇼핑몰 만들기 졸작 의뢰 졸작 만들기 포트폴리오 만들기 i 포트폴리오 제작 게시판만들기 i 게시판 제작 i 사이트 제작  사이트 만들기 사이트 유지보수 i 사이트 제작  i 웹사이트 만들기 i이트 유지보수

[Java] 객체를 제거하는 방법

객체를 제거하는 방법


1. 레퍼런스가 영원히 영역을 벗어남


void A()    {

School s = new School();

}

메소드가 종료되면 's' 라는 레퍼런스도 죽어버립니다.

 

2. 레퍼런스에 다른 객체를 대입


School s = new School();

s = new School();

레퍼런스 's' 에 새로운 객체를 대입하면 첫번째 객체는 버려집니다.

 

3. 레퍼런스를 직접 'null' 로 설정

 

School s = new School();

s = null;

's' 를 '해제' 하면 첫번째 객체는 버려집니다.

즉 레퍼런스 's' 가 아무 객체도 참조하지 않는 것입니다.

 

레퍼런스가 참조하지 않는 객체는 가비지 컬렉션의 대상이 되어 자동으로 정리됩니다.

'Programing > Java' 카테고리의 다른 글

[Java] Wrapper 클래스  (0) 2015.04.13
[Java] 예외 던지기  (0) 2014.12.25
[Java] 예외 만들기  (0) 2014.12.25
[Java] 예외 처리 기본  (0) 2014.12.25
[Java] enum  (0) 2014.12.25

[Java] Wrapper 클래스

Wrapper 클래스


Wrapper 클래스라는 것은 그 이름이 의미하듯이 'wrap(포장)'하는 기능의 클래스를 의미한다.

이것은 주로 JDK1.5 이전에 많이 쓰였던 기능들인데 설명하자만 기본자료형을 객체자료형으로 변환하기 위해서 사용하는 클래스라고 생각하면 된다.


1. 문제제기

Object 타입의 변수는 모든 객체형 자룔를 표현할 수 있지만, int나 byte와 같은 기본형 자료는 선언할 수 없다.

Object a = 123; //컴파일오류


2. 문제를 일으키는 경우

- 변수의 최초 선언

- 메소드의 파라미터, 리턴타입

- 배열과 같은 자료구조


3. 기본형 자료를 객체형 자료로 처리하기 위한 클래스

기본자료형 

Wapper 클래스 

byte

Byte 클래스

short 

Short 클래스

int 

Integer 클래스 

long 

Long 클래스

double 

Double 클래스

float 

Float 클래스 

boolean 

Boolean 클래스 

char 

Character 클래스 


4. 사용법

Integer data = new Integer(3);

int intValue = data.intValue();

double doubleValue = data.doubleValue();

String StringValue = data.toString();


예제)

Integer aa = new Integer(10);

Integer bb = aa;

aa = aa + new Integer(1);

System.out.println("aa:  "+aa);

System.out.println("bb:  "+bb);

실행결과

aa:   11

bb:   10


5. 문자열 --> 기본자료형 / 숫자 --> 문자열

문자를 기본 자료형으로 바꿀 때

기본 자료형을 문자형으로 바꿀 때 

byte b = Byte.parseByte(문자열);

Byte.toString(byte 값);

short s = Short.parseshort(문자열);

Short.toString(short 값); 

int i = Integer.parseInt(문자열); 

Integer.toString(int 값);

double d = Double.parseDouble(문자열);

Double.toString(double 값); 

long l = Long.parseLong(문자열); 

Long.toString(long 값); 

float f = Float.parseFloat(문자열); 

Float.toString(float 값); 

boolean b = new Boolean(문자열); 

Boolean.toString(boolean 값); 

char c = String.클래스의 메소드 이용 

Character.toString(char 값); 


6. Autoboxing / Unboxing

Autoboxing / Unboxing는 JDK1.5에서 지원하는 자동 변환이다. 이 방식 때문에 기본 자료형이나 객체 자료형을 간단히 변환할 수 있게 되었다.

(1) Autoboxing

Autoboxing은 기본 자료형의 데이터를 자동으로 객체 자료형으로 변환하는 것을 의미한다.

int i = 10;

Integer J = 1;


(2) Unboxing

Unboxing은 Wrapper 자료형을 기본 자료형으로 자동 변환한다.

Integer i = new Integer(10);

int j = 1;


'Programing > Java' 카테고리의 다른 글

[Java] 객체를 제거하는 방법  (1) 2015.05.19
[Java] 예외 던지기  (0) 2014.12.25
[Java] 예외 만들기  (0) 2014.12.25
[Java] 예외 처리 기본  (0) 2014.12.25
[Java] enum  (0) 2014.12.25

[DesignPattern] 옵서버 패턴

옵서버 패턴


Observer 패턴에서는 관찰 대상의 상태가 변화하면 관찰자에게 알려준다.

Observer 패턴은 상태 변화에 따른 처리를 기술할 때 효과적이다.

옵서버 패턴은 데이터의 변경이 발생했을 경우 상대 클래스나 객체에 의존하지 않으면서 데이터 변경을 통보하고자 할 때 유용하다.

예를 들어 새로운 파일이 추가되거나 기존 파일이 삭제되었을 때 탐색기는 이를 즉시 표시할 필요가 있다. 탐색기를 복수 개 실행하는 상황이나 하나의 탐색기에서 파일 시스템을 변경했을 때는 다른 탐색기에게 즉각적으로 이 변경을 통보해야 한다. 다른 예로는 차량의 연로가 소진될 때까지의 주행 가능 거리를 출력하는 클래스, 연료량이 부족하면 경고 메시지를 보내는 클래스, 연료량이 부족하면 자동으로 근처 주유소를 표시하는 클래스 등에 연료량의 변화를 통보하는 경우가 있다. 이런 경우에 연료량 클래스는 연료량에 관심을 가지는 구체적인 클래스에 직접 의존하지 않는 방식으로 설계하는 것이 바람직하다.

--> 옵서버 패턴은 통보 대상 객체의 관리를 Subject 클래스와 Observer 인터페이스로 일반화한다. 그러면 데이터 변경을 통보하는 클래스(ConcreateSubject)는 통보 대상 클래스나 객체(ConcreateObserver)에 대한 의존성을 없앨 수 있다. 결과적으로 옵서버 패턴은 통보 대상 클래스나 대상 객체의 변경에도 ConcreateSubject 클래스를 수정없이 그대로 사용할 수 있도록 한다.



▶ 등장인물

 이름

 해설

 Observer

 관찰자를 나타내는 인터페이스

 NumberGenerator

 수를 생성하는 오브젝트를 나타내는 클래스

 RandomNumberGenerator

 랜덤으로 수를 생성하는 클래스

 DigitObserver

 숫자로 수를 표시하는 클래스

 GraphObserver

 간이 그래프로 수를 표시하는 클래스

 Main

 동작 테스트용 클래스


▶ 예제 프로그램 해설

Observer 패턴을 사용한 예제 프로그램을 살펴보자. 이번 예제 프로그램은 많은 수를 생성하는 오브젝트를 관찰자가 관찰해서 그 값을 표시하는 단순한 것이다.

단, 표시의 방법은 관찰자에 따라서 다르다. DigitObserver는 값을 숫자로 표시하지만, GraphObserver는 값을 간이 그래프로 표시한다.


1. Observer

- Observer(관찰자)의 역할: Observer는 Subject 역할로부터 '상태가 변했습니다.'라고 전달 받는 역할을 한다. 이를 위한 메서드는 update이다.

데이터의 변경을 통보 받는 인터페이스 즉, Subject에서는 Observer 인터페이스의 update 메서드를 호출함으로써 ConcreateSubject의 데이터 변경을 ConcreateObserver에게 통보한다.

package observer;


public interface Observer {

    public abstract void update(NumberGenerator generator);

}

Observer 인터페이스는 '관찰자'를 표현하는 인터페이스이고, 구체적인 관찰자는 이 인터페이스를 구현한다. 

update 메서드는 수를 생성하는 NumberGenerator 클래스에서 호출된다.(generator는 '생성하는 것', '발생장치'라는 의미이다.) update 메서드는 NumberGenerator 클래스가 '나의 내용이 갱신되었습니다. 표시 쪽도 갱신해 주십시오'라고 Observer에게 전달하기 위한 메서드이다.


2. NumberGenerator

- Subject(관찰 대상자)의 역할: Subject는 '관찰되는 대상'을 나타낸다. Subject 역할은 관찰자인 Observer 역할을 등록하는 메서드와 삭제하는 메서드를 가지고 있다. 또 '현재의 상태를 취득하는' 메서드도 선언되어 있다. ConcrerateObserver 객체를 관리하는 요소. Observer 인터페이스를 참조해서 ConcreateObserver를 관리하므로 ConcreateObserver의 변화에 독립적일 수 있다.

package observer;


import java.util.ArrayList;

import java.util.Iterator;


public abstract class NumberGenerator {

    private ArrayList observers = new ArrayList(); // Observer를 저장

    public void addObserver(Observer observer) { // Observer를 추가

        observers.add(observer);

    }

    public void deleteObserver(Observer observer) { // Observer를 삭제

        observers.remove(observer);

    }

    public void notifyObservers() { // Observer에 알림

        Iterator it = observers.iterator();      

        while (it.hasNext()) {                 

            Observer o = (Observer)it.next();  

            o.update(this);                   

        }                                     

    }                                      

    public abstract int getNumber(); // 수를 취득한다

    public abstract void execute(); // 수를 생성한다

}

NumberGenerator 클래스는 수를 생성하는 추상 클래스이다. 실제의 수의 생성(excute 메서드)과 수를 취득하는 부분(getNumber 메서드)은 하위 클래스에서 구현되도록 추상 메서드로 되어 있다.(subclass responsibilty). observers 필드는 NumberGenerator를 관찰하는 Observer를 보존하는 필드이다.

addObserver는 Observer를 추가하는 메서드이고, deleteObserver는 Observer를 삭제하는 메서드이다. notifyObservers 메서드는 Observer 전원에 대해서 '나의 내용이 갱신되었으므로, 당신의 표시를 갱신해 주십시오'라고 전한다. 이 메서드 안에서는 observer 안의 Observer들 한 사람 한 사함의 update 메서드를 호출하고 있다.


3. RandomNumberGenerator

- ConcreateSubject(구체적인 관찰 대상자)의 역할ConcreateSubject는 구체적으로 '관찰되는 대상'을 표현하는 역할이다. 상태가 변화하면 그것이 등록되어 있는 Observeer 역할에 전한다. 

변경 관리 대상이 되는 데이터가 있는 클래스. 데이터 변경을 위한 메서드인 excute가 있으며, excute에서는 자신의 데이터인 subjectObserver를 변경하고 Subject의 notifyObservers 메서드를 호출해서

ConcreateObserver 객체에 변경을 통보한다.

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();           

        }

    }

}

RandomNumberGemerator 클래스는 NumberGenerator의 하위 클래스이고, 난수를 생성한다. 

execute 메서드는 난수(0~49의 범위의 정수)를 20개 생성하고, 그때마다 notifyObservers를 사용해서 관찰자에게 통지한다. 여기에서 사용되고 있는 nextInt 메서드는 java.util.Random 클래스의 메서드로 랜덤인 정수를 반환값으로 한다.


4. DigitObserver 

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) {

        }

    }

}

DigitObserver 클래스는 Observer 인터페이스를 구현하는 클래스로 관찰한 수를 '숫자'로 표시하기 위한 것이다. update 메서드 안에서 인수로 주어진 NumberGenerator의 getNumber 메서드를 사용해서 수를 취득하고 그것을 표시한다.


5. GraphObserver

- ConcreateObserver(구체적인 관찰자)의 역할ConcreateObserver는 구체적인 Observer이다. update 메서드가 호출되면 그 메서드안에서 Subject 역할의 현재 상태를 취득한다.

ConcreateSubject의 변경을 통보받는 클래스. Observer 인터페이스의 update 메서드를 구현함으로써 변경을 통보받는다. 변경된 데이터는 ConcreateSubject의 getNumber 메서드를 호출함으로써 변경을 조회한다.

package observer;


public class GraphObserver implements Observer {

    public void update(NumberGenerator generator) {

        System.out.print("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) {

        }

    }

}

GraphObserver 클래스도 Observer 인터페이스를 구현하는 클래스이다. 이 클래스는 관찰한 수를 '간이 그래프'로 표시한다.


6. Main

package observer;


public class Main {

    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();

    }

}

실행결과)

DigitObserver:26

GraphObserver:**************************

DigitObserver:3

GraphObserver:***

DigitObserver:4

GraphObserver:****

DigitObserver:37

GraphObserver:*************************************

DigitObserver:32

GraphObserver:********************************

DigitObserver:26

GraphObserver:**************************

DigitObserver:22

GraphObserver:**********************

DigitObserver:2

GraphObserver:**

DigitObserver:15

GraphObserver:***************

DigitObserver:10

GraphObserver:**********

DigitObserver:19

GraphObserver:*******************

DigitObserver:29

GraphObserver:*****************************

DigitObserver:42

GraphObserver:******************************************

DigitObserver:20

GraphObserver:********************

DigitObserver:39

GraphObserver:***************************************

DigitObserver:1

GraphObserver:*

DigitObserver:18

GraphObserver:******************

DigitObserver:43

GraphObserver:*******************************************

DigitObserver:2

GraphObserver:**

DigitObserver:21

GraphObserver:*********************

Main 클래스에서는 RandomNumberGenerator의 인스턴스를 한 개 만들고, 그 관찰자를 두 개 만든다. observer1은 DigitObserver, observer2는 GraphObserver의 인스턴스이다.

addObserver 메서드를 사용해서 관찰자를 등록한 후 generator.excute를 사용해서 수를 생성한다.


cp.) 디자인 패턴의 목적 중의 하나는 클래스를 재이용 가능한 부품으로 만드는 일이다. Observer 패턴에서는 상태를 가지고 있는 ConcreateSubject 역할과 상태변화를 전달 받는 ConcreateObserver 역할이 등장했다.

그리고 이 두 가지의 역할을 연결하는 것이 인터페이스인 Subject 역할과 Observer 역할이다.

RandomNumberGenerator 클래스는 현재 자신을 관찰하고 있는 것이 (자신이 알려주는 상대가) DigitObserver 클래스의 인스턴스인지 GraphObserver 클래스의 인스턴스인지 몰라도 상관없다.

그러나 observer 필드에 저장되어 있는 인스턴스들이 Obsever 인터페이스를 구현하고 있다는 것은 알고 있다. 이 인스턴스들은 addObserver에서 추가 된 것이므로 반드시 Observer 인터페이스를 구현하고 있으며 update 메서드를 호출할 수 있다. 한편, DigitObserver 클래스는 자신이 관찰하고 있는 것이 RandomNumberGenerator 클래스의 인스턴스인지, 다른 XXXNumberGenerator 클래스의 인스턴스인지 신경 쓰지 않는다.

단지 NumberGenarator의 하위 클래스의 인스턴스이고, getNumber 메서드를 가지고 있다는 것은 알고 있다.


cf.) MVC

Model/View/Controller(MVC) 안의 Model과 View의 관계는 Observer 패턴의 Subject 역할과 Observer 역할의 관계에 대응한다. Model은 '표시 형식에 의존하지 않는 내부 모델'을 조작하는 부분이다.

또 View는 Model이 '어떻게 보일 것인지'를 관리하고 있는 부분이다. 일반적으로 하나의 Model에 복수의 View가 대응한다.


[DesignPattern] 팩토리 메서드 패턴

팩토리 메서드 패턴


팩토리 메서드 패턴에서는 상위 클래스에서 처리의 골격을 만들고, 하위 클래스에서 구체적인 처리의 내용을 만들었다. 이 패턴을 인스턴스 생성의 장면에 적용한 것이 바로 팩토리 메서드 패턴이다. factory는 공장이라는 의미이다. 인스턴스를 생성하는 공장을 템플릿 메서드 패턴으로 구성한 것이 팩토리 메서드 패턴이다. 즉, 팩토리 메서드 패턴을 사용하면 객체 생성 기능을 제공하는 Factory 클래스를 정의하고 이를 활용하는 방식으로 설계하면 된다.

펙토리 메서드 패턴에서는 인스턴스를 만드는 방법을 상위 클래스 측에서 결정하지만 구체적인 클래스 이름까지는 결정하지 않는다. 따라서 인스턴스 생성을 위한 골격과 실제의 인스턴스 생성의 클래스를 분리해서 생각할 수 있다.

cp.) 팩토리 메서드 패턴은 객체의 생성 코드를 별도의 클래스/메서드로 분리함으로써 객체 생성의 변화에 대비하는 데 유용하다. 또한 팩토리 메서드 패턴은 객체 생성을 전담하는 별도의 클래스를 두는 대신 하위 클래스에서 적합한 클래스의 객체를 생성하는 방식으로도 적용할 수 있다.


▶ 등장인물

 패키지 

 이름

 해설

 framework

 Product

 추상메서드 use만 정의되어 있는 추상 클래스

 Factory

 메서드 create을 구현하고 있는 추상 클래스

 idcard

 IDCard

 메서드 use를 구현하고 있는 클래스

 IDCardFactory

 메서드 createProduct, registerProduct를 구현하고 있는 클래스

 Anonymous

 Main

 동작 테스트용 클래스


▶ 예제 프로그램 해설

아래의 예제 프로그램에서는 신분증명서 카드(ID 카드)를 만드는 공장을 소재로 하였다. 여기에는 다섯 개의 클래스가 있다. Product 클래스와 Factory 클래스는 framework라는 패키지에 속해 있다.

이 두 개의 클래스가 인스턴스 생성을 위한 골격(framework)의 역할을 한다.

IDCard 클래스와 IDCardFactory 클래스는 구체적인 내용을 구현하며 idcard라는 패키지에 속해 있다.

Main 클래스는 동작 테스트를 위한 클래스이다.


1. Product: 팩토리 메서드로 생성될 객체의 공통 인터페이스

- Product(제품)의 역할: 이것은 framework 쪽에 포함되어 있다. 이 패턴에서 생성되는 인스턴스가 가져야 할 인터페이스를 결정하는 것은 추상 클래스이다.

구체적인 내용은 하위 클래스의 ConcreateProduct 역할이 결정한다.

package framework;


public abstract class Product {

    public abstract void use();

}

framework 패키지의 Product 클래스는 '제품'을 표현한 클래스이다. 이 클래스에는 추상 메서드 use만이 선언되어 있다.

구체적인 use의 구현은 모두 Product의 하위 클래스에게 맡기고 있다.

이 framework에서는 제품이란 '무엇이든 use할 수 있는(사용할 수 있는) 것'으로 규정하고 있다.


2. Factory: 구체적으로 객체가 생성되는 클래스

- Creator(작성자)의 역할: Product 역할을 생성하는 추상 클래스는 framework 쪽에 가깝다. 구체적인 내용은 하위 클래스의 ConcreateCreator 역할이 결정한다.

Creator 역할은 실제로 생성하는 ConcreateProduct 역할에 가지고 있는 정보가 없다. Creator 역할이 가지고 있는 정보는 Product 역할과 인스턴스 생성의 메소드를 호출하면 Product가 생성된다는 것뿐이다. 예제 프로그램에서는 createProduct 메서드가 인스턴스 생성을 위한 메서드가 된다.

new를 사용해서 실제의 인스턴스를 생성하는 대신에, 인스턴스 생성을 위한 메서드를 호출해서 구체적인 클래스 이름에 의한 속박에서 상위 클래스를 자유롭게 만든다.

package framework;


public abstract class Factory {

    public final Product create(String owner) {

        Product p = createProduct(owner);

        registerProduct(p);

        return p;

    }

    protected abstract Product createProduct(String owner);

    protected abstract void registerProduct(Product product);

}

framework 패키지의 Factory 클래스에서는 템플릿 메서드 패턴이 사용되고 있다.

추상 메서드 createProduct에서는 '제품을 만들고', 만든 제품을 추상 메서드 registerProduct에서 '등록'한다. '제품을 만들고', '등록'하는 구현은 하위 클래스에서 수행한다.

이 framework에서 공장이란 'create 메서드에서 Product의 인스턴스를 생성하는 것'으로 규정하고 있다.

그리고 create 메서드는 'createProduct에서 제품을 만들어서 registerProduct에서 등록한다.'라는 순서로 구현되고 있다.

구체적인 구현 내용은 Factory Method 패턴을 적용한 프로그램에 따라 다르다. Factory Method 패턴에서는 인스턴스를 생성할 때 Templete Method 패턴을 사용한다.


3. IDCard: 구체적으로 객체가 생성되는 클래스

- ConcreateProduct(구체적인 제품)의 역할: 구제적인 제품을 결정하며, idcard 쪽에 해당된다.

package idcard;

import framework.*;


public class IDCard extends Product {

    private String owner;

    IDCard(String owner) {

        System.out.println(owner + "의 카드를 만듭니다.");

        this.owner = owner;

    }

    public void use() {

        System.out.println(owner + "의 카드를 사용합니다.");

    }

    public String getOwner() {

        return owner;

    }

}

내용을 실행하는 측(idcard 패키지)을 살펴보자. 인식번호 카드를 나타내는 IDCard라는 클래스를 만들어보자.

framework에서 분리된 것을 표시하기 위해 idcard 패키지라는 별도의 패키지를 만들고, IDCard 클래스를 제품 product 클래스의 하위 클래스로 정의한다.


4. IDCardFactory: 팩토리 메서드를 구현하는 클래스로 ConcreateProduct 객체를 생성한다.

- ConcreateCreator(구체적인 작성자)의 역할: 구체적인 제품을 만드는 클래스를 결정하며, idcard 쪽에 해당된다.

package idcard;

import framework.*;

import java.util.*;


public class IDCardFactory extends Factory {

    private List owners = new ArrayList();

    protected Product createProduct(String owner) { // 인스턴스 생성을 위한 메서드

        return new IDCard(owner);// IDCard의 인스턴스를 생성해서 제품을 만드는 일을 실현

// new를 사용해서 실제 인스턴스를 사용하는 대신에, 인스턴스 생성을 위한 메서드를 호출해서 

// 구체적인 클래스 이름에 의한 속박에서 상위클래스를 자유롭게 만든다.

    }

    protected void registerProduct(Product product) {

        owners.add(((IDCard)product).getOwner());

    }

    public List getOwners() {

        return owners;

    }

}

IDCardFactory 클래스에서는 createProduct와 registerProduct의 두 가지 메서드를 구현하고 있다. createProduct에서는 IDCard의 인스턴스를 생성해서 '제품을 만드는'을 실현하고 있다.

registerProduct에서는 IDCard의 owner(소유자)를 owners 필드를 추가해서 '등록'이라는 기능을 실현하고 있다.


5. Main

import framework.*;

import idcard.*;


public class Main {

    public static void main(String[] args) {

        Factory factory = new IDCardFactory();

        Product card1 = factory.create("홍길동");

        Product card2 = factory.create("이순신");

        Product card3 = factory.create("강감찬");

        card1.use();

        card2.use();

        card3.use();

    }

}

실행결과)

홍길동의 카드를 만듭니다.

이순신의 카드를 만듭니다.

강감찬의 카드를 만듭니다.

홍길동의 카드를 사용합니다.

이순신의 카드를 사용합니다.

강감찬의 카드를 사용합니다.


[DesignPattern] 템플릿 메서드 패턴

템플릿 메서드 패턴


Templete Method 패턴은 템플릿의 기능을 가진 패턴이다. 상위 클래스 쪽에 템플릿에 해당하는 메서드가 정의되어 있고, 그 메서드의 정의 안에는 추상 메서드가 사용되고 있다.

따라서 상위 클래스의 프로그램만 보면 추상 메서드를 어떻게 호출하고 있는지 알 수 있지만, 최종적으로 어떤 처리가 수행되는지는 알 수 없다.

추상 메서드를 실제로 구현하는 것은 하위 클래스이다. 하위 클래스 측에서 메서드를 구현하면 구체적인 처리가 결정된다. 서로 다른 하위 클래스가 서로 다른 구현을 실행하면 서로 다른 처리가 결정된다. 서로 다른 하위 클래스가 서로 다른 구현을 실행하면 서로 다른 처리가 실행될 것이다.

그러나 어떤 하위 클래스에서 어떤 구현을 하더라도 처리의 큰 흐름은 상위 클래스에서 결정한대로 이루어진다. 이와 같이 상위 클래스에서 처리의 뼈대를 결정하고, 하위 클래스에서 그 구체적인 내용을 결정하는 디자인 패턴을 템플릿 메서드 패턴이라고 한다.




▶ 등장인물

 이름

 해설

 AbstractDisplay

 메서드 display만 구현되고 있는 추상 클래스

 CharDisplay

 메서드 open, print, close를 구현하고 있는 클래스

 StringDisplay

 메서드 open, print, close를 구현하고 있는 클래스

 Main

 동작 테스트용 클래스


1. AbstractDisplay 

- AbstractClass(추상 클래스)의 역할AbstractClass는 템플릿 메서드를 구현한다. 또한 그 템플릿 메서드에서 사용하고 있는 추상 메서드를 선언한다. 이 추상 메서드를 선언한다.

이 추상 메서드는 하위 클래스인 ConcreateClass 역할에 의해 구현된다.

public abstract class AbstractDisplay { // 추상 클래스 AbstractDisplay

  public abstract void open(); // 하위 클래스에 구현을 맡기는 추상 메소드 (1) open

    public abstract void print(); // 하위 클래스에 구현을 맡기는 추상 메소드 (2) print

    public abstract void close(); // 하위 클래스에 구현을 맡기는 추상 메소드 (3) close

    public final void display() { // 추상 클래스에서 구현되고 있는 메소드 display

        open(); // 우선 open하고…

        for (int i = 0; i < 5; i++) { // 5번 print을 반복하고…

            print();                    

        }

        close(); // … 마지막으로 close한다. 이것이 display 메소드에서 구현되고 있는 내용.

    }

}


2. CharDisplay 

- ConcreateClass(구현 클래스)의 역할AbstractClass 역할에서 정의되어 있는 추상 메서드를 구체적으로 구현한다. 여기에서 구현한 메서드는 AbstractClass역의 템플릿 메서드에서 호출된다. 예제 프로그램에서는 CharDisplay 클래스나 StringClass 클래스가 이 역할을 한다.

public class CharDisplay extends AbstractDisplay {  // CharDisplay는 AbstractDisplay의 하위 클래스.

   private char ch;  // 표시해야 할 문자

   public CharDisplay(char ch) { // 생성자에서 전달된 문자 ch을

       this.ch = ch; // 필드에 기억해 둔다.

   }

   public void open() { // 상위 클래스에서는 추상 메소드였다. 여기에서 오버라이드해서 구현.

       System.out.print("<<");                   

   }

   public void print() { // print 메소드도 여기에서 구현한다. 이것이 display에서 반복해서 호출된다.

       System.out.print(ch); // 필드에 기억해 둔 문자를 1개 표시한다.

   }

   public void close() { // close 메소드도 여기에서 구현.

       System.out.println(">>");                  

   }

}


3. StringDisplay 

public class StringDisplay extends AbstractDisplay {  // StringDisplay도 AbstrctDisplay의 하위 클래스.

    private String string;  // 표시해야 할 문자열.

    private int width; // 바이트 단위로 계산한 문자열의 「폭」.

    public StringDisplay(String string) { // 생성자에서 전달된 문자열 string을

        this.string = string; // 필드에 기억.

        this.width = string.getBytes().length; // 그리고 바이트 단위의 폭도 필드에 기억해 두고 나중에 사용한다.

    }

    public void open() { // 오버라이드해서 정의한 open 메소드.

        printLine(); // 이 클래스의 메소드 printLine에서 선을 그리고 있다.

    }

    public void print() { // print 메소드는

        System.out.println("|" + string + "|"); // 필드에 기억해 둔 문자열의 전후에 “|”을 붙여서 표시.

    }

    public void close() { // close 메소드는

        printLine(); // open 처럼 printLine 메소드에서 선을 그리고 있다.

    }

    private void printLine() { // open과 close에서 호출된 printLine 메소드이다. private이기 때문에 이 클래스 안에서만 사용된다.

        System.out.print("+"); // 테두리의 모서리를 표현하는”+” 마크를 표시.

        for (int i = 0; i < width; i++) { // width개의 “-“을 표시하고

            System.out.print("-"); // 테두리 선으로 이용한다.

        }

        System.out.println("+"); // 테두리의 모서리를 표현하는 “+” 마크를 표시.

    }

}


4. Main 

public class Main {

    public static void main(String[] args) {

        AbstractDisplay d1 = new CharDisplay('H'); // 'H'을 가진 CharDisplay 인스턴스를 1개 만든다.

        AbstractDisplay d2 = new StringDisplay("Hello, world."); // “Hello, world.”을 가진 StringDisplay의 인스턴스를 1개 만든다.

        AbstractDisplay d3 = new StringDisplay("안녕하세요."); // “안녕하세요.”를 가진 StringDisplay의 인스턴스를 1개 만든다.

        d1.display(); // d1, d2, d3 모두 AbstractDisplay의 하위클래스의 인스턴스이기 때문에

        d2.display(); // 상속한 display메소드를 호출할 수 있다.

        d3.display(); // 실제 동작은 CharDisplay나 StringDisplay에서 결정한다.

    }

}


실행결과)

<<HHHHH>>

+-------------+

|Hello, world.|

|Hello, world.|

|Hello, world.|

|Hello, world.|

|Hello, world.|

+-------------+

+----------------+

|안녕하세요.|

|안녕하세요.|

|안녕하세요.|

|안녕하세요.|

|안녕하세요.|

+----------------+


[DesignPattern] 프록시 패턴

프록시 패턴


proxy는 대리인이라는 의미이다.

대리인이란 일을 해야 할 본인을 대신(대리)하는 사람이다. 본인이 아니라도 가능한 일을 맡기기 위해서 대리인을 세운다.

대리인은 어디까지나 대리에 지나지 않기 때문에 할 수 있는 일에는 한계가 있다.

대리인이 할 수 있는 범위를 넘는 일이 발생하면, 대리인은 본인한테 와서 상담을 한다.

오브젝트(객체) 지향에서는 '본인'도 '대리인'도 오브젝트(객체)가 된다. 바빠서 일을 할 수 없는 오브젝트 대신에 대리인 오브젝트가 어느 정도 일을 처리하게 된다.




▶ 등장인물

 

 

 Printer

 이름있는 프린터를 나타내는 클래스(본인)

 Printable

 Printer와 PrinterProxy 공통의 인스턴스

 PrinterProxy

 이름있는 프린터를 나타내는 클래스(대리인)

 Main

 동작 테스트용 클래스


▶ 예제 프로그램 해설

아래는 Proxy 패턴을 사용한 예제이다.

이번 예제 프로그램은 '이름이 있는 프린터'이다. 프린터라고 해도 실제로는 화면에 문자열을 표시할 뿐이다. Main 클래스는 PrinterProxy 클래스의 인스턴스(대리인)를 생성한다.

그 인스턴스에 'Alice'라는 이름을 붙이고 그 이름을 표시한다. 그 후 'Bob'이라는 이름으로 변경해서 그 이름을 표시한다. 이름의 설정과 취득에서는 아직 실제 Printer 클래스의 인스턴스(본인)는 생성되지 않는다. 이름의 설정과 취득 부분은 PrinterProxy 클래스가 대리로 실행한다. 마지막에 print 메소드를 호출해서 실제로 프린터를 실행하는 단계가 되어서야 비로소 Printer 클래스는 Printer 클래스의 인스턴스를 생성한다. 그리고 PrinterProxy 클래스와 Printer 클래스를 동일시 하기 위해 Printable 인터페이스가 정의되어 있다.

여기에서는 Printer 클래스의 인스턴스 생성에 많은 시간이 걸린다는 것을 전제로 프로그램을 만든다. 시간이 걸린다는 것을 표현하기 위해서 생성자로부터 heavyJob 메서드를 호출해서 일부러 '무거운일'을 실행시킨다.


1. Printer 클래스

- RealSubject(실제의 주체)의 역할: '대리인'인 Proxy 역할에서 감당할 수 없는 일이 발생했을 때 등장하는 것이 '본인'인 RealSubject 역할이다. 이 역할도 Proxy 역할과 마찬가지로 Subject 역할에서 정해져 있는 인터페이스(API)를 구현한다.

public class Printer implements Printable {

    private String name;

    public Printer() {

        heavyJob("Printer의 인스턴스를 생성 중");

    }

    public Printer(String name) { // 생성자

        this.name = name;

        heavyJob("Printer의 인스턴스 (" + name + ")을 생성 중");

    }

    public void setPrinterName(String name) { // 이름의 설정

        this.name = name;

    }

    public String getPrinterName() { // 이름의 취득

        return name;

    }

    public void print(String string) { // 이름을 붙여 표시

        System.out.println("=== " + name + " ===");

        System.out.println(string);

    }

    private void heavyJob(String msg) {// 무거운 일(의 예정)

        System.out.print(msg);

        for (int i = 0; i < 5; i++) {

            try {

                Thread.sleep(1000);

            } catch (InterruptedException e) {

            }

            System.out.print(".");

        }

        System.out.println("완료");

    }

}

Printer 클래스는 '본인'을 표시하는 클래스이다. 생성자에서는 앞서 말했듯이 더미의 '무거운 일' 즉, heavyJob을 실행하고 있다. setPrinterName은 이름을 설정하는 메서드이고, getPrinterName은 이름을 취득하는 메서드이다. print 메서드는 프린터의 이름을 붙여서 문자열을 표시하고 있다. heavyJob 메서드는 실행에 5초가 걸리는 무거운 일을 표현하고 있다.


2. Printable 인터페이스

- Subject(주체)의 역할: Proxy 역할과 RealSubject 역할을 동일시 하기 위한 인터페이스(API)를 결정한다.

Subject 역할이 있는 덕분에 Client 역할은 Proxy 역할과 RealSubject 역할의 차이를 의식할 필요가 없다. 

public interface Printable {

    public abstract void setPrinterName(String name); // 이름의 설정

    public abstract String getPrinterName(); // 이름의 취득

    public abstract void print(String string); // 문자열 표시(프린트 아웃)

}

Printable 인터페이스는 PrinterProxy 클래스와 Printer 클래스를 동일시 하기 위한 것이다. setPrinterName 메서드는 이름의 설정, getPrinterName 메서드는 이름의 취득, 그리고 print 메서드는 프린트 아웃(문자열 표시)을 위한 것이다.


3. PrinterProxy 클래스

- Proxy(대리인)의 역할: Proxy의 역할은 Client 역할의 요구를 할 수 있는 만큼 처리를 한다. 만약, 자신만으로 처리할 수 없으면 Proxy 역할은 RealSubject 역할에게 처리를 맡긴다. Proxy 역할은 정말로 RealSubject 역할이 필요해지면 그때 RealSubject 역할을 생성한다. Proxy 역할은 Subject 역할에서 정해지는 인터페이스(API)를 구현한다.

public class PrinterProxy implements Printable {

    private String name; // 이름

    private Printer real; // 「본인」

    public PrinterProxy() {

    }

    public PrinterProxy(String name) { // 생성자

        this.name = name;

    }

    public synchronized void setPrinterName(String name) {  // 이름의 설정

        if (real != null) {

            real.setPrinterName(name);  // 「본인」에게도 설정한다

        }

        this.name = name;

    }

    public String getPrinterName() {  // 이름의 설정

        return name;

    }

    public void print(String string) { // 표시

        realize();

        real.print(string);

    }

    private synchronized void realize() { // 「본인」을 생성

        if (real == null) {            

            real = new Printer(name);

        }                           

    }

}

Proxy 패턴의 중심은 PrinterProxy 클래스이다. 

PrinterProxy 클래스는 대리인의 역할을 수행하며, Printable 인터페이스를 구현한다. name 필드는 이름을 저장하고, real 필드는 '본인'을 저장한다.

생성자는 이름을 설정한다.(이 시점에서 '본인'은 만들어지지 않는다.)

setPrinterName 메서드는 새로운 이름을 설정한다. 만약,real이 null이 아니면(즉, '본인'이 이미 만들어져 있으면), 본인에 대해서도 그 이름을 설정한다.

그러나 real이 null이면(즉, '본인'이 아직 만들어져 있지 않으면), 자신(PrinterProxy의 인스턴스)의 name 필드에만 이름을 설정한다.

getPrinterName 메서드는 자신의 name 필드의 값을 반환할 뿐이다.

print 메서드는 대리인이 가능한 일의 범위를 넘어서기 때문에 여기에서 realize 메서드를 호출해서 본인을 생성한다. realize는 '현실화하다'라는 의미이다.

realize 메서드를 실행한 후 real 필드에는 본인(Printer 클래스의 인스턴스)이 저장되어 있기 때문에 real.print를 호출한다. 이것은 위임이다.

setPrinterName과 getPrinterName을 여러 차례 호출해도, Printer의 인스턴스는 생성되지 않는다.

Printer의 인스턴스가 생성되는 것은 '본인'이 정말로 필요할 때이다.(본인이 생성되었는지 아닌지를 PrinterProxy의 이용자는 전혀 알 수 없고, 알 필요도 없다.)

realize 메서드는 단순하다. real 필드가 null이면 new Printer에 의해 Printer의 인스턴스를 만든다. 그리고 real 필드가 null이 아니면 (즉, 이미 만들어져 있으면) 아무 처리도 하지 않는다.

기억해야 할 점은 Printer 클래스는 PrinterProxy의 존재를 모른다는 점이다. 자신이 PrinterProxy을 경유해서 호출되고 있는지 아니면 직접 호출되고 있는지 Printer 클래스는 모른다.

반면에 PrinterProxy 클래스는 Printer 클래스를 알고 있다. 왜냐하면 PrinterProxy 클래스의 real 필드는 Printer형이고, PrinterProxy 클래스의 소스 코드안에는 Printer 클래스 이름이 기술되어 있기 때문이다. 이처럼 PrinterProxy 클래스는 Printer 클래스와 깊이 관련되 부품이다.


cf.) PrinterProxy 클래스에서 setPrinterName 메서드와 realize 메서드가 synchronized 메서드로 되어 있는 이유

synchronized 메서드로 하지 않은 경우, 복수의 스레드로부터 setPrintName과 realize가 개별적으로 호출되면, PrinterProxy 클래스의 name과 Printer 클래스의 name에 차이가 생길 경우가 있다.

최초에 PrinterProxy의 name 필드의 값이 'Alice'이고, real 필드의 값이 null(즉, Printer 클래스의 인스턴스는 아직 생성되어 있지 않다.)이라고 가정하자.

스레드 A가 setPrinterName("Bob")을 실행함과 동시에 스레드 B가(print 메서드 경유로) realize 메서드를 호출했다고 하자. 만약 스레드가 교체되면 PrinterProxy 클래스의 name 필드의 값은 "Bob"이 되지만, Printer의 name 필드의 값은 "Alice"로 되고 만다.

setPrintName 메서드와 realize 메서드를 synchronized 메서드로 하면 이와 같은 스레드 교체가 발생하지 않는다. synchronized 메서드에 의해 real 필드의 값에 대한 판단과 값의 변경이 제각각 실행되지 않도록 하고 있다. synchronized 메서드를 이용해서 real 필드를 지키고 있는 것이 된다.


4. Main 클래스

- Client(의뢰인)의 역할: Proxy 패턴을 이용하는 역할이다.

public class Main {

    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.");

    }

}

Main 클래스는 PrinterProxy를 경유해서 Printer를 이용하는 클래스이다. 이 클래스는 처음에 PrinterProxy를 생성하고, getPrinterName을 이용해서 이름을 표시한다.

그리고 나서 setPrinterName으로 이름을 설정하고, 마지막에 print로 "Hello, world"라고 표시한다.

실행결과를 보고 이름의 설정과 표시를 하는 동안에는 Printer의 인스턴가 생성되지 않고, print 메서드를 호출한 후에 생성되고 있는 점을 확인하자.

실행결과)

이름은 현재 Alice입니다.

이름은 현재 Bob입니다.

Printer의 인스턴스 (Bob)을 생성 중.....완료

=== Bob ===

Hello, world.