'2011/09'에 해당되는 글 23건

  1. 2011.09.27 삼성전자 S급 인력관리
  2. 2011.09.27 김연우 나가수 탈락심경 + 동영상(눈물이 나는 날에는)
  3. 2011.09.27 유재석 기미영춘
  4. 2011.09.26 Visitor
  5. 2011.09.26 Mediator
  6. 2011.09.26 Builder
  7. 2011.09.26 FlightWeight
  8. 2011.09.26 ProtoType
  9. 2011.09.26 Observer
  10. 2011.09.26 Facade
  11. 2011.09.26 Chain of responsibility
  12. 2011.09.26 Decorate
  13. 2011.09.26 Composite
  14. 2011.09.26 Strategy
  15. 2011.09.26 Singleton
  16. 2011.09.26 Template
  17. 2011.09.26 Factory
  18. 2011.09.26 Adapter
  19. 2011.09.26 Iterator
  20. 2011.09.21 웹서비스 프레임워크 비교(Comparision of WS) Apache Axis2, CXF and Sun JAX-WS (3)
posted by 동건이 2011. 9. 27. 10:11

삼성전자 S급 인력관리


삼성전자의 S급 인재로 영입돼 현재 근무 중인 A박사를 만나 인재 확보를 둘러싼 삼성전자 내부 이야기를 들어봤다.

A박사는 몇 년 전 삼성전자에 특채된 기술진 인재다. 그는 외국에서 공학 박사를 마친 후 이름만 들으면 누구나 알 만한 해외 회사에서 단기간에 상당한 실적을 쌓았다.

그의 연구는 단순히 돈을 많이 벌어들이는 주제를 다룬 건 아니었다. 또 거대한 연구나 대형 프로젝트에서 실적을 쌓은 것도 아니었다. 그는 “다른 사람들과 달리 창조적인 해법을 내놓는 일을 종종 해낸 게 인정을 받은 것 같다”며 “거대 연구나 대형 프로젝트는 오히려 연구자 개인의 기여도를 확인하기 어려워 좋은 커리어가 못 된다”고 말했다.

그는 한국으로 치면 부장급으로 해외에서 근무 중일 때 삼성전자 한 임원으로부터 직접 연락을 받았다. 임원은 그와 같은 대학에서 다른 전공으로 박사를 받았던 모 직원의 소개로 전화를 걸었다며 “우리 회사를 좀 도와달라”는 말로 저녁식사를 권했다. 저녁에 만난 그 임원은 당시 고향 이야기나 글로벌 비즈니스 환경 이야기, 맛있는 식당 이야기 등으로 대충 시간을 보낸 뒤 “사람 만나는 게 일이라서 보자고 했다”고 말했다. 그리고 한 다리 건너 비슷한 전공자라며 당시 발표한 논문 이야기를 묻기도 했다.

한 달 뒤 그 임원은 A박사에게 다시 전화를 걸어 “더 근사한 식당이 있으니 술이나 한잔하자”며 저녁에 초대했다. 임원은 그 자리에서 “삼성은 이제 보통 회사가 아니다”며 “나라를 위한 기업이 되도록 도와달라”고 본심을 털어놨다. 또 A박사에게 한국에서, 한국 기업에서 일하는 의미를 강조했다. 그리고 어떤 환경에서 가장 일을 잘할 수 있는지 자세히 물었다.

그 자리에선 파격적인 복지책이나 연봉액수를 내놓지 않았다. 대신 “우리는 오랜 시간을 두고 인재를 모으고 있으니 급할 것 없다”며 “충분히 시간을 드릴 테니 생각해 보고 원하는 게 있으면 기탄없이 말해 달라”는 말만 남겼다. A박사는 “그 후 직장을 옮겨 출근하기까지 3개월이 더 걸렸는데 원하는 입사 조건을 거의 대부분 들어주더라”고 회상했다.

최근 삼성전자는 이 회장의 S급 인재 확보 지시 이후 A박사에게 “S급 인재로 괜찮은 사람을 빨리 추천하라”고 재촉했다. 자신의 전공분야에서 눈여겨봐온 사람이 있으면 적극적으로 찾아내 리스트업하라는 것이다. A박사는 “(S급 인재로서) 내가 맡은 일을 하는 것은 당연하고 또 다른 인재를 뽑아오는 것도 중요한 실적”이라고 말했다.

회사는 A박사에게 S급 인재에 대한 기준을 알려주지 않았다. A박사도 “S급 인재로 어떤 사람을 추천해야 할지 나도 궁금하다”며 “기준을 딱히 뭐라고 말하진 않지만 다들 어느 정도 컨센서스는 있다”고 말했다. 그는 “회사는 분명히 학벌, 경력을 깡그리 무시해도 된다지만, 어느 정도 수준 있는 인사의 최소한의 요건은 엇비슷하게 마련”이라며 “추천하는 사람 자체가 이미 어느 정도 학벌과 경력을 가진 사람들이기 때문에 그들이 만족하기 위한 주관적인 요건은 분명 있을 것”이라고 말했다. A박사는 일단 영어권 대학에서 박사를 받으면 회사 커뮤니케이션에서 유리할 것으로 봤다. 국적은 관계없고, 외국인이어도 상관없다. 한때 삼성전자가 외국인을 더 선호한 적은 있지만 외국인 비율이 높아지면서 이제는 그런 선호 기준이 거의 사라졌다고 한다. 석사나 학사 등 좀 더 젊은 시절에 영미권에서 공부했다면 더욱 유리할 거라고 예상했다. 과거에는 비영미권 중 독일 대학 출신자를 선호한 적도 있었지만, 몇 년 만에 금방 인기가 시들해졌다고 한다.

A박사가 영어능력을 중요하게 생각한 이유는 본인의 경험 때문이다. 그는 부모의 직업 특성상 중등교육 과정부터 외국에서 학교를 다녔다. 우리말을 포함해 3개국어에 능통하다. 단순히 여러 외국어를 구사하는 수준을 넘어 외국어로 설득력 있는 프레젠테이션을 할 수 있는 정도다.

그는 “삼성전자 안에는 외국인 직원이 워낙 많아 영어로 커뮤니케이션하는 게 대단히 중요하다”며 “일반적인 회화 수준으로는 논쟁 성격이 강한 토론에 감히 끼어들지 못하며 토론에서 소외되면 좋은 프로젝트를 받을 수도 없다”고 설명했다. 영어를 네이티브처럼 하는 A박사도 “이따금 실리콘밸리에서나 통하는 최신 유머를 따라잡지 못해 머쓱해진 적도 있다”고 털어놨다.

전공 선택도 중요하다. A박사의 전공인 공학 분야에 한정해 살펴보면, 일단 삼성전자에서 권위자가 없는 전공이 유리하다. 비슷한 전공 분야를 중복으로 가지고 있을 필요가 없고, 있다 해도 권위자에게 밀려날 가능성이 커서다.

전공은 박사학위 논문의 키워드로 판단한다. 과거 인기 있는 전공보다는 보다 진취적인 분야를 선택한 사람이 눈길을 끈다. A박사는 “사업부(삼성전자)와 기술원(삼성종합기술원)이 각기 뽑으려는 인재의 전공을 다소 엇갈리게 잡아 서로 보완적인 관계를 만든다”고 전했다. 경력은 좀 더 결정적이다. 박사를 받고 다른 연구소 등에 들어가 있던 사람보다는 ‘누구나 이름만 대면 알 수 있는’ 유명 회사에서 일한 경력이 먹힌다. 대략 10년 정도 근무한 40대 초중반이 주요 영입 대상이다.

삼성전자를 그만두고 모 대학으로 옮긴 B교수는 “세월이 지남에 따라 인정받는 경력 수준도 변했다”고 말했다. 그에 따르면 20여 년 전 삼성전자 설립 초반기에는 미국 박사가 부장으로 바로 영입됐다. 10여 년 전 회사가 도약기에 들어서면서부터는 미국 박사이면서 유명 회사 경력이 있어야 부장으로 들어올 수 있었다. 최근에는 부장급 중에서도 고참부장이나 임원급이 돼야 S급 인재 후보로 이름을 올릴 수 있다고 한다. 삼성전자의 글로벌 지위가 상당히 올라갔고 외국 기업에 젊은 임원이 많이 등장했기 때문이다.

직전 직장에서 일하면서 얻은 특허를 가진 사람은 이직이 다소 까다롭다. 특허 소유 관계 때문에 삼성이 데려오는 데 따른 부담이 커지기 때문이다.

B교수는 “전에는 전자 쪽 중요 특허를 가진 사람을 주로 천거했는데, 이제는 특허 이전 문제를 미리 해결했거나 특허를 오롯이 개인이 소지한 사람을 바라는 편”이라고 말했다.

A박사가 새로운 후보자를 물색하는 방법은 해당 분야 저널이나 해외 유명 미디어의 인터뷰를 통해서다. 그는 “굳이 수준을 제시하자면 IEEE펠로에 이름을 올릴 정도가 S급 후보가 될 수 있다”고 설명했다. 미국 전기전자학회 석학회원을 의미하는 ‘IEEE펠로’는 IEEE 회원 상위 0.1% 내에 있는 회원에게만 부여하는 최고 등급이다. 반도체, 전기, 전자 등 각 분야에서 10년 이상 경력을 가진 회원 가운데 탁월한 자질과 연구개발 업적으로 사회 발전에 기여한 사람 중에 선정한다. 삼성전자 현직 임원 중에도 IEEE펠로는 흔한 편이다.

해외 미디어에 한번쯤 이름이 올랐다고 S급 후보는 될 수 없다. 그가 속한 분야 학회의 활동 상황, 최근 논문 및 상벌 상황을 꼼꼼히 살펴본 뒤 월드와이드급 실적을 다년간 유지해야 S급 후보가 될 수 있다.

삼성전자 기술직군의 연구직원 C과장은 “회사 직원 대부분이 저마다 프라이드가 높은 편이기 때문에 웬만큼 뛰어나서는 S급으로 인정해주지 않는다”면서 “다른 사람에 대해 평가할 시간에 맡은 일 열심히 해서 사내 경쟁에서 자신이 이기겠다는 생각 뿐”이라고 말했다. 그는 또 “촉망받는다는 여러 인재도 삼성전자에 들어왔다가 무참히 깨지는 경우가 허다하다”며 “신임 부장이 실무능력이나 기술이 부족하면 과장들이 회의 자리에서 정면으로 반박해 창피나 면박을 주기도 한다”고 말했다.

특히 국내 사정에 어두운 해외 유학파가 이론만 제시하며 회의를 끌어가다 강한 비난을 받는 경우가 종종 있다. 연구 현장에서 오랫동안 실무를 쌓은 과장급 이하 직원들의 기세가 만만치 않기 때문이다. ‘그 말씀은 몇 년 전 어떤 학회 프로시딩(발표논문집) 몇 페이지 몇째 줄에서 누가 제시했다가 이런저런 문제로 오류를 인정한 건데, 논문을 끝까지 안 읽어보셨어요? 실험 한 번 해보면 다 아는 거 아닌가요?’라는 식이다. 영원한 S급 인재가 없다는 이야기다. 삼성전자 사람들이 생각하는 ‘S급 인재’에 대한 기준은 여러 가지다. 그러나 S급 인재라는 게 입사할 때나 회자되는 이야기일 뿐 입사 후 실력을 발휘하지 못하면 언제든 S급 타이틀을 잃어버린다는 건 공통된 이야기다.

'뉴스' 카테고리의 다른 글

갤럭시S를 만든 사람들(기념)  (2) 2011.10.13
스마트폰, 후각, 청각 기능이 있다.  (0) 2011.10.06
삼성전자 S급 인력관리  (0) 2011.09.27
구글 안드로이드와 삼성(2011.8.15)  (0) 2011.08.20
www에서 m의 시대로  (0) 2010.01.05
구글 인터넷 버스  (2) 2009.02.17

댓글을 달아 주세요

posted by 동건이 2011. 9. 27. 10:02

김연우 나가수 탈락심경 + 동영상(눈물이 나는 날에는)


김연우 탈락 심경 “‘나는 가수다’ 꼴찌? 나 ‘레전드’ 인데…”

 김연우나는 가수다탈락 심경

'나는 가수다' 탈락 심경을 고백한 김연우 ⓒ MBC 방송화면 캡쳐
[SSTV l 이금준 기자] 가수 김연우가 MBC ‘우리들의 일밤-서바이벌 나는 가수다’(이하 나는 가수다)에서 탈락한 심경을 고백해 눈길을 끌었다.

MBC ‘유재석 김원희의 놀러와’는 26일 방송을 통해 ‘나는 가수다’로 인기를 모은 김연우, 김조한, BMK, JK김동욱, 고영욱 등의 에피소드를 공개했다. 이들은 깨알 같은 방송 에피소드를 공개하며 시청자들의 웃음을 자아냈다.

특히 이날 방송에서 김연우는 ‘나는 가수다’ 탈락 심경에 대해 “아직도 가슴 한 켠이 먹먹하고 답답하다”며 “체기가 아직 안 내려간 것 같다”고 털어놨다. 그는 지난 5월 ‘나는 가수다’에서 1, 2라운드 종합 7위에 머물러 탈락한 바 있다.

특히 김연우는 탈락 심경과 관련 “받아들일 수밖에 없기는 한데”라며 “‘난데? 김연우인데? 내가 탈락이야?’ 했다. ‘나는 실용음악계의 레전드인데?’ 싶었다. 믿을 수 없는 일이었다”라고 덧붙여 웃음을 자아냈다.

아울러 김조한도 “‘나는 가수다’에서 탈락했을 때 쿨하게 받아들이고 싶었다. 회식도 재밌게 끝냈는데 집에 혼자 돌아갈 때 눈물이 나더라. 내가 울 줄은 몰랐다”는 말로 다시 한 번 스튜디오를 폭소케 했다.

한편, 김연우를 비롯 김조한의 탈락 심경이 화제를 모은 이날 ‘놀러와’에서는 출연자들이 ‘나는 가수다’에서 1위를 차지했던 노래들을 선사하기도 했다.

[동영상-인터넷뉴스 No.1 SSTV|www.ahaTV.co.kr]
모바일로 생생연예현장 동영상보기 [SHOW,fimm+TV+뉴스와생활+SSTV]

댓글을 달아 주세요

posted by 동건이 2011. 9. 27. 09:56

유재석 기미영춘

유재석 김영춘 용돈, 후배사랑 남다른 1인자 

 

유재석 김영춘 용돈 . '춘드래곤' 김영춘 씨가 유재석에가 수표를 받은 사연을 밝혀 훈훈함을 알렸습니다.

26일 김영춘 씨는 자신의 미투데이를 통해 "무한도전 '하나마나' 녹화를 했다.

끝이나고 인사를 드리고 집에 가려고 일어났다" 며

유재석 선배님이 잘가라고 악수를 건네주셨다 고 전했습니다.

 

유재석 김영춘 용돈을 주며 훈훈한 모습을 보였습니다.

유재석 김영춘 손을 잡으며 손에 수표 2장을 쥐어주셔서 너무 놀랐다고 괜찮다고 했지만

유재석 선배님이 차비하고 영춘아 열심히 하라며 격려를 했다고 합니다.

유재석 후배를 향한 남다른 애정에 김영춘 씨는 고마움을 드러냈다고 하네요.

 

역시 유재석 후배사랑이 남다르네요. 김영춘 멋진 선배님을 두신거 부럽습니다.

 

 























 

 

한편 무한도전 ‘하나마나 특집’에는 바다를 대신해 배우 신세경이 출연해 관심을 모았으며

이봉원 닮은 꼴 정재형이 이봉원 분장을 하고 등장해 웃음을 자아냈다고 합니다.

 

평소 후배를 잘 챙기는 등 모범적인 생활을 해오기로 유명한 유재석

김영춘 챙기는 모습이 보이는 이미지 만큼이나 정말 최고의 훈남 개그맨 유재석 이라고 생각합니다.

 

유재석 김영춘 무한도전 얼른 보고싶네요 ^^

 

댓글을 달아 주세요

posted by 동건이 2011. 9. 26. 19:31

Visitor

자바 디자인 패턴 16 - Visitor

1. Visitor 패턴은..

복잡한 구조체 안을 돌아다니면서 어떤 일을 해야 할 경우가 있습니다. Visitor는 어떤 구조체에 대해 그 안을 돌아다니면서 어떤 일을 하는 것입니다. 이 때, 구조체 1개에 하는 일이 딱 1개라는 보장은 없습니다. 하나의 구조체에 대해 다양한 일들을 할 수 있습니다. 하고 싶은 일이 추가된다고 해서 구조체를 변경하는 것은 무리입니다. 이런 때는 Visitor를 추가하면 됩니다. 예제에서는 PC의 디렉토리-파일 구조에 대해 야동을 찾는 일을 하는 Visitor를 구현해보았습니다. 

2. 예제

--------- Component, Composite, Leaf 등은 Composite 패턴 설명에 썼던 것을 거의 그대로 사용했습니다. 바뀐 부분은 색깔 처리했습니다. 처리한 부분만 보시면 됩니다.


package ch16_Visitor;
import java.util.ArrayList;
import java.util.List;

public abstract class Component implements Acceptor{
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    private String componentName;
    protected List<Component> children = new ArrayList<Component>();
    public Component(String componentName) {
        this.componentName = componentName;
    }
    public String getComponentName() {
        return componentName;
    }
    public abstract void add(Component c);
    public List<Component> getChildren(){
        return children;
    }
}

package ch16_Visitor;
public class Composite extends Component {
    public Composite(String componentName) {
        super(componentName);
    }
    @Override
    public void add(Component c) {
        children.add(c);
    }
}

package ch16_Visitor;
public class Leaf extends Component{
    public Leaf(String componentName) {
        super(componentName);
    }
    @Override
    public void add(Component c) {
        throw new UnsupportedOperationException();
    }
}
------------------ Visitor를 받아들일 수 있는 구조체 ---------------- 
package ch16_Visitor;

public interface Acceptor {
    void accept(Visitor visitor);
}
------------------ Acceptor를 방문하는 Visitor---------------- 
package ch16_Visitor;

public interface Visitor {
    void visit(Acceptor acceptor);
}
------------------ 야동 찾는 YadongFinder ---------------- 
package ch16_Visitor;
import java.util.ArrayList;
import java.util.List;
public class YadongFinder implements Visitor {
    private List<String> yadongList = new ArrayList<String>();
    private List<String> currentList = new ArrayList<String>();
    public void visit(Acceptor acceptor) {
        if (acceptor instanceof Composite) {
            Composite composite = (Composite) acceptor;
            currentList.add(composite.getComponentName());
            List<Component> children = composite.getChildren();
            for (Component component : children) {
                component.accept(this);
            }
            currentList.remove(currentList.size()-1);
        }else  if (acceptor instanceof Leaf) {
            Leaf leaf = (Leaf) acceptor;
            doSomething(leaf);
        }
    }
    protected void doSomething(Leaf leaf){
        if (isYadong(leaf)) {
                String fullPath = getFullPath(leaf);
                yadongList.add(fullPath);
            }
    }
    protected String getFullPath(Leaf leaf) {
        StringBuilder fullPath = new StringBuilder();
        for (String element : currentList) {
            fullPath.append(element).append("\\");
        }
        return fullPath.append(leaf.getComponentName()).toString();
    }
    private boolean isYadong(Leaf leaf) {
        return leaf.getComponentName().endsWith(".avi");
    }

    public List<String> getYadongList() {
        return yadongList;
    }
}
------------------ 테스트 클래스 ----------------  
package ch16_Visitor;

public class Test {
    public static void main(String[] args) {
        Composite main = createComposite();
        YadongFinder visitor = new YadongFinder();
        visitor.visit(main);
        for (String string : visitor.getYadongList()) {
            System.out.println(string);
        }

    }

    private static Composite createComposite() {
        Composite main = new Composite("C:");
        Composite sub1 = new Composite("Program Files");
        Composite sub2 = new Composite("WINDOWS");
        Composite sub11 = new Composite("Pruna");
        Composite sub21 = new Composite("system32");
        Composite sub111= new Composite("Incoming");

        Leaf leaf1111 = new Leaf("강호동 닮은여자-짱이쁨.avi");
        Leaf leaf1112 = new Leaf("EBS야동특강.avi");
        Leaf leaf211 = new Leaf("야메떼-다이조부.avi");
        Leaf leaf212 = new Leaf("이건 야동아님.jpg");
        
        main.add(sub1);
        main.add(sub2);
        sub1.add(sub11);
        sub2.add(sub21);
        sub11.add(sub111);

        sub111.add(leaf1111);
        sub111.add(leaf1112);
        sub21.add(leaf211);
        sub21.add(leaf212);
        return main;
    }
}
---------------- 테스트 결과 -------------
C:\Program Files\Pruna\Incoming\강호동 닮은여자-짱이쁨.avi
C:\Program Files\Pruna\Incoming\EBS야동특강.avi
C:\WINDOWS\system32\야메떼-다이조부.avi

위의 예제에서 중요한 것은 Visitor의 visit(Acceptor) 와  Acceptor의 accept(Visitor)  입니다.

Visitor는 visit(Acceptor) 를 가지고 있고, Acceptor는 accept(Visitor) 를 가지고 있습니다. 둘의 차이가 헤깔립니다. 게다가 accept(Visitor) 를 구현해 놓은 것을 보면 아래와 같이 그냥 Visitor한테 자기 자신을 던져버리는 게 하는 일의 전붑니다.

public void accept(Visitor visitor) {
    visitor.visit(this);
}

이렇게 해 놓은 이유는 구조체를 돌아다닐 수 있게 하기 위한 것입니다. 실제로 구조체를 돌아다니는 일은 Visitor에서 담당하게 됩니다. 만약 이렇게 해놓지 않았다면, Visitor 안에서 구조체를 돌기 위해 재귀적인 호출을 해야만 복잡한 구조를 다 돌 수 있습니다. YadongFinder의 visit(Acceptor) 를 보면, 재귀적인 호출은 없습니다. 일반적으로 accept(Visitor)의 구현은 위와 같으며 달라질 일이 거의 없습니다.

Visitor의 visit(Acceptor)나 Acceptor의 accept(Visitor) 중 하나는 구조체를 도는 역할을 해야합니다. 구조체를 도는 역할은 Visitor의 visit(Accept)에 맡기는 것이 좋습니다. 구조체를 돌면서 하는 일 뿐만 아니라 "구조체를 도는 방법"도 다른 게 할 수도 있기 때문입니다. 위의 예제에서 YadongFinder는 아무래도 엄마가 짠 것 같습니다. 만약에 아들이 짰다면 구조체를 돌 때 "C:\Program Files\Pruna\Incoming\" 과 같은 비밀스러운 디렉토리는 슬쩍 뛰어넘는 로직을 짤 수 있었겠지요.

그러나 일반적으로 순화하는 로직은 거의 바뀌지 않습니다. 위의 예제에서는 YadongFinder를 상속 받아 doSomething(Leaf) 만 override 하면 뭔가 새로운 Visitor를 만들 수 있습니다.

3. 기타

visit(Acceptor) 안 쪽에서 instance of 로 어떤 클래스인지를 찾아냅니다. 이것은 visit(Composite) 와 visit(Leaf)로 분리시켰더라면 굳이 instance of를 쓸 필요가 없었을 것입니다. 하지만, 그렇게되면 Acceptor라는 인터페이스가 무의미해집니다.

Visitor와 Acceptor는 매우 밀접하게 묶여있습니다. 하지만 사실 Visitor의 구현체와 구조체 사이도 꽤 끈끈하게 묶여있습니다. YadongFinder 안에서 instance of로 체크한 것은 전부 구조체에서 정의된 클래스들(Composite, Leaf) 입니다.

테스트 코드의 YadongFinder를 선언하는 부분을 보면,
YadongFinder visitor = new YadongFinder();  와 같이 되어있습니다. 왜
Visitor visitor = new YadoingFinder(); 라고 인터페이스로 정의를 하지 않았을까요?

YadongFinder의 getYadongList() 부분이 포인트입니다. visitor는 구조체를 돌아다니면서직접 일을 할 수도 있습니다.( 예를들어, YadongFinder를 조금만 수정하면 YadongRemover 로 만들 수도 있습니다.) 하지만 돌아다니면서 뭔가를 수집하는 것과 같이 직접 구조체를 수정하지 않고 단지 정보만 수집하는 경우가 있는데, 그런 경우는 수집한 정보를 다시 뽑아서 사용할 수 있는 방법을 제공해야 합니다. Test 클래스에서 야동 리스트를 찍는 부분을 보시면 됩니다.

'개발 > 디자인패턴(외부글)' 카테고리의 다른 글

Visitor  (0) 2011.09.26
Mediator  (0) 2011.09.26
Builder  (0) 2011.09.26
FlightWeight  (0) 2011.09.26
ProtoType  (0) 2011.09.26
Observer  (0) 2011.09.26

댓글을 달아 주세요

posted by 동건이 2011. 9. 26. 19:24

Mediator

자바 디자인 패턴 15 - Mediator

1. Mediator 패턴은..

비행기가 이착륙하다가 충돌하는 일은 좀체로 일어나지 않습니다. 비행기들끼리 서로 통신하지 않는데도 말이죠. 각각의 비행기는 관제탑하고만 통신을 하고, 관제탑이 각각의 비행기에게 착륙해도 된다 또는 안 된다 식으로 메시지를 보내줍니다. 비행기들끼리 서로서로 직접 통신을 한다면 통신할 경우의 수가 무진장 많아져서 혼란스럽게 됩니다. Mediator 패턴은 관제탑과 같이 통신을 집중시킴으로써 통신의 경로를 줄이고 단순화시키는 역할을 합니다.

2. 예제

------------------ 관제탑 역할을 하는 ControlTower (활주로 역할도 함) ---------------- 
package ch15_Mediator;

public class ControlTower {
    private volatile  boolean inUse;
    
    public synchronized boolean getPermission(){
        if (inUse) {
            return false;
        }else{
            inUse = true;
            return true;
        }
    }
    
    public void land(Airplane airplane) throws InterruptedException{
        int seq = airplane.getSeq();
        System.out.println(seq +"번 비행기 착륙 시작");
        Thread.sleep(50L);
        System.out.println(seq + "번 비행기 착륙 끝");
        inUse = false;
    }
}
------------------ 착륙허가를 받아야하는 Airplane ---------------- 
package ch15_Mediator;

public class Airplane extends Thread {
    private final ControlTower tower;
    private final int seq;

    public Airplane(ControlTower tower, int seq) {
        this.tower = tower;
        this.seq = seq;
    }

    public int getSeq() {
        return seq;
    }

    @Override
    public void run() {
        try {
            while (!tower.getPermission()) {
                // System.out.println(seq +"번 째 비행기 대기 중.");
                Thread.sleep(10L);
            }
            tower.land(this);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

------------------ 테스트 클래스 ---------------- 
package ch15_Mediator;

public class Test {
    public static void main(String[] args) {
        ControlTower tower = new ControlTower();
        Airplane[] airplanes = new Airplane[10];
        for (int i = 0; i < airplanes.length; i++) {
            airplanes[i] = new Airplane(tower, i);
        }
        for (Airplane airplane : airplanes) {
            airplane.start();
        }
    }
}

위 프로그램을 실행시키면, 비행기가 동시에 활주로를 사용하는 일은 발생하지 않습니다. 10대의 비행기는 모두 ControlTower를 가지고 있는데, 이는 동일한 ControlTower입니다. 즉, 비행기들끼리는 통신하지 않고 관제탑하고만 통신해서 충돌을 방지하게 되는 것입니다. 
위의 예제에서 ControlTower는 모든 통신의 중계 역할을 합니다. 이를 Mediator(중계자)라 합니다. 그리고, Airplane은 Mediator와 통신을 하는 역할을 합니다. 이를 Colleague(사전적인 의미는 동료이지만, Mediator와 통신하는 객체들이라고 생각하시면 됩니다.) 라고 합니다.

위의 예제를 좀 더 확장해서 생각해 봅시다.

첫번째로 Airplane 은 ControlTower가 한 번 세팅이 되면 바꿀 방법이 없습니다. 위의 예제만으로는 비행기는 일회용이겠죠. 다른 공항에 착륙하고자할 때는 비행기가 통신할 관제탑이 바뀔 수도 있습니다. 그래서 보통은 Colleague 클래스는 setMediator 와 같은 메쏘드를 가지고 있습니다.

두번째로는 callback입니다. 예제에서는 비행기들이 관제탑에 "나 착륙해도 되?" 라고 계속 물어봅니다. "된다"라는 대답을 들을 때까지! 관제탑에서는 정신없습니다. (녹색부분의 주석을 풀어보면 얼마나 정신없는 지 바로 보입니다.)  
질문 방식을 바꾸면 어떨까요? 비행기가 "나 착륙할라고 하는데 활주로 비면 연락줘. 기둥기께" 라고 관제탑에 메시지를 보내고 관제탑은 그 비행기를 착륙 대기 리스트에 추가시켜 놓고, 리스트 앞에서 부터 각각의 비행기에게 "너 인제 착륙해도 된다"는 메시지를 보내면 됩니다. 이렇게 하면 통신 횟수를 확 줄일 수 있습니다.
callback은 명령을 내리고 임무를 완료하면 다시 연락하라는 겁니다. 짜장면 주문을 예로 들어보겠습니다. 짜장면집에 전화를 걸고, 주문을 합니다. 우리는 짜장면이 올 때까지 딴짓을 합니다. 짜장면이 배달오면 배달원이 초인종을 누릅니다. "주문" 이 명령이고, "배달"이 임무 완료입니다. "내"가 "짜장면집"에 명령을 하고 "짜장면집"은 그 명령을 수행합니다. "짜장면집"이 임무를 완수하면 "나"한테 "다시 연락"을 줍니다. 임무가 수행되는 동안 "나"는 "짜장면집"에서 짜장면을 잘 만들고 있는 지 신경을 쓰지 않습니다.

 세번째는 활주로가 1개 뿐이라는 겁니다. 활주로가 여러개라면 Runway 라는 활주로 클래스를 만들어야 합니다. 이 활주로는 당연히 ControlTower와 통신을 해야 합니다.  ControlTower가 어떤 비행기가 착륙 요청했다는 정보를 활주로에 알려주고, 비행기한테는 어떤 활주로에 착륙해라 라고 비행기와 활주로 사이에 관계를 맺어줍니다. 실제 착륙 작업에서는 더 이상 관제탑이 관여할 게 없습니다. 다만, 착륙이 완료 되면 활주로한테 이제 활주로가 다시 사용가능하다는 callback은 받아야겠죠. 
이 경우 Runwary도 Colleague가 되어야 합니다. Colleague는 한 가지 특정타입(예제의 경우 Airplane)으로 국한될 필요는 없습니다.
Airplane의 getPermission은 getAvailableRunway로 바뀌고 리턴 타입역시 Runway로 바뀌어야겠습니다. 사용가능한 활주로가 없을 때는 null을 리턴한다거나 하면 되겠습니다.
그리고 land 메쏘드도 ControlTower가 아닌 Runway로 옮겨가야겠지요. 그리고 land 안에는 ControlTower에게 착륙이 끝났다는 정보를 전달할 수 있는 로직이 필요합니다.

3. 수 많은 Adapter가 필요한 경우

A,B,C 3개 회사가 합병을 했다 칩시다. 각각의 회원 정보를 다음과 같은 인터페이스로 정의해서 사용합니다.

interface ACompanyUser{     String getName();  }
interface BCompanyUser{     String getName();  }
interface CCompanyUser{     String getName();  }

이제 하나가 된 만큼 회원 정보를 서로 공유해야 하는데, 각 회사의 시스템은 예전 자기 회사의 인터페이스만을 받아들이도록 되어있습니다. 서로 캐스팅이 불가능한 객체를 캐스팅하고 싶을 때 Adapter 패턴을 씁니다. 다음과 같은 6개의 Adapter가 필요합니다.

AToBUser
AToCuser
BToAUser
BToCUser
CToAUser
CToBUser

아... 복잡해지기 시작합니다. 중계 객체를 하나 두면 어떨까요? 중계 객체를 M이라 합시다. 즉, A에서 B로 바꾸려면 위에서는 AToBUser 를 통해서 바꾸면 되었지만, 이제 AToM , MToB와 같이 두 단계를 거쳐서 만들면 됩니다. 단계가 늘어났지만 뭔가 좋은 게 있을 겁니다.

AToM
BToM
CToM
MToA
MToB
MToC

얼레? 똑같이 6갭니다. 그러나 갯수가 많아지면 얘기가 달라집니다. N개의 회사가 있다고 하면, 직접 변환을 할 경우 N*(N-1) 개의 인터페이스가 필요합니다. 그러나, 중계 객체를 만들면 2*N개만 있으면 됩니다. 4개의 회사의 경우 12개 - 8개 , 5개 회사는 20개-10개 와 같이 갯수가 많아 질수록 중계 객체를 두는 게 유리해집니다.

'개발 > 디자인패턴(외부글)' 카테고리의 다른 글

Visitor  (0) 2011.09.26
Mediator  (0) 2011.09.26
Builder  (0) 2011.09.26
FlightWeight  (0) 2011.09.26
ProtoType  (0) 2011.09.26
Observer  (0) 2011.09.26

댓글을 달아 주세요

posted by 동건이 2011. 9. 26. 19:16

Builder

자바 디자인 패턴 14 - Builder

1. Builder 패턴은..

뭔가가 만들어 지는 과정은 꽤나 복잡할 수가 있습니다. 게다가 그 복잡한 과정이 순서대로 실행되어야 할 때도 있습니다. 객체의 생성에 있어서 이런 복잡한 과정들을 분리해 내는 것이 Builder 패턴입니다. 

2. 예제

---------------- 복잡한 과정을 거쳐서 만들어 지는 객체가 될 Hero 클래스 ---------------- 

package ch14_builder;

public class Hero {
    private String armSource;
    private String legSource;
    private String name;
    
    public Hero(String name) {
        super();
        this.name = name;
    }
    public void setArmSource(String armSource) {
        this.armSource = armSource;
    }
    public void setLegSource(String legSource) {
        this.legSource = legSource;
    }
    public void showResult(){
        System.out.println(armSource +"로 만든 팔과 " + legSource +"로 만든 다리를 가진 " + name);
    }
}

---------------- 복잡한 Hero 객체를 만들어내기 위한 객체 생성과정을 관리하는 Builder 인터페이스 ---------------- 

package ch14_builder;

public interface Builder {
    void makeArm();
    void makeLeg();
    Hero getResult();
}

---------------- 복잡한 Hero 객체를 실제로 만들어내는 Builder의 구현체인 배트맨 찍어내는 클래스 ------------------

package ch14_builder;

public class BatmanBuilder implements Builder {
    private Hero batman;
    BatmanBuilder(){
        batman = new Hero("배트맨");
    }
    public void makeArm() {
        batman.setArmSource("돈지랄");
    }
    public void makeLeg() {
        batman.setLegSource("돈지랄");
    }
    public Hero getResult() {
        return batman;
    }
}

---------------- Builder를 관리해 주는 Director ---------------- 

package ch14_builder;

public class Director {
    private Builder builder;
    public Director(Builder builder) {
        this.builder = builder;
    }
    public void build(){
        builder.makeArm();
        builder.makeLeg();
    }
    public Hero getHero(){
        return builder.getResult();
    }
}

---------------- Director를 이용해 Hero를 찍어내는 Test클래스 -------------

package ch14_builder;

public class Test {
    public static void main(String[] args) {
        Builder builder = new BatmanBuilder();
        Director director = new Director(builder);
        director.build();
        Hero hero = director.getHero();
        hero.showResult();
    }
}

---------------- 테스트 결과 -------------
돈지랄로 만든 팔과 돈지랄로 만든 다리를 가진 배트맨

Hero라는 클래스가 있습니다. 이 클래스는 그냥 생성자만 호출해서는 아무 쓸모없는 클래스입니다. 이런 저런 정보들이 많이 쎄팅이 되어야 비로소 쓸만한 객체가 됩니다.(예제에서는 setArmSource()과 setLegSource()와 같은 게 그런 복잡한 세팅 과정에 관여하는 메쏘듭니다.) 따라서 이 클래스의 객체를 생성하는 과정은 매우 번거롭습니다.
이런 번거로운 과정을 Director에서 간단하게 build()라는 메쏘드로 해결을 하려고 합니다. build()라는 메쏘드는 참 간단한 것 같은데, 번거로운 과정을 어떻게 다 커버하느냐는 결국 Builder에 위임해서 해결합니다.
Builder는 비교적 세부적인 사항들을 다룹니다. 이에 비해 Director는 좀 더 포괄적인 과정은 다룹니다. 위의 예제의 경우는 Builder는 팔은 어떻게 만들어 지고 다리는 어떻게 만들어지는 지 등과 같은 것을 다루며(세부적인 사항인 makeArm(),makeLeg()와 같은 메쏘드), Director는 팔을 만들고 다리를 만들면 대략 Hero 하나 완성시킬 수 있다는 전체적인 로직(포괄적인 과정인 build() 메쏘드)을 다룹니다. 즉, Director는 다른 Hero를 만드는데도 활용할 수 있지만, Builder는 각각의 Hero에 국한됩니다.

위의 예제는 예제인 만큼 간단하게 만들었습니다만, Hero를 만들기 위해서 수십수백 가지의 정보가 세팅되어야 한다고 칩시다. 이럴 때, Hero의 생성자에 그런 정보들을 다 세팅해줄 수는 없습니다.

3. UML을 벗어나서...

위에서는 등장인물이 Builder(interface)와 그 구현체들, 그를 관리하는 Director 그리고 만들어지는 생산품(Hero) 등이 있었습니다. 그러나, 이는 그냥 전형적이 UML 모냥새를 나타내는 것 뿐이고, Builder 패턴에 있어서는 저런 UML을 벗어나는 경우들이 허다합니다.
Builder에서의 포인트는 "뭔가 복잡한 과정을 거쳐 만들어지는 객체가 있는데, 이때 이 복잡한 과정을 Builder에게 담당시키겠다"는 것입니다. 따라서 Builder와 Product 두 개만으로도 구성될 수 있습니다. 즉, Builder 자체가 Abstract Class나 인터페이스가 아니라 그냥 클래스 일수도 있습니다.
StringBuilder와 같은 것이 대표적인 예입니다. StringBuilder는 이름 그대로 String을 만들어냅니다. 코드를 봅시다.

String ab = new StringBuilder("a").append("b").toString();

여기서 StringBuilder는 결국 "ab"라는 String을 만들기 위한 것입니다. StringBuilder 자체가 의미를 가지는 것이 아니라, 만들어 내는 String이 의미를 가지는 것입니다.

4. Builder를 쓰면 좋은 경우

같은 클래스의 인자를 여러 개 받은 Constructor를 생각해봅시다.

public class Person{
    private final String name;
    private final String city;
    public Person(Stirng name, String city){ //인자가 둘 다 String
        this.name = name;
        this.city = city;
    }
    //getter는 생략.
}

이 코드는 다음과 같이 호출되어야 합니다.
Person p = new Person("홍길동", "서울");

그런데, 인자가 둘 다 String이라 다음과 같은 실수가 있을 수도 있죠.
Person p = new Person("서울", "홍길동"); // 앞 뒤가 바뀜!

이러면 "서울"에 사는 "홍길동"씨가 아니라, "홍길동"에 사는 "서울"씨가 만들어집니다. 

Person p = new PersonBuilder().setName("홍길동").setCity("서울").build();

와 같이 호출된다면, 앞 뒤가 바뀔 가능성이 별로 없겠지요.

(PersonBuilder는 생략합니다.)

이번에는 다음과 같은 복잡한 클래스를 생각해 봅시다.

public class Complex(){
    private Object member1;
    등등등 수많은 member 변수..
    public void set1(Object arg) { member1 = arg;}
    등등등 수많은 setter..
    public Object get1() { return member1;}
    등등등 수많은 getter..
}

------ Complex를 만들어 내는 클라이언트 ---- 
Complex complex = new Complex();
complex.set1(...);
여러 개의 setter 호출....

------ Complex를 가져다 쓰는 클라이언트 ---- 
complex.get1();
여러 개의 getter 호출

이 클래스의 사용 코드를 보면, 복잡하게 만들어내는 부분과 가져다가 사용하는 부분이 명확하게 분리가 되어있습니다. 따라서 다음과 같은 요구가 있을 수 있습니다.

1. 한번 세팅된 값은 변하면 안 된다.
2. getter는 모든 member 변수가 세팅이 된 후에만 사용할 수 있다.

모든 setter는 수정하려는 변수가 세팅이 되어있는지 확인해야 하고, 모든 getter에서는 모든 member 변수가 세팅이 되었는지 확인해야 합니다. 이런 경우 코드가 다음과 같이 좀 지저분하게 수정되어야 합니다.

public class Complex(){
    private Object member1;
    등등등 수많은 member 변수..
    private boolean completed(){
        //member 들이 세팅이 전부 완료되었는 지를 체크해서 리턴.
    }


    public void set1(Object arg) { if(member1 != null) throw new IllegalStateException("세팅 중복") member1 = arg;}
    등등등 수많은 setter..
    public Object get1() { if(!completed()) throw new IllegalStateException("세팅 미완료") return member1;}
    등등등 수많은 getter..
}

모든 getter 들에서 세팅이 완료되었는지 체크하는 로직이 들어가야 합니다. completed() 안의 부분을 효율적으로 바꿀 수는 있지만, getter에서 IllegalStateException이 발생하는 것은 근본적으로 막을 수는 없습니다.

여기서의 문제는 Complex 클래스를 한 번에 만들어내지 못한다는 것이었습니다. 이럴 경우 만들어내는 복잡한 과정을 ComplexBuilder를 만들어서 해결하면 됩니다. 또 ComplexBuilder는 오로지 Complex 클래스를 만들어내기 위한 클래스입니다. 따라서 ComplexBuilder를 분리하면 됩니다.

public class ComplexBuilder{
    private Object member1;
    등등등 수많은 member 변수..
    public void set1(Object arg) { member1 = arg;}    
    등등등 수많은 setter..
    //여기는 getter가 있을 지 없을 지 생각 좀 해보고 필요에 따라 넣던지 빼던지...
    public Complex build(){
        //Complex 객체를 만드는 과정을 전부 넣어둠.
    }
}

public class Complex(){
    Complex(){} //public constructor를 제공하지 않음.
    
    private Object member1;
    등등등 수많은 member 변수..
    void set1(Object arg) { member1 = arg;} // public이 아님!!
    등등등 수많은 setter..
    public Object get1() { return member1;}
    등등등 수많은 getter..
}

위와 같이 2개의 클래스를 같은 패키지에 넣어두고 쓰면 됩니다.

이를 사용하는 코드는 아래와 같이 됩니다.

------ Complex를 만들어 내는 클라이언트 ---- 
ComplexBuilder cb = new ComplexBuilder();
cb.set1(...);
여러 개의 setter 호출....
Complex complex = cb.build();

------ Complex를 가져다 쓰는 클라이언트 ---- 
complex.get1();
여러 개의 getter 호출

일단 Complex는 public constructor를 제공하지 않도록 했습니다. 잘못된 객체 생성을 막고, 오로지 ComplexBuilder를 통해서만 만들어지게 합니다. 또 Complex의 setter 들도 public이 아닙니다. 즉, 외부에서 변수를 세팅하지 못하도록 막았습니다. 따라서 멤버 변수가 변경될 수 있는 가능성을 근본적으로 막아버렸습니다. 그리고 getter에서 member 변수가 세팅이 되었는지를 일일이 확인할 필요도 없어졌습니다.

간략히 정리하면, 복잡하게 만들어지는 Immutable 클래스는 Builder를 통해 만들면 편하다는 얘기죠.

5. Factory 패턴과의 차이점

Factory와 Builder는 모두 객체 생성에 관련된 패턴이고 둘이 좀 비슷해보이기도 합니다. 그러나 용도가 좀 다릅니다.

Factory는 일반적으로 다음의 두 가지 경우에 해당하는 경우에 사용합니다.

1. 리턴 타입이 일정하지 않은 경우. 예를들어, create("소") 라고 하면 Cow 클래스의 인스턴스를 리턴하고, create("개")라고 하면 Dog 클래스의 인스턴스를 리턴하는 것처럼 인자에 따라 리턴 타입이 바뀌는 경우.
2. 대략 비슷하지만 멤버가 살짝 다른 경우. Boolean.valueOf(String arg) 와 같은 경우 리턴 타입은 모두 Boolean 이지만, 속의 내용이 조금 다름. 이 경우는 대부분 Singleton 패턴으로 처리됨.

그러나 Builder는 객체 생성과정의 복잡성을 떠넘기는 게 포인트입니다.

'개발 > 디자인패턴(외부글)' 카테고리의 다른 글

Visitor  (0) 2011.09.26
Mediator  (0) 2011.09.26
Builder  (0) 2011.09.26
FlightWeight  (0) 2011.09.26
ProtoType  (0) 2011.09.26
Observer  (0) 2011.09.26

댓글을 달아 주세요

posted by 동건이 2011. 9. 26. 19:03

FlightWeight

자바 디자인 패턴 13 - Flyweight

1. Flyweight 패턴은..

Flyweight 는 동일한 것을 공유해서 객체 생성을 줄여 가볍게 만드는 것입니다. 클래스 별로 factory를 씁니다. 그리고 그 factory에서는 자신이 찍어내는 객체들을 관리합니다. 이미 가지고 있는 객체에 대한 요청이 들어왔을 때는 관리하고 있던 객체를 던져주고, 가지고 있지 않은 것을 요청하면 새로 객체를 만들어 관리 리스트에 추가시키고 던져줍니다.

2. 예제

---------------- Person class 및 Person을 Flyweight로 관리하는 Factory -------------

package ch13_Flyweight;
import java.util.HashMap;
import java.util.Map;
public class PersonFactory {
    private static Map<String, Person> map = new HashMap<String, Person>();
    public synchronized static Person getPerson(String name){
        if (!map.containsKey(name)) {
            Person tmp = new Person(name);
            map.put(name, tmp);
        }

        return map.get(name);
    }
    public static class Person {
        private final String name;
        private Person(String name) {
            this.name = name;
        }
        public String getName() {
            return name;
        }
    }
}

---------------- Flyweight가 적용된 factory를 사용하는 Test클래스 -------------

package ch13_Flyweight;
import ch13_Flyweight.PersonFactory.Person;
public class Test {
    public static void main(String[] args) {
        Person p1 = PersonFactory.getPerson("홍길동");
        Person p2 = PersonFactory.getPerson("김말자");
        Person p3 = PersonFactory.getPerson("홍길동");
        
        System.out.println(p1 == p2);
        System.out.println(p1 == p3);
    }
}

---------------- 테스트 결과 -------------
false
true

테스트 코드에서 p1과 p3는 둘다 "홍길동"이란 String을 이용해서 객체를 주문합니다. 같은 String으로 객체를 주문했으므로 같은 객체가 리턴됩니다. 두번째 요청에서는 새로 생성하지 않습니다.

위에서 관리하려는 클래스는 Person입니다. Person은 PersonFactory에 의해 관리됩니다. 다시 말해 Person이 Flyweight 를 담당하고, PersonFactory가 Flyweight Factory를 담당합니다. 

예제에서는 Map을 이용해서 Person의 인스턴스를 관리합니다.(일반적인 Flyweight는 다 Map으로 관리합니다.) Map의 key, value가 어떤 클래스로 결정되는지 살펴보죠. value는 당연히 리턴 타입과 같아야 하지만, key를 무엇으로 쓸 것인지는 깊이 생각해봐야 합니다. 역시 HashMap을 사용하고 있으므로, key로 사용할 클래스에는 hashCode()와 equals()가 잘 구현되어있어야 합니다.

PersonFactory에 getPerson() 메쏘드는 sysnchronized로 처리가 되어있습니다. HashMap이 아니라 Hashtable을 쓰면, 알아서 synchonized가 걸리지만, 그건 Hashtable의 메쏘드 안에서만 걸린다는 뜻입니다. 예제에서 녹색처리된 if 블럭이 하나의 synchronized 블럭에 들어가 있어야 하기 때문에 메쏘드에 synchronized가 걸려 있어야 합니다. Hashtable로 해결해서는 안 됩니다. 다시 정리를 하면, if 블럭 안에서 하는 일은 두 단계로 되어있습니다. 이미 있는 놈인지 체크를 하고(1단계) 없으면 새로 만듭니다.(2단계) 1,2 단계가 합쳐져서 하나의 synchronized 안에 들어가야 한다는 것입니다. Hashtable을 쓰면, 각각의 단계별로 synchronized가 걸립니다.

3. Flyweight  추가 사항

Flyweight 클래스(여기서는 Person)은 가능하면 생성자를 외부로 공개하지 않는 것이 좋습니다. 몇 가지 방법이 있습니다.
첫째는 위에서 사용한 것처럼 Flyweight Factory에서 내부 클래스로 가지고 있으면서 생성자를 외부로 공개하지 않는 방법입니다.
두번째는 Flyweight의 클래스 선언은 public으로 생성자는 default(package-privae 또는 friendly 라고도 합니다. 접근자를 선언하지 않는 거죠) 로 선언하고, Flyweight Factory 클래스를 같은 패키지 안에 넣는 방법입니다.

4. JAVA API에 있는 Flyweight 

일반적으로 immutable 타입만을 멤버 변수로 가지고 어떤 메쏘드를 제공하는 경우에도 flyweight를 사용합니다. Servlet 이 그 대표적인 예입니다. 하나의 servlet에 여러 쓰레드가 동시에 접근할 수 있습니다. 하지만, 이 요청들을 처리하는 것은 하나의 인스턴스 입니다. javax.servlet.Servlet 인터페이스를 기준으로 보면, service 메쏘드는 여러 쓰레드에서 동시에 실행시킬 수 있습니다.
따라서 보통 Servlet의 경우는 멤버 변수에 의존하는 로직을 만들면 안 됩니다. 아주 미세한 시간 차이로 꼬일 수 있습니다. 사용자 정보를 출력하는 Servlet의 경우 사용자 정보를 Servlet에 저장하게 되면, 다른 사용자가 그 Servlet에 접근했을 때 보여질 수도 있습니다.

'개발 > 디자인패턴(외부글)' 카테고리의 다른 글

Mediator  (0) 2011.09.26
Builder  (0) 2011.09.26
FlightWeight  (0) 2011.09.26
ProtoType  (0) 2011.09.26
Observer  (0) 2011.09.26
Facade  (0) 2011.09.26

댓글을 달아 주세요

posted by 동건이 2011. 9. 26. 18:56

ProtoType

자바 디자인 패턴 12 - Prototype

1. Prototype 패턴은..

기존에 만들어진 복잡다난한 인스턴스의 내용이 일부만 살짝 변경된 비스무레한 객체가 필요한 경우에 쓰입니다. 일반적으로 객체를 새로 생성할 때는 new Object()와 같은 방법으로 생성을 합니다. 그러나 그렇게 생성할 경우 기존에 만들어진 것과 유사하다고 해도 결국 모든 정보를 다시 세팅해주어야 합니다. 그러나, clone()을 이용할 경우에는 기존에 만들어진 것을 복사해서 바뀐 부분만 대체해 주면 인스턴스를 생성하기가 쉽습니다. 아주 일반적인 "원형"을 만들어서 그것을 복사한 후 적당히 커스터마이징을 하면 new로 객체를 생성하는 것보다 쉽게 됩니다. 

2. 예제

---------------- 복잡한 정보를 가지고 있는 Complex -------------

package ch12_prototype;
import java.util.Date;
public class Complex implements Cloneable{
    private String complexInfo;

    private Date date;

    public Complex(String complexInfo) {
        this.complexInfo = complexInfo;
    }
    public String getComplexInfo() {
        return complexInfo;
    }
    public void setDate(Date date){
        this.date = new Date(date.getTime());
    }
    public Date getDate() {
        return date;
    }
    @Override
    public Object clone() throws CloneNotSupportedException {
        Complex tmp = (Complex) super.clone();
        return tmp;
    }
}



---------------- 복사를 실행하는 테스트 클래스 -------------

package ch12_prototype;
import java.util.Date;
public class Test {
    public static void main(String[] args) {
        Complex com = new Complex("매우 복잡한 정보");
        try {
            Complex cloned1 = (Complex)com.clone();
            cloned1.setDate(new Date(2008,0,1));

            Complex cloned2 = (Complex)com.clone();
            cloned2.setDate(new Date(2008,2,1));
            
            System.out.println(cloned1.getDate());
            System.out.println(cloned2.getDate());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

---------------- 테스트 결과 -------------
Wed Jan 01 00:00:00 KST 3908
Sun Mar 01 00:00:00 KST 3908


테스트 클래스에는 변수가 3개 등장합니다. com과 cloned1, clone2 입니다. 셋다 Complex 클래스의 인스턴스 들입니다.
com은 소스코드에서는 매우 쉽게 생성해냈지만, 생성하는 과정이 매우 복잡하며, 멤버 변수로 매우 복잡한 것들을 가지고 있다고 가정합니다. 그래서 다시 한 번 똑같은 것을 만들어 내기가 매우 어렵다고 가정합시다.
그렇게 어렵게 만들어진 com과 다 똑같은 데 멤버 변수 한 두개만 다른 값을 가지는 객체를 만들고 싶습니다. cloned1과 cloned2가 바로 그런 인스턴스들입니다. com이 바로 원형이 되는 것이고, cloned1과 cloned2는 그 원형에서 복사해서 만들어진 객체들이며, 실제로 뭔가를 하는 것은 cloned1, cloned2 입니다. com은 cloned1,cloned2를 찍어내기 위한 원형일 뿐입니다.

clone 메쏘드에 대한 구현은 http://iilii.egloos.com/4022941 를 참조하세요. 위의 예제에서는 코드가 너무 길어져서 hashCode()나 equals는 별도로 구현하지 않았습니다. 또 Date의 Constructor로 Date(int, int, int)는 Deprecated 된 거지만 걍 예제니까 씁니다.

3. Prototype 용도 및 일반적인 구현.

스타크래프트 같은 걸 생각을 해봅시다. 건물은 Building 이라는 클래스 하나로 정의된다고 칩시다.(물론, 안 그렇겠지만 그렇다 칩시다.-_-;) 한 명의 플레이어는 커맨드 센터를 여러 개 지을 수 있습니다. 각각의 커맨드 센터끼리는 위치 등 일부 정보만 다르고 생김새, 기본 에너지 등등 다른 건 다 비스무레할 겁니다. 그럴 때 Building이라는 클래스의 속성으로 모든 값들을 다시 세팅하는 것은 굉장히 번거로운 작업이 될 겁니다. 그래서 대략적인 모양새를 갖춘 객체를 하나 생성하고 그 객체를 복사해서 필요한 필드만 다시 세팅하면 쓸만한 커맨드 센터가 만들어집니다.

또 위의 예제에서는 그렇게 구현하지 않았지만 일반적으로 Prototype은 외부로 드러내지 않습니다. 팩토리 패턴과 조합해서 쓰는 게 일반적입니다. Factory 클래스에서 원형을 관리하고, 그 Factory의 create 메쏘드가 호출되면, 원형으로부터 복사해서 외부로 던져주는 겁니다. Prototype은 Factory에서만 관리되고 그 외부로 드러나지 않습니다.

clone() 메쏘드가 호출되어 새로운 객체가 생성되는 시점에 원형이 어찌 생겼는지 크게 신경쓰지 않습니다. 그냥 다짜고짜 복사할 뿐입니다.

'개발 > 디자인패턴(외부글)' 카테고리의 다른 글

Builder  (0) 2011.09.26
FlightWeight  (0) 2011.09.26
ProtoType  (0) 2011.09.26
Observer  (0) 2011.09.26
Facade  (0) 2011.09.26
Chain of responsibility  (0) 2011.09.26

댓글을 달아 주세요

posted by 동건이 2011. 9. 26. 13:30

Observer

자바 디자인 패턴 11 - Observer

1. Observer 패턴은..

어떤 클래스에 변화가 일어났을 때, 다른 클래스에 통보해 주는 패턴입니다. 통보를 하는 "어떤 클래스"가 Observable 이고, 통보를 받는 "다른 클래스"는 Observer입니다. Observable은 여러개의 Observer를 가질 수 있습니다. Observable이 "담임 떴다"를 외치면, Observer은 알아서 그에 걸맞는 행동을 합니다. 어떤 Observer는 만화책을 덮고 교과서를 꺼내고 어떤 Observer는 흘린 침 닦고 일어나서 공부하는 척하고 또 어떤 Observer는 먹던 도시락 치우고 창문 열어 환기를 시킵니다. Observable은 "담임 떴다"까지만 알려주지 "담임 떴으니깐, 누구는 어찌하고 누구는 저찌해라~" 까지 상세한 지시는 하지 않습니다.

2. 예제

---------------- 변화를 통보하는 Observable -------------

package ch11_Observer;
import java.util.Observable;
public class Watcher extends Observable {
    public void action(String string) {
        System.out.println("======="+string+"========");
        setChanged();
        notifyObservers(string);
    }
}

---------------- 변화를 통보받는 직원 -------------

package ch11_Observer;
import java.util.Observable;
import java.util.Observer;
public class Employee implements Observer {
    private String desc;
    public Employee(String desc) {
        this.desc = desc;
    }
    public void update(Observable o, Object arg) {
        if (o instanceof Watcher) {
            System.out.println(desc + "이 일하는 척");
        }
    }
    public String getDesc() {
        return desc;
    }
}

---------------- 변화를 통보받는 사장 끄나풀 -------------

package ch11_Observer;
import java.util.Observable;
import java.util.Observer;
public class Spy implements Observer {
    private Employee employee;
    public Spy(Employee employee) {
        this.employee = employee;
    }
    public void update(Observable o, Object arg) {
        if (o instanceof Watcher) {
            System.out.println("고자질쟁이가 "+employee.getDesc() +"이 놀고 있었다고 고자질.");
        }
    }
}

---------------- 테스트 클래스 -------------

package ch11_Observer;
public class Test {
    public static void main(String[] args) {
        Watcher watcher = new Watcher();
        Employee pc1 = new Employee("만화책보는 놈");
        Employee pc2 = new Employee("퍼질러 자는 놈");
        Employee pc3 = new Employee("포카치는 놈");
        //spy는 pc3을 보고 있음.
        //요놈은 꼰질르기의 대가
        Spy spy = new Spy(pc3);
        
        watcher.addObserver(pc1);
        watcher.addObserver(pc2);
        watcher.addObserver(pc3);
        watcher.addObserver(spy);
        
        watcher.action("사장 뜸.");
        watcher.deleteObserver(pc3);
        watcher.deleteObserver(spy);
        
        watcher.action("사장 뜸.");
    }
}

---------------- 테스트 결과 -------------

=======사장 뜸.========
고자질쟁이가 포카치는 놈이 놀고 있었다고 고자질.
포카치는 놈이 일하는 척
퍼질러 자는 놈이 일하는 척
만화책보는 놈이 일하는 척
=======사장 뜸.========
퍼질러 자는 놈이 일하는 척
만화책보는 놈이 일하는 척

일단 테스트 클래스만 보세요.
사장이 오는 지 감시하는 Watcher가 하나 있습니다. 그리고 3명의 Employee와 사장 끄나풀인 1명의 Spy가 있습니다. 얘네들은 전부 Watcher한테 사장 뜨면 알려달라고 얘기해 놓습니다. addObserver() 가 Observer로 등록하는 부분입니다.
각각의 Employee 들은 사장이 뜨면, 하던 일 멈추고 일하는 척을 합니다. Spy는 사장이 뜨면, 누가 놀고 있었다고 꼰지릅니다. Employee와 Spy는 같은 통보(사장이 떴다는 것)을 받지만 하는 일은 전혀 다릅니다. 이렇게 같은 통보에 전혀 다른 Observer 들을  붙여 버릴 수도 있습니다.
테스트 코드와 테스트 결과를 보면, observer를 등록한 순서와 통보를 받는 순서가 일치하지 않는 것을 알 수 있습니다. 통보를 받는 순서는 등록 순서와 무관합니다. 
사장이 처음 떴을 때, "포카치는 놈"은 Spy한테 고자질 당해서 짤렸습니다. 짤렸기 때문에 더 이상 사장이 뜨던 말던 관심 없습니다. 그래서 Watcher에 Observer로 더 이상 등록되어 있을 필요가 없어졌습니다. 또, Spy는 잘했다고 포상휴가 갑니다. 얘도 더 이상 사장이 뜨는 지 안 뜨는지 통보 받을 필요가 없어졌습니다. 그래서 얘네 둘은 Watcher에 등록된 Observer 리스트에서 제거합니다. 색칠된 deleteObserver() 가 제거하는 부분입니다.
그래서 두 번째로 사장이 떴을 때는 "만화책 보는 놈"과 "퍼질러 자는 놈"만 통보를 받습니다.

다음은 Employee 클래스를 봅시다.
Observer 인터페이스를 구현하고 있습니다. Observer 인터페이스에는 update(Observable , Object) 메쏘드가 정의되어 있습니다.
첫번째 인자 Observable은 update를 호출해준 Observable 을 말합니다. 여기서는 Watcher 클래스가 되겠습니다. 하나의 Observer는 여러 개의 Observable에 등록될 수가 있습니다. "만화책 보는 놈"의 경우는 사장이 떠도 통보를 받아야겠지만, 신간 만화책이 나왔을 때도 통보를 받아야 합니다. 각기 다른 통보인 만큼 할 일이 달라지겠지요. 그러나, update 메쏘드를 각각의 Observable에 대응하도록 여러 개를 만들 수 없기 때문에 어떤 일인지를 Observable을 받아서 파악하고, 그에 걸맞는 행동을 합니다. 예제에서는 그 Observable이 단지 Watcher 클래스의 인스턴스인지 체크하는 방식으로 구현했습니다.
두번째 인자 Object를 통해서는 구체적인 정보를 받을 수 있습니다.  

이번에는 Watcher 클래스를 봅시다.
일단 Observable이란는 클래스를 상속받습니다. Observable 에는 몇 가지 메쏘드가 있습니다만 여기서는 두가지만 썼습니다. setChanged()와 notifyObservers() 입니다. setChanged() 는 변화가 일어났다는 것을 알리는 겁니다. 변화가 일어나지 않으면, 굳이 Observer 들에게 알릴 필요가 없습니다. 예를들어, Watcher는 무조건 감시하고 있는 클래스이기 때문에 잡상인이 뜨던가 사장이 뜨던가 다 압니다. 하지만, 잡상인이 떴을 경우는 Observer들에게 알릴 필요가 없습니다. 사장이 떴을 때만 setChanged()를 호출하고, Observer들에게 알립니다. (사장인지 아닌 지를 판단하는 로직은 코드가 길어져서 뺐습니다.) setChanged()가 호출되지 않고 notifyObservers()가 호출되면, 아무일도 일어나지 않습니다.
다음에 notifyObservers() 메쏘드가 있는데, 이 메쏘는 오버로드되어있습니다. notifyObservers()와 notifyObservers(Object) 두 가지가 있습니다. Object에는 어떤 일이 일어났는지 상세 정보를 담을 수 있습니다. 사장이 떴는지, 부장이 떴는 지 정도의 정보를 담을 수 있겠지요. notifyObservers()는 notifyObservers(null) 과 같습니다. 
어찌되었건 notifyObservers(Object)가 호출이 되면, Observer들에게 전부 알립니다. 그러면, Observer들은 각각 자기가 가진 update() 메쏘드가 호출됩니다.

3. Observer 의 특징

말이 Observer이지 단지 Observerable에게 통보를 받는 입장입니다. 
Observer 들은 Observable에 추가 삭제가 자유롭습니다. Observable 입장에서는 어떤 Observer인지 신경쓰지 않습니다. 단지 어떤 일이 일어났다는 통보만을 합니다. 통보에 대한 반응은 전적으로 Observer가 합니다. update() 메쏘드가 Observer에 있고, notifyObservers() 메쏘드가 Observable에 있는 것이 바로 그런 얘깁니다.

4. JAVA API에 있는 Observer

또, GUI 쪽 얘기를 꺼내게 되는군요. GUI에 있는(awt, swing, swt 등등 전부 비슷합니다.) 각종 버튼, 텍스트 등에 add머시기Listener 메쏘드 들이 잔뜩 있습니다. 이벤트가 일어나면, 뭔가를 호출하라는 것이지요. 예제에서 설명한 것과는 인터페이스가 다소 상이하긴 합니다.
GUI의 event는 Observer 패턴으로 구현되어 있어 이벤트 추가 삭제가 매우 자유롭습니다. 

'개발 > 디자인패턴(외부글)' 카테고리의 다른 글

FlightWeight  (0) 2011.09.26
ProtoType  (0) 2011.09.26
Observer  (0) 2011.09.26
Facade  (0) 2011.09.26
Chain of responsibility  (0) 2011.09.26
Decorate  (0) 2011.09.26

댓글을 달아 주세요

posted by 동건이 2011. 9. 26. 12:07

Facade

자바 디자인 패턴 10 - Facade

1. Facade 패턴은..

여러가지 복잡한 것들을 하나로 간주해서 편하게 다루는 방법입니다.
예를 들어, 우리가 흔히 자동차라고 하는 물건은 바퀴, 엔진, 오디오, 사이드 미러 등등 으로 이루어져있지만 우리는 그냥 다 자동차라고 생각하는 게 편합니다. 차안에서 음악을 듣고 싶으면, 그냥 오디오를 켜면 됩니다. "차량에 내장된 오디오"라는 것을 굳이 명확히 하기는 귀찮죠. 

2. 예제

----------------- 내부구성품 1. TV ----------

package ch10_Facade;
public class TV {
    private boolean turnedOn = false;
    public void turnOn(){
        turnedOn = true;
        System.out.println("TV를 켬.");
    }
    public void turnOff(){
        turnedOn = false;
        System.out.println("TV를 끔.");
    }
    public boolean isTurnedOn(){
        return turnedOn;
    }
}

----------------- 내부구성품 2. 오디오 ----------

package ch10_Facade;
public class Audio {
    private boolean playing = false;
    public void play(){
        playing = true;
        System.out.println("음악을 연주.");
    }
    public void stop(){
        playing = false;
        System.out.println("음악을 멈춤");
    }
    public boolean isPlaying() {
        return playing;
    }
}

----------------- 내부구성품 3. 전등 ----------

package ch10_Facade;
public class Light {
    private int lightness = 0;
    public int getLightness() {
        return lightness;
    }
    public void setLightness(int lightness) {
        System.out.println("밝기를 "+ lightness + "로 변경.");
        this.lightness = lightness;
    }
}

----------------- Facade ----------

package ch10_Facade;
public class Home {
    private Audio audio;
    private Light light;
    private TV tv;
    public Home(Audio audio, Light light, TV tv) {
        this.audio = audio;
        this.light = light;
        this.tv = tv;
    }
    public void enjoyTv(){
        System.out.println("==불을 밝게하고 TV보기.");
        light.setLightness(2);
        tv.turnOn();

    }
    public void enjoyMusic(){
        System.out.println("==불을 약간 어둡게하고 음악듣기.");
        light.setLightness(1);
        audio.play();

    }
    public void goOut(){
        System.out.println("==TV끄고, 음악도 끄고, 불도 끄고 외출하기.");
        if (tv.isTurnedOn()) {
            tv.turnOff();
        }
        if (audio.isPlaying()) {
            audio.stop();
        }
        light.setLightness(0);

    }
}

----------------- 테스트 클래스 ----------

package ch10_Facade;
public class Test {
    public static void main(String[] args) {
        TV tv = new TV();
        Audio audio = new Audio();
        Light light = new Light();
        
        Home home = new Home(audio, light, tv);
        
        home.enjoyTv();
        home.enjoyMusic();
        home.goOut();
    }
}

----------------- 테스트 결과 ----------

==불을 밝게하고 TV보기.
밝기를 2로 변경.
TV를 켬.
==불을 약간 어둡게하고 음악듣기.
밝기를 1로 변경.
음악을 연주.
==TV끄고, 음악도 끄고, 불도 끄고 외출하기.
TV를 끔.
음악을 멈춤
밝기를 0로 변경.

Test 클래스에서는 TV, Audio, Light 등의 클래스의 인스턴스를 가지고 있긴하지만, Home을 생성하는 용도 외에는 사용하지 않습니다.
TV나 Audio 등이 저차원 클래스라면, Home은 저차원 클래스들을 감싸고 있는 고차원 클래스 입니다. 실질적인 저차원 클래스들의 조합은 바로 고차원 클래스인 Home클래스가 담당합니다.(코드의 황토색 부분이 조합시키는 부분입니다.) 클라이언트인 Test클래스는 고차원 클래스만 신경씁니다. 

3. Facade 의 특징

예제에서 굳이 TV, Light, Audio라는 클래스들을 만들지 않고, Home 클래스 안에다가 기능을 다 넣을 수도 있었습니다. 그런데, TV가 꼭 Home에 종속되어야 할까요? 요즘은 핸드폰으로도 TV보는 세상인데요. 
Facade 패턴을 사용하면 최소단위로 클래스를 설계할 수 있습니다. 물론, 지나치게 잘게 쪼개는 것도 그다지 바람직하진 않습니다 . 코드를 다른 데에 복사해서 사용하지 않는 선 정도에서 클래스를 분리시키면 될 것 같습니다.
예제코드의 경우 TV를 분리시키지 않는다면 핸드폰 클래스가 나왔을 때, 핸드폰 클래스의 TV기능을 다시 구현해야하는 사태가 발생합니다.

'개발 > 디자인패턴(외부글)' 카테고리의 다른 글

ProtoType  (0) 2011.09.26
Observer  (0) 2011.09.26
Facade  (0) 2011.09.26
Chain of responsibility  (0) 2011.09.26
Decorate  (0) 2011.09.26
Composite  (0) 2011.09.26

댓글을 달아 주세요

posted by 동건이 2011. 9. 26. 12:00

Chain of responsibility

자바 디자인 패턴 9 - Chain of Responsibility

1. Chain of Responsibility 패턴은..

오션스 일레븐과 같은 류의 영화를 보신 적이 있죠? 전문가들이 몇 명 있습니다. 그러나, 그 전문가들은 할 수 있는 일이 극히 제한되어 있죠. 예를 들어, 해커가 격투에 능하진 않습니다. 해커는 단지 해킹에만 능합니다. 그들은 각각은 할 수 있는 일들이 제한적이지만, 모여있으면 세상만사 다 해결합니다. 각각의 전문가들이 자기가 할 수 있는 일만 하면 되거든요. 만약에 그들 모두가 해결할 수 없는 문제가 발생하면... 오션스 투엘브가 되고, 오션스 써틴이 되고 하면 됩니다. 또 영입하면 되죠 멀..
Chain of Responsiblity 패턴에서는 문제 해결사들이 한줄로 쫙 서있다가 문제가 들어오면, 자기가 해결할 수 있으면 해결하고, 안 되면 다음 해결사에게 문제를 넘겨버립니다.

2. 예제

------------------ 전문가: 상위 클래스 ---------------

package ch09_ChainOfResponsibility;
public abstract class Expert {
    private Expert next;
    protected String expertName;
    public final void support(Problem p){
        if (solve(p)) {
           System.out.println(expertName+ "이(가) " + p.getProblemName()  +"을(를) 해결해 버렸네.");
        }else{
            if (next != null) {
                next.support(p);
            }else{
                System.out.println(p.getProblemName() + "은(는) 해결할 넘이 없다.");
            }
        }
    }
    public Expert setNext(Expert next){
        this.next = next;
        return next;
    }
    protected abstract boolean solve(Problem p);
}

----------- 전문가들이 풀어야할 문제 클래스 -----------

package ch09_ChainOfResponsibility;
public class Problem {
    private String problemName;
    public Problem(String name) {
        this.problemName = name;
    }
    public String getProblemName() {
        return problemName;
    }
}

--------------- 첫번째 전문가 파이터! --------

package ch09_ChainOfResponsibility;
public class Fighter extends Expert {
    public Fighter(){
        this.expertName = "격투가";
    }
    @Override
    protected boolean solve(Problem p) {
        return p.getProblemName().contains("깡패");
    }
}

--------------- 두번째 전문가 해커! --------

package ch09_ChainOfResponsibility;
public class Hacker extends Expert {
    public Hacker(){
        this.expertName = "해커";        
    }
    @Override
    protected boolean solve(Problem p) {
        return p.getProblemName().contains("컴퓨터");
    }
}

--------------- 세번째 전문가 카사노바! --------

package ch09_ChainOfResponsibility;
public class Casanova extends Expert {
    public Casanova(){
        expertName = "카사노바";
    }
    @Override
    protected boolean solve(Problem p) {
        return p.getProblemName().contains("여자") || p.getProblemName().contains("여성");
    }
}

----------------- 테스트 클래스 ------------------

package ch09_ChainOfResponsibility;
public class Test {
    public static void main(String[] args) {
        Problem[] problems = new Problem[5];
        problems[0] = new Problem("덩치 큰 깡패");
        problems[1] = new Problem("컴퓨터 보안장치");
        problems[2] = new Problem("까칠한 여자");
        problems[3] = new Problem("날렵한 깡패");
        problems[4] = new Problem("폭탄");
        
        Expert fighter = new Fighter();
        Expert hacker = new Hacker();
        Expert casanova = new Casanova();
        
        fighter.setNext(hacker).setNext(casanova);
        
        for (Problem problem : problems) {
            fighter.support(problem);
        }
    }
}

--------------- 결과 ----------------

격투가이(가) 덩치 큰 깡패을(를) 해결해 버렸네.
해커이(가) 컴퓨터 보안장치을(를) 해결해 버렸네.
카사노바이(가) 까칠한 여자을(를) 해결해 버렸네.
격투가이(가) 날렵한 깡패을(를) 해결해 버렸네.
폭탄은(는) 해결할 넘이 없다.


등장인물들은 전부 테스트 클래스에 있습니다. 5개의 문제점들이 있고, 3명의 전문가들이 있죠. 테스트 클래스에서는 어떤 전문가가 어떤 문제를 해결하는 지는 관심 없습니다. "문제는 해결만 되면 된다!"가 클라이언트인 테스트 클래스의 입장입니다.

Expert 클래스는 마치 Decorator 패턴 처럼 Expert를 멤버 변수로 가지고 있습니다. 그러나 Decoarator와는 달리 그 값이 null일 수도 있습니다. 다음 전문가가 없을 수도 있는 거죠. 위의 코드에서는 casanova가 마지막 전문가입니다. 즉, casanova는 next라는 변수 값이 null입니다. 
일반적인 set머시기 하는 메쏘드들은 리턴 타입이 void인데, 여기서는 리턴 타입이 Expert입니다. 예제코드처럼 전문가 그룹을 연결시키는 코드를 한줄로 만들기 위해서입니다. 만약 리턴 타입이 void였다면, 황토색 부분은 아래와 같이 두 줄로 바뀔 것입니다.

fighter.setNext(hacker);
hacker.setNext(casanova);

Expert 클래스의 support() 가 하는 일은 자기가 해결할 수 있으면 하고, 못하면 다음 전문가한테 넘기고, 떠넘길 다음 전문가가 없으면, 못한다고 생떼를 쓰는 겁니다. 내부적으로 sovle()메쏘드를 호출합니다.
solve()는 각각의 개별 클래스별로 자기가 해결 가능한지 불가능한지를 판단하는 매쏘드입니다. 당연히 구체적으로 기술해야 하므로 하위 객체에 떠넘깁니다. 

3. Chain of Responsibility의 특징

위의 예제에서는 폭탄 앞에 전문가들이 좌절해야 했습니다. 기존에 있던 애들은 그대로 두고, 폭탄전문가를 한 명 영입하면 일이 해결될 것 같습니다. 폭탄전문가 클래스를 새로 만들어서 테스트 클래스에 넣어주면 일이 해결됩니다. 또 별로 쓸모 없는 전문가를 영입했을 때는 짜르기도 쉽습니다. 전체적인 로직이 바뀌지 않습니다.

템플릿 메쏘드가 숨어있습니다. 테스트 클래스에서는 각 전문가클래스의 solve() 메쏘드를 호출한 적이 없지만, 내부적으로 호출이 됩니다.

문제가 나타났을 때 어떤 전문가가 해결할 것인지 단번에 결정이 되지 않습니다. 일단 모든 문제는 fighter 객체를 거쳐갑니다. 맨 앞에 있으니까요. 따라서 전문가 객체의 순서가 전체적인 수행 속도에 영향을 끼칠 수 있습니다. 가능한한 일반적인 문제해결사들을 앞쪽에 세워두는 게 좋습니다.

4. JAVA API에 있는 Chain of Responsibility

JAVA 1.0의 GUI 에서는 Chain of Responsibility를 사용했었다고 하는군요. 이벤트가 발생했을 때 자기가 해결할 수 있는 지 보고 해결이 안되면 상위 컴포넌트로 던졌다네요. (확인한 적은 없습니다만, 대충 말은 되는 것 같군요.)

'개발 > 디자인패턴(외부글)' 카테고리의 다른 글

Observer  (0) 2011.09.26
Facade  (0) 2011.09.26
Chain of responsibility  (0) 2011.09.26
Decorate  (0) 2011.09.26
Composite  (0) 2011.09.26
Strategy  (0) 2011.09.26

댓글을 달아 주세요

posted by 동건이 2011. 9. 26. 11:55

Decorate

자바 디자인 패턴 8 - Decorator

1. Decorator 패턴은..

기존에 구현되어 있는 클래스에 기능을 추가하기 위한 패턴입니다. 기존에 있던 클래스를 상속하여 만들기 때문에 기존 클래스와 사용법이 크게 다르지는 않습니다.

2. 예제

-------------- 데코레이터 -------------
package ch08_Decorator;
public class Decorator {
    public String getMerong(){
        return "merong";
    }
}

-------------- 데코레이터를 상속 받은 넘 ----
package ch08_Decorator;
public class ChildDecorator extends Decorator{
    private Decorator decorator;
    public ChildDecorator(Decorator decorator){
        this.decorator = decorator;
    }
    @Override
    public String getMerong(){
        return "@" + decorator.getMerong() + "@";
    }
}

-------------- 테스트 클래스 --------------
package ch08_Decorator;
public class Test {
    public static void main(String[] args) {
        Decorator decorator = new Decorator();
        System.out.println(decorator.getMerong());
        Decorator child = new ChildDecorator(decorator);
        System.out.println(child.getMerong());
        Decorator child2 = new ChildDecorator(child);
        System.out.println(child2.getMerong());
    }
}
-------------- 결과 -----------------
merong
@merong@
@@merong@@

데코레이터 패턴에서 상위클래스(Decorator라 합니다.) 와 하위 클래스(ConcreteDecorator라 합니다.) 와의 관계를 알아봅시다.
첫째, 하위 클래스는 상위클래스의 형식을 멤버변수로 가집니다. ChildDecorator 는 Decorator를 멤버변수로 받습니다. 일반적으로 생성자의 인자로 받아서 멤버변수로 쎄팅을 합니다. 별도의 setter를 가지는 경우는 거의 없습니다.
둘째, 하위 클래스는 상위클래스를 상속 받아 상위클래스의 메쏘드를 이용합니다. 하위 클래스의 getMerong() 이라는 메쏘드는 상위 클래스의 getMerong()을 오버라이드하지만, 내부적으로 상위클래스의 getMerong()을 사용하고 있습니다.

3. Decorator가 일반적인 상속과 다른 점

Decorator는 메쏘드의 확장 개념입니다. 멤버 변수로 받은 객체의 메쏘드를 이용하여 그 메쏘드를 확장하는 것입니다.

4. JAVA API에 있는 Decorator

java.io에 있는 InputStream, Reader, OutputStream, Writer 등은 모두 Decorator 패턴으로 구성되어 있습니다.
파일을 Reader로 읽는 경우를 살펴보겠습니다.

Reader reader = new FileReader("파일명");

Reader reader = new BufferedReader(new FileReader("파일명");

파일은 위의 두가지 방법으로 모두 읽을 수 있습니다. 둘다 Reader의 형식으로 받습니다. BufferedReader의 생성자는 Reader를 받아 멤버 변수로 가지고 있으며, Reader를 상속 받습니다. 멤버 변수로 받은 Reader를 이용하여, 버퍼를 이용해서 읽습니다.

'개발 > 디자인패턴(외부글)' 카테고리의 다른 글

Facade  (0) 2011.09.26
Chain of responsibility  (0) 2011.09.26
Decorate  (0) 2011.09.26
Composite  (0) 2011.09.26
Strategy  (0) 2011.09.26
Singleton  (0) 2011.09.26

댓글을 달아 주세요

posted by 동건이 2011. 9. 26. 11:51

Composite

자바 디자인 패턴 7 - Composite

1. Composite 패턴은..

파일 데이터와 같은 일반적인 트리 구조의 데이터 타입을 만드는 것이 Composite 패턴입니다. Composite 패턴에서 주요등장 인물은 3개입니다. 첫째는 상위 컴포넌트. 둘째는 상위 컴포넌트를 상속 받으며 자식 컴포넌트를 가질 수 있는 Composite. 세째는 상위 컴포넌트를 상속 받으며, 하위 컴포넌트를 가질 수 없는 Leaf. 디렉토리가 Composite라면, 파일은 Leaf라고 보시면 됩니다.

2. 예제

이번 것은 소스가 좀 깁니다. 색칠된 부분만 중점적으로 보세요.

----------------- 상위 Component ----------------- 
package ch07_Composite;
import java.util.ArrayList;
import java.util.List;
public abstract class Component {
    private String componentName;
    protected List<Component> children = new ArrayList<Component>();
    public Component(String componentName) {
        this.componentName = componentName;
    }
    public String getComponentName() {
        return componentName;
    }
    public abstract void add(Component c);
    public List<Component> getChildren(){
        return children;
    }
    public String getString(){
        return getString(0);
    }
    private String getString(int depth){
        StringBuffer sb = new StringBuffer();
        if (this instanceof Composite) {
            for (int i = 0; i < depth; i++) {
                sb.append("  ");
            }
            sb.append("+"+getComponentName() +"\n");
            for (Component comp: children) {
                sb.append(comp.getString(depth+1));
            }
        }else{
            for (int i = 0; i < depth; i++) {
                sb.append("  ");
            }
            sb.append("-"+getComponentName()+"\n");
        }
        return sb.toString();  
    }
}

----------------- 하위 Composite(하위 노드 가질 수 있음) ----------------- 
package ch07_Composite;
public class Composite extends Component {
    public Composite(String componentName) {
        super(componentName);
    }
    @Override
    public void add(Component c) {
        children.add(c);
    }
}

----------------- 하위 Leaf(하위 노드 가질 수 없음) ----------------- 
package ch07_Composite;
public class Leaf extends Component{
    public Leaf(String componentName) {
        super(componentName);
    }
    @Override
    public void add(Component c) {
        throw new UnsupportedOperationException();
    }
}

----------------- 테스트 클래스 ----------------- 
package ch07_Composite;

public class Test {
    public static void main(String[] args) {
        Composite main = new Composite("Main");
        Composite sub1 = new Composite("sub1");
        Composite sub2 = new Composite("sub2");
        Composite sub11 = new Composite("sub11");
        Composite sub12 = new Composite("sub12");
        Composite sub13 = new Composite("sub13");
        Composite sub21 = new Composite("sub21");
        Composite sub22 = new Composite("sub22");
        Leaf leaf14 = new Leaf("leaf14");
        Leaf leaf121 = new Leaf("leaf121");
        
        main.add(sub1);
        main.add(sub2);
        sub1.add(sub11);
        sub1.add(sub12);
        sub1.add(sub13);
        sub2.add(sub21);
        sub2.add(sub22);
        sub1.add(leaf14);
        sub12.add(leaf121);
        
        System.out.println(main.getString());
    }
}

----------------- 테스트 결과 ----------------- 
+Main
  +sub1
    +sub11
    +sub12
      -leaf121
    +sub13
    -leaf14
  +sub2
    +sub21
    +sub22

Component는 멤버 변수로 List<Component>를 가집니다. 이것이 트리 구조를 만드는 포인트입니다. Component에 있어서 중요한 메쏘드는 add()와 getChildren()입니다. add()의 인자는 Component 이고, getChildren()의 리턴 타입도 List<Component>입니다.  Composite인지 Leaf인지 구분하지 않습니다.

3. add와 getChildren 의 구현 방법

첫째, Component 에서 모든 것을 구현하고, Leaf에서는 add 메쏘드 호출 시 UnsupportedOperationException 을 던집니다. Component-Composite-Leaf 3 개의 구조가 아니라 Component-Leaf의 2개 구조만 있어도 됩니다. 그래서 구조가 간단해집니다. 그러나 Composite에는 있고, Leaf에는 없는 메쏘드를 구현할 방법이 없어집니다. 위의 예제는 단지 트리구조를 구현하는 것이라 상관없지만, 추가 기능을 구현할 가능성이 있는 경우는 이 방법을 쓰면 후에 문제가 생길 수 있습니다.
둘째, Component 에서는 abstract로 선언만 하고 Composite와 Leaf에서 구현을 합니다. Leaf에서는 첫번째 방법과 마찬가지로 UnsupportedOperationException 를 던지면 됩니다. 구조는 복잡하지만, 첫번째 방법에 비해 다른 기능 추가는 상대적으로 쉽습니다.

4. JAVA API에 있는 Composite

java.awt의 Container 는 Component를 상속 받고, Component를 다시 하위 객체로 가집니다. (여기서 하위는 상속의 하위 개념이 아닙니다. UI 구조상의 하위 구조입니다.) 예제에서 설명한 Leaf의 역할을 하는 객체들은 여러가지가 있습니다. Button, Text 등 우리가 알고 있는 일반적인 awt의 컴포넌트는 전부 포함된다고 보시면 됩니다.

'개발 > 디자인패턴(외부글)' 카테고리의 다른 글

Chain of responsibility  (0) 2011.09.26
Decorate  (0) 2011.09.26
Composite  (0) 2011.09.26
Strategy  (0) 2011.09.26
Singleton  (0) 2011.09.26
Template  (0) 2011.09.26

댓글을 달아 주세요

posted by 동건이 2011. 9. 26. 11:32

Strategy

자바 디자인 패턴 6 - Strategy

1. Strategy 패턴은..

Template Method 패턴이 상속을 이용해서 어떤 구현을 했다면, Strategy 패턴은 구성을 이용합니다. Template Method와 마찬가지로 바뀌는 부분과 바뀌지 않는 부분을 나눠서 생각할 수 있습니다. Template Method가 하위 클래스에서 바뀌는 부분을 처리한다면 Starategy는 바뀌는 부분을 인터페이스로 분리하여 처리합니다. 그 인터페이스의 구현체를 바꿈으로서 로직을 변경하는 것입니다. 또 Template Method와 크게 다른 점은 Template Method에서는 외부로 공개되는 것이 Template Method를 가지고 있는 상위 클래스였지만, Strategy에서는 인터페이스를 사용하는 클래스(그 클래스를 Context라고 합니다.)입니다.

2. 예제

------------------------ 상위 인터페이스 --------------------
package ch06_Strategy;

public interface Seller {
    public void sell();
}

------------------------- 인터페이스 구현체1 -----------------
package ch06_Strategy;

public class CupSeller implements Seller {
    public void sell() {
        System.out.println("컵을 팔아요.");
    }
}
------------------------- 인터페이스 구현체2 -----------------
package ch06_Strategy;

public class PhoneSeller implements Seller {
    public void sell() {
        System.out.println("전화기를 팔아요.");
    }
}
------------------------- 인터페이스 사용하는 클래스 -----------------
package ch06_Strategy;

public class Mart {
    private Seller seller;
    public Mart(Seller seller) {
        this.seller = seller;
    }
    public void order(){
        seller.sell();
    }
}
------------------------- 테스트 클래스 -----------------
package ch06_Strategy;

public class Test {
    public static void main(String[] args) {
        Seller cupSeller = new CupSeller();
        Seller phoneSeller = new PhoneSeller();
        Mart mart1 = new Mart(cupSeller);
        mart1.order();
        Mart mart2 = new Mart(phoneSeller);
        mart2.order();
    }
}

위에서 보시다 시피 테스트 클래스에서는 Seller의 sell()을 호출하지 않습니다. Mart의 order()를 호출합니다. Seller의 메쏘드는 외부로 공개되지 않습니다. 
Mart 클래스가 여기서는 외부로 공개되는 Context가 됩니다. Mart는 멤버 변수로 Seller를 가집니다. Mart에서 가지는 Seller를 바꿔치기함으로써 Mart의 order()에서 실제 실행되는 로직이 달라질 수 있습니다.

3. Strategy의 유용성

예제에서는 Context 클래스가 한 개의 Strategy 인터페이스만을 가집니다. Seller 외에 여러가지 인터페이스를 가질 수도 있습니다. 예를 들어 만드는 사람, 운반하는 사람, 파는 사람은 각각 다를 수 있습니다. 예제에서는 코드를 줄이기 위해 파는 사람만 2가지 종류의 클래스를 만들었습니다. 그러나, 만드는 사람 인터페이스와 운반하는 사람 인터페이스 등을 만들고 그 구현체 들을 만들면, 상당히 다양한 로직이 나올 수 있습니다. 만드는 사람의 구현체가 3종류, 운반하는 사람의 구현체가 3종류, 파는 사람의 구현체가 3종류라하면, 만들어서 운반해서 파는 로직은 총 3*3*3= 27가지가 나옵니다. 이를 상속을 이용한 클래스를 제작하면, 27가지의 구현체가 필요합니다. Strategy를 쓰면, 9개의 구현체만 필요하며, 또 인터페이스를 이용한 프로그램이 가능합니다.


4. JAVA API에 있는 Strategy

java.util.Collections 에 sort(List<T> list, Comparator<? super T> c) 라는 메쏘드가 있습니다. List를 Comparator에서 지정한 방법으로 정렬하는 메쏘드입니다. Comparator는 compare(T o1, T o2) 메쏘드 하나만 있는 인터페이스 입니다. 이 인터페이스를 구현하는 방법에 따라서 정렬된 결과가 달라집니다. "101"이 먼저일까요, "11"이 먼저일까요? 일반적인 순서에서는 "101"이 먼저입니다. 그러나 이게 숫자라면, 정렬 방법이 달라져야 합니다. Comparator를 구현함으로써 해결할 수 있습니다.

'개발 > 디자인패턴(외부글)' 카테고리의 다른 글

Decorate  (0) 2011.09.26
Composite  (0) 2011.09.26
Strategy  (0) 2011.09.26
Singleton  (0) 2011.09.26
Template  (0) 2011.09.26
Factory  (0) 2011.09.26

댓글을 달아 주세요

posted by 동건이 2011. 9. 26. 11:23

Singleton

자바 디자인 패턴 5 - Singleton

1. Singleton 패턴은..

각종 설정 등이 저장된 클래스가 하나 있다고 칩시다. 프로그램 내에서 여기저기서 마구 접근해서 설정을 바꾸기도 하고 값을 가져오기도 합니다. 이런 클래스는 인스턴스를 하나만 가져야 합니다. 하나 만들어서 쓰는 곳마다 인자로 전달해주면 되긴 합니다만, 접근하는 곳이 많다면, 계속 인자로 전달하는 것은 그다지 바람직하지 않습니다. 전역변수처럼 아무곳에서나 이 인스턴스에 접근을 하면 편하겠죠. Singleton 패턴을 이용하면, 하나의 객체를 만들어서 아무데서나 접근할 수 있습니다.

2. 예제

---------------------  Singleton으로 구현된 클래스 ----------------
package ch05_Singleton;

public class SingletonCounter {
    private static SingletonCounter singleton = new SingletonCounter();
    private int cnt = 0;
    private SingletonCounter(){
    }

    public static SingletonCounter getInstance(){
        return singleton;
    }

    public int getNextInt(){
        return ++cnt;
    }
}
---------------------- 테스트 클래스 ---------------------
package ch05_Singleton;

public class Test {
    public static void main(String[] args) {
        Test t = new Test();
        t.Amethod();
        t.Bmethod();
    }
    public void Amethod(){
        SingletonCounter sc = SingletonCounter.getInstance();
        System.out.println("Amethod에서 카운터 호출 " + sc.getNextInt() );
    }
    public void Bmethod(){
        SingletonCounter sc = SingletonCounter.getInstance();
        System.out.println("Bmethod에서 카운터 호출 " + sc.getNextInt() );
    }
}

---------------------- 실행 결과 -----------------------
Amethod에서 카운터 호출 1
Bmethod에서 카운터 호출 2


singleton에서 중요한 것은 다음 세 가지입니다.
첫째, private 멤버 변수로 자기 자신의 클래스의 인스턴스를 가집니다. 황토색 부분입니다.
둘째, private 생성자를 지정하여, 외부에서 절대로 인스턴스를 생성하지 못하게 합니다. 보라색 부분입니다.
셋째,getInstance() 메쏘드를 통해 객체를 static하게 가져올 수 있습니다. 파란색 부분입니다.

이는 유일무이한 인스턴스를 만들기 위해 생긴 규약들입니다. 무슨 수를 써도 Singleton 클래스를 수정하지 않는 한 새로운 인스턴스를 만들 수 없습니다. 

3. Singleton을 구현하는 몇 가지 방법

------------- 첫번째 --------------
package ch05_Singleton;

public class Singleton1 {
    private static Singleton1 single = new Singleton1();
    public static Singleton1 getInstance(){
        return single;
    }
    private Singleton1(){
    }
}

클래스 로드시 new가 실행이 됩니다. 항상 1개의 인스턴스를 가지게 되겠죠. 코드가 가장 짧고 쉽습니다. 성능도 다른 방법에 비해 좋습니다. 

-------------- 두번째 --------------
package ch05_Singleton;

public class Singleton2 {
    private static Singleton2 single;
    public static synchronized Singleton2 getInstance(){
        if (single == null) {
            single = new Singleton2();
        }
        return single;
    }
    private Singleton2(){
    }
}

클래스 로드시에는 인스턴스가 생성되지 않습니다. getInstance()가 처음 호출될 때 생성이 되지요. 그러나 synchornized가 걸려 있어서 성능이 안 좋습니다. 인스턴스를 사용할 필요가 없을 때는 인스턴스가 생성되지 않는다는 점이 첫번째 방벙에 비해 장점입니다.

--------------- 세번째 ---------------
package ch05_Singleton;

public class Singleton3 {
    private volatile static Singleton3 single;
    public static Singleton3 getInstance(){
        if (single == null) {
            synchronized(Singleton3.class) {
                if (single == null) {
                    single = new Singleton3();
                }
            }
        }
        return single;
    }
    private Singleton3(){
    }
}

첫번째의 장점인 성능이 좋다(synchronized 가 안 걸려서)와 두번째의 장점인 안 쓸 때는 인스턴스를 아예 만들지 않는다의 장점만 뽑아온 방법입니다. 코드는 제일 깁니다^^.
여기서 중요한 점은 if(single == null) 을 두 번이나 체크합니다. A, B 2개의 thread가 접근을 한다고 가정합니다.
A와 B가 거의 동시에 들어와서 바깥쪽 single== null 인 부분을 통과했다고 칩시다. 그리고 A가 조금 먼저 synchronized 블럭에 진입했습니다. B는 그 앞에서 대기 중이지요. A가 다시 single== null을 체크합니다. 여전히 null이지요. 그러면 인스턴스를 만들고 synchronized 블럭을 탈출합니다. 그러면 B가 synchronized 안으로 진입합니다. single은 더 이상 null이 아닙니다. A가 만들었으니까요. B는 그냥 synchronized 블럭을 빠져나옵니다.
바깥쪽 if(single == null) 가 없다면, 성능 저하가 발생합니다. 매번 synchronized 블럭 안으로 들어가니까요. 두번째 방법과 같다고 보시면 됩니다. 안쪽의 if(single == null) 가 없다면, singleton이 보장되지 않습니다. 
volatile 키워드도 꼭 써줘야 합니다. volatile 키워드는 변수의 원자성을 보장합니다. single = new Singleton3(); 이란 구문의 실행은 원자성이 아닙니다.(원자성이란 JVM이 실행하는 최소단위의 일을 말합니다. 즉 객체 생성은 JVM이 실행하는 최소단위가 몇 번 실행되어야 완료되는 작업이란 뜻입니다.)  JVM에 따라서 single이라는 변수의 공간만을 먼저 생성하고 초기화가 나중에 실행되는 경우도 있습니다. 변수의 공간만 차지해도 null은 아니기 때문에 singleton이 보장된기 어렵습니다. JVM 버전이 1.4(어쩌면 1.5 잘 기억이..--;; ) 이전에서는 volatile 키워드가 정상적으로 작동하지 않을 수도 있다고 합니다.

--------------- 네번째 ---------------
package ch05_Singleton;

public class Singleton4 {
    private Singleton4(){
    }
    private static class SingletonHolder{
        static final Singleton4 single = new Singleton4();
    }
    public static Singleton4 getInstatnce(){
        return SingletonHolder.single;
    }
}

네번째 방법은 내부 클래스를 사용하는 방법입니다. 기존의 3가지 방법에서는 Singleton 클래스가 자기 자신의 타입을 가지는 멤버 변수를 가지고 있는데, 네번째의 경우는 내부 클래스가 가지고 있습니다. 내부 클래스가 호출되는 시점에 최초 생성이 되기 때문에, 속도도 빠르고 필요치 않다면 생성하지도 않습니다.

4. Singleton의 특징

Singleton은 당연히 인스턴스가 1개만 생깁니다. 그러자고 만든 거니까요. 또 하나의 규약은 private 생성자 때문에 상속이 안 된다는 점입니다. (상속받은 하위체는 상위체의 생성자를 호출합니다.) 예를 들어 Singleton에서 설정관련된 xml 파일을 수정한다고 칩시다. 상속을 받아 다른 객체를 만들어서 파일을 수정하는 시도를 하면 안되지요. 상속을 받게 되면 "인스턴스 1개"라는 원칙을 깨게 됩니다.
private 생성자는 외부에서의 직접호출을 통한 생성을 막는 것과 상속을 막는 두 가지 기능을 수행합니다. 둘 다 "인스턴스 1개"라는 원칙을 지키는 것이죠.

Factory 패턴과 사용법이 매우 유사합니다. Singleton은 Factory의 특이 케이스로 볼 수도 있습니다. Factory는 매번 객체를 만들어서 리턴하는 방법이고 Singleton은 한 개만 만들어서 요청이 들어올 때마다 만들어진 객체를 리턴한다는 게 차이점입니다. 또 일반적으로 Factory는 create...과 같은 메쏘드 이름을 사용하고 Singleton은 getInstance라는 메쏘드 이름을 사용합니다.

위에서 말한 세가지 방법 중 첫번째 방법의 경우는 public으로 멤버 변수를 선언하고 외부에서 직접 변수에 접근해서 사용하게 해도 됩니다. (반드시 private이어야할 필요는 없다는 거죠. ) 두번째와 세번째는 초기화가 보장이 안 되어 있지만, 첫번째의 경우는 보장되어있기 때문입니다. 주의할 점은 외부에서 악의적으로 public 멤버 변수는 바꿔치기를 할 수도 있기 때문에 이런 식으로 접근할 때는 final 을 붙여주는 게 좋습니다.(어차피 private 생성자를 가지고 있으니, 외부에서 새로운 객체를 만들어 낼 수는 없지만 null을 대입할 수는 있기 때문에 final이 필요합니다.) 그럼 public static final이 되는군요! 상수란 말이죠. 하지만 일반적인 상수와는 다릅니다. 일반적인 상수는 Immutable 로 구현이 되어있기 때문입니다. 상수로 많이 쓰는 String, Integer, Boolean 등은 전부 Immutalbe입니다. 
물론 이런 접근이 권장사항은 아닙니다. 그냥 가능하긴 하다는 얘깁니다.

5. JAVA API에 있는 Singleton

Boolean에 있는 valueOf 들은 전부 Singleton 비스무레하게 구현되어 있습니다. 다만 인자를 받기 때문에 멤버 변수로 예제처럼 1개만 가지고 있는 것이 아니라 여러개를 가질 수 있습니다. true라는 값을 가지는 Boolean과 false라는 값을 가지는 Boolean 객체 2개가 존재하는 것이죠.
Collections에 있는 empty.. 하는 메쏘드들도 전부 Singleton입니다.

jdk 안에 있는 Singleton은 대부분 위에서 말한 방법 중 첫번째 방법을 쓰고 있습니다. 클래스 로드시 멤버 변수들을 초기화하는 방법입니다. 그래서 대부분 그 멤버 변수들은 public static final 로 선언되어있습니다.

'개발 > 디자인패턴(외부글)' 카테고리의 다른 글

Composite  (0) 2011.09.26
Strategy  (0) 2011.09.26
Singleton  (0) 2011.09.26
Template  (0) 2011.09.26
Factory  (0) 2011.09.26
Adapter  (0) 2011.09.26

댓글을 달아 주세요

posted by 동건이 2011. 9. 26. 11:17

Template

자바 디자인 패턴 4 - Template Method

1. Template Method 패턴은..

전체적인 로직에는 큰 차이가 없지만 일부분만 바뀌는 비스무레한 몇 가지 클래스가 있다고 칩시다. 일부분을 위해서 전체를 새로 작성할 필요는 없지요. Template Method에서는 전반적인 구현은 상위클래스(주로 Abstract로 만듭니다.)에서 담당하고 부분적인 곳의 구체적인 구현은 하위클래스가 담당합니다.

2. 예제

------------- 템플릿 메쏘드가 있는 Abstract Class ---------------
package ch04_TemplateMethod;

public abstract class Worker {
    protected abstract void doit();
    public final void work(){
        System.out.println("출근");
        doit();
        System.out.println("퇴근");
    }
}
------------- Abstract Class 구현체 1 ---------------------
package ch04_TemplateMethod;

public class Designer extends Worker {
    @Override
    protected void doit() {
        System.out.println("열심히 디자인");
    }
}
------------- Abstract Class 구현체 2 ---------------------
package ch04_TemplateMethod;

public class Gamer extends Worker {
    @Override
    protected void doit(){
        System.out.println("열심히 껨질");
    }
}
------------- 테스트 코드 ---------------------
package ch04_TemplateMethod;

public class Test {
    public static void main(String[] args) {
        Worker designer = new Designer();
        designer.work();
        Worker gamer = new Gamer();
        gamer.work();
    }
}

Worker 클래스의 work()는 내부적으로 abstract 메쏘드인 doit()을 호출하고 있습니다. work() 안에서 전반적인 로직이 수행되고, 로직 중 각각의 특성을 탈 수 있는 부분을 doit() 안에서 해결합니다. doit()은 실제 구현체에서 알아서 구현하면 됩니다.
work() 를 final로 구현한 것은 하위 클래스에서 전체적인 로직 변경을 하지 못하도록 하는 것입니다.

3. Template Method 사용시 고려사항

Template Method는 위험성을 어느 정도 내포하고 있습니다. 바로 전체적인 프로세스가 바뀌는 것입니다. 상위 클래스에서 변동이 일어날 경우 하위 클래스가 안전하리라는 보장은 할 수 없습니다. 상위 클래스에 abstract method가 하나만 추가되어도 모든 하위 클래스는 변경이 불가피합니다. 나중에 발생하는 작은 변경이 큰 재난을 일으킬 수 있습니다. 이것은 상속이 가지는 위험성입니다.
그래서 Template Method 패턴을 사용할 때는 상위클래스에 대한 심사숙고가 반드시 필요합니다. 일반적으로는 전체적인 프로세스를 담당하는 로직을 final 메쏘드로 정의하기도 하지만, 프로세스 자체의 변경을 고려해 상속의 여지를 남겨두기 위해 final 메쏘드로 정의하지 않기도 합니다.
또 한가지는 하위 클래스의 메쏘드들은 외부에서 직접 호출되지 않고 상위 클래스의 Template Method에서 호출됩니다. 그래서 주로 protected 로 선언됩니다. 그런 이유로 외부의 호출과 구체적인 구현체의 메쏘드가 실행되기까지의 과정을 쉽게 파악하기가 어렵습니다. 문제가 생겼을 때 추적이 어려울 수도 있다는 것이죠.

4. JAVA API에 있는 Template Method

아마 JDK안에 가장 많이 들어 있는 패턴 중 하나가 Template Method일 겁니다. 
Servlet 을 개발할 때, HttpServlet을 상속 받아 doGet() 과 doPost() 를 구현합니다. HttpServlet의 service() 에서 하위구현체의 doGet()이나 doPost() 등으로 분기를 시킵니다.(그 외에도 do머시기하는 메쏘드가 있지만 생략합니다.) Template Method의 전형적인 사용법 중 하나입니다. 분기를 담당하는 부분은 상위클래스에 구체적으로 구현하고, 분기된 이후의 행동은 하위구현체에 떠 넘기는 방법이죠.

HashSet이라는 Set의 구현체를 아시죠? 얘는 Set이니까 중복 데이터를 요소로 가지지 않습니다. 그럼 '같다 다르다'의 기준은 뭘까요? 내용이 같으면 될까요? 아니면, 실제 레퍼런스가 같아야 할까요?
이름에 나와 있듯 hash 값이 일단 같아야 합니다. hash값은 Object의 hashCode() 메쏘드를 이용하여 체크합니다. 그리고 나서 또 Object의 equals() 메쏘드를 호출합니다. 그래서 Set에 넣을 요소에 대해서는 hashCode() 메쏘드와 equals() 메쏘드가 잘 구현되어있어야 합니다. 그렇지 않으면, 중복된 데이터가 삽입될 수 있습니다.
HashSet의 경우는 위에서 설명한 Template Method와는 좀 다릅니다. 상위클래스와 하위클래스의 상관관계 같은 게 없죠. 다만 나도 모르는 사이에 내가 구현한 메쏘드가 호출될 수 있기 때문에 Template Method가 될 수 있습니다.(HashSet의 add(SomeClass) 메쏘드는 SomeClass의 hashCode()와 equals()를 호출합니다. )
정확히 말하면 Set.add(SomeClass) 메쏘드는 Set.contains(SomeClass) 메쏘드를 호출하고 그 안에서 다시 SomeClass.hashCode()와 SomeClass.equals(SomeClass) 를 호출합니다. ( 중간에 좀 더 복잡한 과정이 있지만, 설명할 필요는 없으므로 생략합니다. )
여기서 UI관련 각종 EventListener들도 그런 맥락에서 Template Method라고 볼 수 있습니다. event 발생에서 리스너까지의 과정을 알 필요가 없습니다. 대략 어떤 이벤트를 발생시키면, 어떤 이벤트 리스너한테 전달된다 정도만 알면 되죠. 이벤트 핸들링에는 그 외에도 여러가지 패턴들이 적용되어 있습니다.

'개발 > 디자인패턴(외부글)' 카테고리의 다른 글

Strategy  (0) 2011.09.26
Singleton  (0) 2011.09.26
Template  (0) 2011.09.26
Factory  (0) 2011.09.26
Adapter  (0) 2011.09.26
Iterator  (0) 2011.09.26

댓글을 달아 주세요

posted by 동건이 2011. 9. 26. 11:12

Factory

자바 디자인 패턴 3 - Factory Method

1. Factory Method패턴은..

factory는 공장이죠. 객체를 막 찍어내는 놈입니다. 객체 선언은 보통 new 객체() 이런식으로 하죠. factory는 내부에서 그런 일을 해줍니다. 즉 factory를 가져다가 쓰는 부분에서는 new 객체()와 같은 식으로 변수를 선언할 필요가 없습니다.Abstract class나 인터페이스에 대해서 다양한 하위 구현체가 있을 경우에 사용하면 좋습니다. 사용법은 Factory.create(인자는 맘대로) 와 같이 됩니다. 

2. 예제

package chap03_StaticFactory;
public interface Animal {
    public void printDescription();
}


package chap03_StaticFactory;
public class AnimalFactory {
    public static Animal create(String animalName){
        if (animalName == null) {
            throw new IllegalArgumentException("null은 안 되지롱~");
        }
        if (animalName.equals("소")) {
            return new Cow();
        }else if (animalName.equals("고양이")) {
            return new Cat();
        }else if (animalName.equals("개")) {
            return new Dog();
        }else{
            return null;
        }
    }
}


package chap03_StaticFactory;
public class Cat implements Animal {
    public void printDescription() {
        System.out.println("쥐잡기 선수");
    }
}


package chap03_StaticFactory;
public class Cow implements Animal {
    public void printDescription() {
        System.out.println("우유 및 고기 제공");
    }
}


package chap03_StaticFactory;
public class Dog implements Animal {
    public void printDescription() {
        System.out.println("주로 집 지킴");
    }
}


package chap03_StaticFactory;
public class Test {
    public static void main(String[] args) {
        Animal a1= AnimalFactory.create("소");
        a1.printDescription();
        Animal a2= AnimalFactory.create("고양이");
        a2.printDescription();
        Animal a3= AnimalFactory.create("개");
        a3.printDescription();
    }
}

이번 것은 소스가 좀 깁니다. 일단 Animal이라는 인터페이스가 있습니다. Cat, Cow, Dog 는 이 인터페이스의 구현체들입니다.
그리고 AnimalFactory가 있는데, 여기서 Animal의 구현체를 돌려줍니다.
Test에서 new Cow()와 같이 하지 않고, AnimalFactory.create("소")를 호출하는 게 일반적인 방법과의 차이입니다.
Animal의 구현체가 더 늘어나면 어떻게 될까요? 전부 new AnotherAnimal()과 같이 생성하는 것보다는 Facotry의 create()메쏘드만 수정하는 게 좀 편하겠죠? 

3. Factory 의 유용성

Animal a1 = AnimalFactory.create("소"); 와 같은 코드에서 a1이 Cow라는 것을 굳이 신경쓰지 않겠다는 겁니다. Test클래스 안에는 new 라는 구문 자체가 없습니다. 정확히 어떤 클래스의 인스턴스인지 신경쓰지 않고 구현할 수 있는 장점이 있습니다. 객체 타입이 굉장히 유연해 질 수 있죠.

4. JAVA API에 있는 Factory Method

Factory 패턴의 중요한 특징 중 하나는 Factory에서 리턴할 때는 매번 객체를 새로 만들지 않을 수도 있다는 겁니다.
Boolean.valueOf(boolean) 을 먼저 살펴 보죠. 
        Boolean a = Boolean.valueOf(true);
        Boolean b = Boolean.valueOf(true);
        System.out.println(a==b);
이 코드를 실행시키면 어떤 결과가 나올까요? true 가 나옵니다. 왜냐하면  Boolean.valueOf(true) 는 Boolean.TRUE 라는 상수를 리턴합니다. 즉, 인스턴스를 새로 만드는 것이 아니라 기존에 있는 것을 그냥 리턴합니다. 매번 새로 만들지 않는다는 거죠. 각종 Wrapper 클래스에 있는 많은 메쏘드 들이 이렇게 구현되어 있습니다.
Calendar.getInstance() 를 호출하면, 사용자 환경에 맞는 Calendar 객체가 리턴됩니다. 보통은 GregorianCalendar가 리턴된죠.
(이 메쏘드의 이름은 좀 잘못지어진 것 같습니다. 보통 getInstance()는 singleton 패턴에서 쓰이는 이름입니다.)

5. Factory Method의 종류

예제에서는 Factory의 인스턴스를 만들지 않고, static 메쏘드인 create()만을 호출했습니다. 이런 방식을 static factory method라고 합니다.
그냥 factory method라고 하면, factory의 인스턴스를 만들어서 쓰는 방식입니다. static factory에 비해 사용 빈도는 좀 떨어지지만, factory의 인스턴스에 귀속되는 객체를 생성해야 할 때는 이런 방식을 씁니다.(static factory에 비해 많이 쓰지 않으므로 자세한 것은 생략합니다.

'개발 > 디자인패턴(외부글)' 카테고리의 다른 글

Strategy  (0) 2011.09.26
Singleton  (0) 2011.09.26
Template  (0) 2011.09.26
Factory  (0) 2011.09.26
Adapter  (0) 2011.09.26
Iterator  (0) 2011.09.26

댓글을 달아 주세요

posted by 동건이 2011. 9. 26. 11:07

Adapter

자바 디자인 패턴 2 - Adapter

1. Adapter 패턴은..

이미 구현되어 있는 코드가 있는데, 둘이 연결 좀 시켜주고 싶을 때가 있죠. 어떤 좋은 메쏘드가 있는데, 인자로 A라는 형식을 받습니다. 근데, 이미 구현되어 있는 코드에는 B라는 형식으로 구현되어 있습니다. 이럴 때, B를 A의 형식으로 바꿔주면 좋은 메쏘드를 써먹을 수 있습니다. Adapter 패턴은 어떤 오브젝트를 캐스팅이 불가능한 다른 클래스의 형태로 변환시켜주는 것입니다.

2. 예제

---------------Adapter Class --------------------
package ch02_adapter;

import java.util.Enumeration;
import java.util.Iterator;

public class IteratorToEnumeration implements Enumeration<String>{
    private Iterator<String> iter;
    public IteratorToEnumeration(Iterator<String> iter) {
        this.iter = iter;
    }
    public boolean hasMoreElements() {
        return iter.hasNext();
    }
    public String nextElement() {
        return iter.next();
    }
}

---------------뭔가 훌륭한 method를 가지고 있는 클래스 ------------
package ch02_adapter;

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

public class Test {
    public static void goodMethod(Enumeration<String> enu){
        while (enu.hasMoreElements()) {
            System.out.println(enu.nextElement());
        }
    }
 
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("이은결");
        list.add("Kevin parker");
        list.add("David Blaine");
        Enumeration<String> ite = new IteratorToEnumeration(list.iterator());
        Test.goodMethod(ite);
   }
}

우리가 최종적으로 쓰고자하는 것은 goodMethod() 입니다. 그 녀석은 인자로 Enumeration을 받고 있지요. 그러나 우리가 가지고 있는 것은 Iterator입니다. IteratorToEnumeration 클래스는 Iterator를 받아서 Enumeration 으로 변경시켜줍니다. 
AtoB의 형태를 가지는 Adapter는 A를 멤버변수로 가지고 B를 구현합니다. 소스코드에 색깔 칠해논 부분을 확인하세요.

3. Adapter를 구현하는 방법

위에서 소개된 방법은 "구성을 통한 방법" 또는 "위임을 통한 방법"입니다. Adapter 자체는 하는 일이 별로 없습니다. 내부적으로  멤버한테다가 일을 다 떠넘깁니다. 외관상 다른 형태로 변환가능하기 위한 것이지 어떤 일을 직접할려는 것은 아닙니다.

두번째 방법은 상속을 이용하는 방법입니다. A to B로 할 경우 A와 B를 둘다 구현하는 방법입니다. A와 B가 둘 다 인터페이스거나, 하나만 인터페이스일 때는 가능하지만, 둘 다 클래스일 경우에는 불가능하죠. 
상속을 쓰는 것은 바람직하지 않습니다. "상속보다는 구성을 이용하라"는 원칙에 어긋납니다. 이 원칙에 대해서는 다음 설명드리죠.

세번째 방법은 Adapter 클래스를 만들지 않고 method로 만드는 방법입니다.  다음과 같은 코드는 위에서 구현한 것과 같은 효과를 보여주죠.

    public static Enumeration<String> iteratorToEnumeration(final Iterator<String> iter) {
        return new Enumeration<String>() {
            public boolean hasMoreElements() {
                return iter.hasNext();
            }

            public String nextElement() {
                return iter.next();
            }
        };
    }

눈치 빠르신 분들은 인자가 final이란 것을 보셨겠지요. final로 말뚝 박는 건 "변경 절대 불가!" 를 보장하는 겁니다. 메쏘드 안에서 Enumeration의 구현체가 인자로 받은 것을 사용하는데, 이런 경우에는 인자가 final 로 정의되어야 하며, 그렇지 않을 경우 컴파일시에 문제가 됩니다.
final로 정의한다는 것은 read-only라는 뜻입니다. 주의할 것은 read-only라는 게 다른 변수로 할당을 못한다는 것 뿐 내부의 메쏘드를 호출하지 못한다는 것은 아닙니다. (메쏘드 내부에서 iter = 따른거; 와 같이 할 수 없다는 거죠.) 즉 Immutable일 경우에는 진짜 read-only가 보장되지만, 위의 예제 같은 경우는 보장되지 않습니다. Immutable에 대한 얘기는 따로 다루겠습니다.

세번째 방법은 Adapter 패턴이라고 불리지는 않습니다. 그러나 하는 일이 똑같죠. 뭐라 불리건 말이 뭐가 중요합니까~~

4. JAVA API에 있는 Adapter

JDK에는 Adater를 구현해 논 게 없댑니다. AWT와 같은 UI관련된 애들 중에서 알아서 상속해서 쓰라는 인터페이스만 제공하고 있습니다.

위에서 소개한 세번째 방법을 통한 케이스는 무수히 많습니다. Integer, Float, Long 등과 같은 Wrapper 클래스들이 잔뜩 있지요.
Interger.valueOf(String) 은 String을 Integer로 바꿔주지요.

'개발 > 디자인패턴(외부글)' 카테고리의 다른 글

Strategy  (0) 2011.09.26
Singleton  (0) 2011.09.26
Template  (0) 2011.09.26
Factory  (0) 2011.09.26
Adapter  (0) 2011.09.26
Iterator  (0) 2011.09.26

댓글을 달아 주세요

posted by 동건이 2011. 9. 26. 11:07

Iterator

자바 디자인 패턴 1 - Iterator

1. iterator 패턴은..

프로그래밍을 하다 보면, array나 List, Set, Map과 같은 애들을 많이 씁니다. 얘네들의 특징은 어떤 데이터들의 집합체라는 겁니다. 원래 집합체란 게 속에 뭐가 들었냐가 중요하죠. 그래서 집합체들을 다룰 때는 얘들이 가지고 있는 개별 원소에 대해서 이런 저런 작업들을 할 일이 많습니다. 
iterator를 쓰게 되면, 집합체와 개별 원소들간에 분리시켜 생각할 수가 있습니다. 심지어는 그 집합체가 어떤 클래스의 인스턴스인지 조차 신경쓰지 않아도 됩니다.

2. 예제

package c01_iterator;

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

public class MagicianList implements Iterable<String> {
 private List<String> list = new ArrayList<String>();
 
 public void add(String name){
  list.add(name);
 }
 
 public Iterator<String> iterator() {
  return  
new Iterator<String>(){
   int seq = 0;
   public boolean hasNext() {
    return  seq < list.size();
   }
   public String next() {
    return list.get(seq++);
   }
   public void remove() {
        throw new UnsupportedOperationException();
   }
  }
;

 }
 
 
 public static void main(String[] arg){
  MagicianList magicians = new MagicianList();
  magicians.add("이은결");
  magicians.add("Kevin parker");
  magicians.add("David Blaine");
  
  Iterator<String> iterator = magicians.iterator();
  while (iterator.hasNext()) {
   String element = iterator.next();
   System.out.println(element);
  }

 }
}

먼저, main 함수의 황토색 부분을 보면, magicians 의 원소들을 뽑아내는데, magicians 라는 변수를 전혀 쓰지 않습니다. 물론, 내부적으로 iterator라는 변수가 magicians와 관계를 유지해주고 있긴합니다만, 일단 iterator를 가지고 온 후에는 데이터 집합체가 뭐냐에 신경을 쓸 필요가 없어진 거죠. iterator만 가져오면, 걍 hasNext() , next() 만 가지고 반복하면서 원소들에 대해서 처리를 하면 됩니다.

3. Iterator관련 interface

소스 코드의 보라색 부분이 jdk 안에 있는 Iterator에 관한 부분입니다. 
java.util.Iterable 이란 넘을 구현하고 있습니다. 고놈한테는 Iterator<E> iterator() 라는 메소드 한개만 있습니다. 뭔소리냐면, 이 클래스는 무슨무슨 집합체 데이터를 가꾸 있으니깐, iterator로 원소들을 뽑아다가 쓸 수 있도록 제공하겠다는거죠.

그담에 등장하는 것이 java.util.Iterator입니다. 소스 코드의 청록색 부분입니다.
method가 3개가 있죠? hasNext()는 다음 구성 요소가 있냐고 물어봅니다. next()는 그 요소를 뽑아옵니다. remove()는 일부러 구현을 안했습니다. API에 보면, 마지막으로 꺼낸 요소를 제거한다.(optional operation) 이라고 되어있습니다. optional이라는 걸 강조할라고 구현 안 했습니다.
요기서 한가지 짚고 넘어가야 할 것은 시퀀스는 hasNext()가 아니라 next()에서 증가시켜야 한다는 것입니다. 좀 비상식적인 얘기긴 합니다만, hasNext()를 호출하고 또 호출하는 일이 발생할 수도 있기 때문이죠. hasNext라는 메소드 이름이, next를 가지고 있는지를 체크하겠다는 것이니까요.

4. JAVA API에 있는 Iterator

우리가 알고 있는 일반적인 집합체들은 전부 Iterator를 제공합니다. Set, List 등은 Collection 을 상속 받는데, Collection이 Iteratable을 상속 받기 때문입니다.
위에서 청록색 부분을 list.iterator() 라고 쭐여버려도 됩니다. 걍 있는 거 안 쓰고 굳이 구현한 건 예제 파일을 함 보여줄라고 한 겁니다. 사실은 예제 전체가 억지로 만들어낸 겁니다. 일반적인 집합체를 구현해서 쓰는 일은 거의 없고, JDK 안에 들어 있는 애들을 가져다 쓰는데, 걔들은 거의 대부분 Iterator를 제공하거든요.(Map은 한 다리 건너서 제공합니다.) 그래서 Iterator를 직접 구현할 일은 거의 없습니다. 가져다가 쓸 일이 있을 뿐이죠.

이제 Map은 왜 Iterator를 제공하지 않는 지를 살펴보죠. Map은 Set이나 List와는 달리 key-value의 구조입니다. key에 대한 Iterator인지 value에 대한 Iterator인지 구별할 방법이 없죠. 그래서 아예 제공을 안 합니다. 그러나 Map에는 key에 대해서는 Set<K> keySet()이라는 key를 Set으로 가져오기를 지원하고, value에 대해서는 Collection<V> values() 를 제공합니다. 위에서 말씀드렸다시피 Set과 Collection은 둘다 Iterator를 제공합니다.

5. Enumeration vs Iterator

둘은 굉장히 유사합니다. Enumeration의 경우는 boolean hasMoreElements() 와 E nextElement() 를 제공합니다. Iterator의 hasNext() , next() 에 대응되는 메쏘드들이죠. 
차이는 두 가집니다. 첫째 Iterator에는 remove()가 있다. 둘째, Iterator의 함수 이름이 훨씬 쉽다.(타이핑 노가다가 쭐어든다.-_-; )
처음에 Enumeration이 나왔고, 그걸 쫌 편하게 만들어보자한 것이 Iterator랍니다.

'개발 > 디자인패턴(외부글)' 카테고리의 다른 글

Strategy  (0) 2011.09.26
Singleton  (0) 2011.09.26
Template  (0) 2011.09.26
Factory  (0) 2011.09.26
Adapter  (0) 2011.09.26
Iterator  (0) 2011.09.26

댓글을 달아 주세요

posted by 동건이 2011. 9. 21. 21:05

웹서비스 프레임워크 비교(Comparision of WS) Apache Axis2, CXF and Sun JAX-WS


Apache Axis2

Axis2 is the follow-up of the popular Axis1 framework. Axis2 is based on a completely new architecture and has been rewritten from scratch. That makes Axis2 very flexible. For instance the databinding that is responsible for the serialization of Java objects into XML is not limited to one technology. For instance the following databindings are available:

Data BindingDescription
ADB The Axis2 Databinding Framework is leightweight and simple but doesn't cover all XML Schema functions.
JiBX Allows the use of pojos without any modifications. A mapping defines how the pojos are serialized to XML.
XMLBeans Offers the most comprehensive XML Schema support.

Table 1: Axis2 Databindings

Asynchronous Web Services are well supported by Axis2. An Axis2 service accepts also asynchronous requests without any changes to the service. The service can use an endpoint reference delivered in the request to address a callback after finishing an asynchronous request.

Asynchronous Web Service Invocation
Figure 1: Asynchronous Web Service Invocation

The deployment of services for Axis1 has often been criticized. Therefore Axis2 has a completely new deployment model. An Axis2 runtime is a Web application and can be installed on every JEE compliant Application Server. The Axis2 web application itself is a container for Web Services. Web Services are packed into an own file format with the file extension aar. Due to these archives, Web Services can be installed and configured by a simple user interface at run time. A Web Service is regarded as an independent package which can be installed into the Axis2 runtime environment. Services are configured using the service.xml configuration file.

Axis2 Deployment
Figure 2: Axis2 Deployment

Axis2 offers modules providing additional functionality, for example WS-* standard support, which can also be installed during runtime.

Apache CXF

CXF is also a project of the Apache software foundation. CXF came up from a fusion of XFire and Ionas Celtix project.

CXF was developed with the intention to integrate it into other systems. This is reflected in the CXF API and the use of the Spring framework. Therefore it is simple to integrate CXF into existing systems. For instance CXF respectively its predecessor XFire was integrated into numerous open and closed source projects like ServiceMix or Mule.

A proprietary API as well as the standardized JAX-WS interface are available for the development and use of Web Services.

The JAX-WS Standard

The Java API for XML based Web Services is the successor of the JAX-RPC specification. JAX-WS respectively its predecessor is message based and supports asynchronous communication. The Configuration is managed by annotations therefore Java 5 or higher is required. JAX-WS isn't downwardly compatible to its predecessor JAX-RPC. With JAX-WS it is pretty easy to write and consume Web Services. The default values of numerous parameters are comfortable for the programmer, so that simple Pojos declared with a @WebService annotation can be used as a Service. Listing 1 shows a simple implementation of a Web Service using the @WebService annotation.

01.import javax.jws.WebService;
02.import javax.jws.WebMethod;
03. 
04.@WebService
05.public class HelloWorldService {
06. 
07.public String helloWorld() {
08.return "Hello World!";
09.}
10.}
Listing 1: HelloWorld Web Service using JAX-WS

A WSDL document can also be generated from the class. Annotations can influence the content of the generated WSDL document. For example a different namespace or other element names can be used. If JAXB is already known to the programmer, its annotations can be used to define the serialization in every detail. With the contract first approach a Service Interface, JAXB annotated classes for serialization and a skeleton for the implementation can be generated out of WSDL. According to the WSDL document the generated classes can have numerous annotations. Annotations are also the main point of criticism of JAX-WS. Despite this criticism the JAX-WS specification came out well. It is well attuned and combinable with other Java and Java EE specifications. For example, Listing 2 shows a stateless EJB 3.0 Bean which acts as a Web Service at the same time.

1.@WebService
2.@Stateless
3.public class MyService{
4.public String hello(String name) {
5.return "Hallo"+name;
6.}
7.}
Listing 2: Stateless SessionBean acting as Web Service

JAX-WS Reference Implementation

The JAX-WS Reference Implementation represents the core component of the Web Services protocol stack Metro. The Metro Stack provides support for the following Web Services standards:

  • WS-Addressing
  • WS-Policy
  • Web Services Security aka WS-Security
  • WS-Transaction
  • WS-Reliable Messaging
  • WS-Trust
  • WS-SecureConversation

JAX-WS and Metro are documented in detail. Apart from the JAX-WS, JAXB and JWS specifications there are numerous tutorials und samples. The Netbeans IDE as well as the tutorials of the enterprise pack makes it particularly easy to get started. Of course, Eclipse can also be used for the development with Metro.

Web applications containing Web Services which have been realized with JAX-WS are executable in Glassfish and many other application servers. To make services also executable in Tomcat, two libraries (JAX-WS and JAXB) have to be installed.

JBossWS

JBossWS is the Web Services Framework for the JBoss application server. By providing an integration layer it is possible to choose from three stack options:

  • Native Stack:
  • Apache CXF Stack:
  • Metro Stack:

Performance

Due to the modern streaming StAX parser, the performance of all three SOAP engines is very well. The ping time for a locale roundtrip is less than 1 millisecond (message size about 3KB, Dual Core Notebook). Therefore the time delay by the SOAP communication is negligible in many projects.

WS-* Standards

The support of the WS-Standard family can also be decisive for the selection of a SOAP engine. For example, messages sent to services can be secured with signatures as described in the Web Service Security standard (in short WSS). Table 1 lists the supported WS-* standards for each toolkit.

StandardsAxis2CXFJAX-WS/Metro
WS-Addressing X X X
WS-Coordination X(2) X
WS-MetadataExchange X
WS-Policy X X X
WS-ReliableMessaging X(3) X X
Web Services Security X(1) X(4) X
WS-SecureConversation X(1) X(4) X
WS-SecurityPolicy X X X
WS-Transaction X(2) X
WS-Trust X X X
WS-Federation

Table 2: Support for WS-* Standards ( as of August 2010)

(1) Supported by the additional module Apache Rampart
(2) Supported by the additional module Apache Kandula2
(3) Supported by the additional module Apache Sandesha2
(4) By Apache WSS4J

Conclusion

None of the Web Services frameworks is in general superior to the others. Axis2 is structured modularly, has many features and can be used as an application server for Web Services. A special feature of Axis2 is the support of exchangeable binding frameworks, for example XMLBeans. Axis2 together with the XMLBeans framework is well suited for Web Services which are using very complex schema definitions. The disadvantages of Axis2 are its complexity as well as the insufficient JAX-WS support. Therefore anyone who wants to work with JAX-WS should choose Apache CXF or the reference implementation.

Those who prefer a seamless integration with the Spring framework are well advised with Apache CXF. Furthermore CXF is slim and easy to use. CXF is the tool of choice if a SOAP engine has to be embedded into an existing software.

Who wants to code against the standard is well advised with the JAX-WS implementation. The enterprise pack of the Netbeans development environment supports JAX-WS RI very well. Only a few clicks are needed to build a server or to call a Web Service. The Metro stack that includes JAX-WS RI offers the most comprehensive support for the WS-* standards. A major advantage of Metro 2.0 is its compatibility with Microsofts Windows Communication Foundation.

I hope this article could help you with the decision for a WS toolkit.

댓글을 달아 주세요

  1. Favicon of http://www.moncleroutletespain.com/ BlogIcon moncler españa 2013.01.04 14:02  Addr  Edit/Del  Reply

    You can also know about Visa regulations in specific countries, http://www.moncleroutletespain.com/ moncler. You can know about the ground delays of flights and track the rescheduling, http://www.moncleroutletespain.com/ moncler online. You also do not have to glue yourself before television or news channels to know the status of flight because of bad weathers. You can now easily know about airport closure beforehand. You save time and cost both, http://www.moncleroutletespain.com/ http://www.moncleroutletespain.com/. If you are first time flyer, you can easily know about the boarding process of a particular airport, http://www.moncleroutletespain.com/ moncler españa. If you are going to board at Philadelphia airport or Newark, http://www.moncleroutletespain.com/ moncler chaquetas, you will only get specific knowledge of this airport only. From parking to shops around, http://www.moncleroutletespain.com/ moncler outlet, these airport guides are comprehensive knowledge source for frequent and first time flyers alike. These can save you from the endless hassles which could face you if you are unprepared. Offering the simplest of services such as relevant information on flights from Dallas Fort Worth, on-site and off-site parking slots at Airport, these airport guides work towards to save your day and trip both.Related articles:


    http://2ssoosike.tistory.com/146 http://2ssoosike.tistory.com/146

    http://scor7910.tistory.com/92 http://scor7910.tistory.com/92

  2. Favicon of http://www.moncleroutletespain.com/ BlogIcon moncler outlet 2013.01.04 17:46  Addr  Edit/Del  Reply

    Le chef des opérations de maintien de la paix des Nations unies, http://www.moncleroutletespain.com/ moncler chaquetas, Alain Le Roy, http://www.moncleroutletespain.com/ moncler, a déclaré mercredi à l'AFP qu'il allait demander l'envoi de 1, http://www.moncleroutletespain.com/ moncler outlet.000 à 2, http://www.moncleroutletespain.com/ moncler españa.000 Casques bleus supplémentaires en C?te d'Ivoire, http://www.moncleroutletespain.com/ moncler online, en proie à une grave crise politique, http://www.moncleroutletespain.com/ http://www.moncleroutletespain.com/.Related articles:


    http://exsihell.tistory.com/920 http://exsihell.tistory.com/920

    http://tsukiko.tistory.com/14 http://tsukiko.tistory.com/14

  3. Favicon of http://www.moncleroutletespain.com/ BlogIcon moncler chaquetas 2013.01.05 16:35  Addr  Edit/Del  Reply

    Des contr, http://www.moncleroutletespain.com/ moncler españa?les de contamination à la dioxine vont être effectués, http://www.moncleroutletespain.com/ moncler, selon un porte-parole du ministère de l'Agriculture de l'Etat régional de Basse-Saxe, où sont situés la plus grande partie des élevages, http://www.moncleroutletespain.com/ moncler online.?La nourriture pour ces élevages, aujourd'hui concernés par cette alerte, a, http://www.moncleroutletespain.com/ moncler outlet? été livrée par le fabricant Harles & Jentzsch, installé à Uetersen dans le nord du pays. Selon la Fédération des agriculteurs allemands, http://www.moncleroutletespain.com/ http://www.moncleroutletespain.com/, Harles & Jentzsch a re, http://www.moncleroutletespain.com/ moncler chaquetas?u de l'acide gras contaminé en dioxine d'un commer?ant hollandais qui s'est lui-même fourni auprès d'un fabricant allemand de biodiesel situé à Emden (Nord de l'Allemagne).Les dioxines sont des résidus essentiellement formés lors des combustions, industrielles ou naturelles. Elles ont été classées comme substance cancérigène par l'Organisation Mondiale de la Santé. Santé Un test pour détecter les cellules cancéreuses bient?t sur le marché Actu en continu Inondations/Australie: attention aux serpents et crocodilesRelated articles:


    http://guraguna.tistory.com/category/?page=12+ http://guraguna.tistory.com/category/?page=12+"powered+by+daum"+~endorsement&ct=clnk

    http://hubnbridge.tistory.com/531 http://hubnbridge.tistory.com/531