디자인 패턴 #6 - 커맨드 패턴 (command pattern)

이미지
1. 커맨드 패턴 (command pattern) 각종 기능을 캡슐화하여 여러 기능을 실행/관리할 수 있다. 커맨드 패턴을 이용하면 요구 사항을 객체로 캡슐화 할 수 있으며, 매개변수를 써서 여러가지 다른 요구 사항을 집어 넣을 수도 있다. 또한 요청 내역을 큐에 저장하거나 로그로 기록할 수도 있으며, 작업 취소 기능도 지원할 수 있다. 커맨드 패턴은 (1)명령, (2)수신자, (3)발동자, (4)클라이언트로 이루어진다. - 명령(command) : 수행 동작을 담고 있는 클래스 - 수신자(receiver) : 명령 수행하는 클래스 (ex. TV, 오디오 등 각종 기기) - 발동자(invoker) : 명령 수행을 요청하는 클래스 (ex. 리모컨) - 클라이언트(client) : 어떤 명령을 수행할지 결정하는 클래스 (1) 명령 LightOnCommand 클래스는 Command 인터페이스를 구현하여, Light 객체의 On/Off를 실행할 수 있다. (2) 수신자 Light 클래스는 장소(mPlace)의 점/소등을 할 수 있다. Command 클래스의 execute() 메소드에서 호출한다. (3) 발동자 RemoteControl 클래스는 입력된 Command를 실행한다. 총 7개의 slot이 존재하고, 미리 Command를 입력한 뒤, slot 번호를 통해 명령을 실행할 수 있다. onButtonWasPushed()와 함께 slot 번호를 입력하면 slot에 해당하는 기기의 정의된 동작을 수행하게 된다. (4) 클라이언트 각 Command 객체와 RemoteControl 객체 생성한다. 명령의 실행과 시점을 결정한다. 하기에서는 main 클래스가 해당 역할을 담당한다. LivingRoom과 Kitchen의 점등과 소등 명령을 정의하였으며, onButtonWasPushed(), offButtonWasPushed()를 통해 실행할 수 있다. 2. 결과 수행 결과는 하기와 같다. 각 slot에 해당하는 사...

오픈 소스 라이선스 (Open Source License)

이미지
1. 정의 소프트웨어 혹은 하드웨어의 제작자의 권리를 지키면서 원시 코드를 누구나 열람할 수 있도록 한 소프트웨어 혹은 오픈 소스 라이선스에 준하는 모든 통칭을 일컫는다. 2. 특징 - 소스 코드가 공개되어 있어 원하는대로 사용/복사/재배포 가능하다. - 공개된 소스의 무료 사용 권리가 있으나, 그에 따른 법적 책임이 뒤따른다. - 권한 및 책임의 종류에 따라 약 1,000가지의 다양한 open source license가 존재한다. - OSI(Open Source Initiative)에서 인증한 라이선스는 약 70여개가 있다. * 법적 위험을 피하기 위해 open source에 대해 잘 이해하고 대응해야 한다. 3. 소프트웨어 지적재산권 - 저작권(copyright) : 등록과 같은 절차 없이, 창작과 동시에 권리가 발생한다. 저작권자의 허가없이 저작물의 사용/복제/배포/수정이 불가능하다. - 특허권(patent) : 발명에 관하여 발생하는 권리로 출원의 절차가 필요하다. 특허 기술을 사용하기 위해서 특허권자의 허락이 필요하다. - 사용허가권(license) : 소프트웨어를 사용할 수 있는 권리를 의미한다. 4. 오픈 소스 라이선스 비교 - GPL : 미국 자유소프트웨어 재단이 만들었으며, open source의 53%를 차지하고, 공개범위가 넓다. Sourceforge의 60%이상을 차지하고 있는 대표적인 lisence. 대표적인 사례로는 Linux kernel, GNU tools(gcc, gdb 등), Samba등이 있다. - LGPL : 자유소프트웨어 재단이 open source 활성화를 위해 GPL의 소스 공개 의무를 완화한 lisence이다. 독점 library가 표준이 되는 것을 방지하기 위해 만들어졌다. 대표 소프트웨어는 GNU C library (glibc, uclibc)가 있다. - MPL : open source의 상용 제품 적용을 용이하게 하기 위해 모질라 재단에서 만들었다. 상용 소...

디자인 패턴 #5 - 컴포지트 패턴 (composite pattern)

이미지
1. 컴포지트 패턴 (composite pattern) 객체들의 관계를 트리 구조로 구성하여 부분-전체를 나타내는 계층 구조로 만들 수 있다. 이 패턴을 이용하면 클라이언트에서 단일 객체와 여러 객체들로 구성된 복합 객체 모두 동일하게 다루도록 한다. 쉽게 표현하자면 담는 그릇과 내용물을 동일하게 다루는 것으로 디렉토리 트리 구조가 이에 해당한다. 파일 관리자와 같은 디렉토리 트리 구조는 디렉토리와 파일을 동일한 개념으로 다룬다. 사용자가 디렉토리의 depth를 변경하며 이동할 수 있고, 특정 디렉토리에 속한 디렉토리와 파일 목록을 보여준다. 디렉토리와 파일을 담고 있는 디렉토리도 같은 레벨의 파일과 동일하게 취급하게 된다.(이해가 어려우면 당장 핸드폰의 파일관리자를 실행해보라.) 파일관리자와 클래스 다이어그램을 비교하자면 Composite 클래스는 디렉토리를, Leaf를 파일을 의미한다. Composite 클래스와 Leaf 클래스는 Component 추상 클래스를 상속받았기 때문에 동일하게 취급할 수 있다. 이러한 composite 패턴의 특징을 이용해 디렉토리 트리 구조를 만들고, 다룰 수 있다. Component는 추상 클래스로 Directory의 reference를 가지고 있다. add(), getChildren()은 필요 시 하위 클래스에서 override하여 구현한다. mParentDirectory는 부모 디렉토리 참조이며, getAbsolutePath() 호출 시 재귀호출을 통해 절대 경로를 완성한다. 클래스 다이어그램에 나타난 것과 같이 Directory 클래스는 하위 클래스를 가질 수 있고, 이를 지원하기 위한 메소드를 override하여 정의한다. File 클래스는 Leaf에 해당하는 클래스로 트리 구조의 마지막에 위치한다.

디자인 패턴 #4 - 싱글턴 패턴 (singleton pattern)

이미지
1. 싱글턴 패턴 (singleton pattern) 생성자가 여러 차례 호출되더라도 실제로 생성되는 객체는 하나이고, 최초 생성 이후에 호출된 생성자는 최초의 생성자가 생성한 객체를 return한다. 즉 해당 클래스의 인스턴스가 하나만 만들어지고, 어디서든지 그 인스턴스에 접근할 수 있다. 보통 getInstance()로 불리오는 정적 메소드를 호출하는 형식으로 구현된다. 클래스 다이어그램을 살펴보면 클래스 내부의 singleton 객체와 생성자는 private로 외부에서 접근이 불가하나, getInstance()를 통해 객체를 생성하고 얻을 수 있도록 접근제어자가 public으로 설정되어 있다. 코드를 살펴보면 위에 언급한대로 구현되어 있는 상태이다. getInstance() 메소드를 살펴보면 동기화(syncronized)처리가 되어 있는 것을 확인할 수 있는데, 다중 thread 상황에서 mSingleton 객체가 만들어지기 전에 2개 이상의 thread가 접근해 객체를 여러 개 만드는 경우가 발생할 수 있다. 이를 위해 syncronized 처리를 한 것인데, 동기화 처리를 하면 속도가 많이 저하된다. 객체가 2개 이상 만들어 질 수 있는 때는 최초 생성하는 경우인데, 동기화에 따르는 비용이 크기 때문에 큰 문제가 아니라면 그냥 두는 것도 방법일 수 있다.

디자인 패턴 #3 - 데코레이터 패턴 (decorator pattern)

이미지
1. 데코레이터 패턴 (decorator pattern) 데코레이터 패턴에서는 객체에 추가적인 요건을 동적으로 첨가한다. 데코레이터는 서브클래스를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법을 제공한다. 쉽게 말해 샌드위치와 같이 본래의 재료에 무언가를 추가할 때 사용된다. 클래스 다이어그램을 살펴보면 Component는 기본적인 재료를 의미한다. 샌드위치로 예를 들자면 Component는 "빵"이라고 할 수 있다. 기본 재료인 "빵"에 Decorator 를 상속받은 ConcreteDecoratorA, ConcreteDecoratorB를 사용하여 각종 햄, 야채, 소스를 추가하면 된다. Decorator 객체는 Component의 reference를 가지고 있고, 이 곳에 꾸며야 할 객체(ConcreteComponent)를 전달받아 여러 가지 동작/정보를 추가하게 된다. Beverage 클래스는 Component에 해당하는 객체로 기본적인 음료를 나타내는 추상 클래스이다. CondimentDecorator 클래스는 Decorator의 역할을 하는 클래스로 첨가물을 나타내는 추상클래스이다. 전달받은 Beverage 클래스를 저장하여, 꾸며 주게(첨가물 추가) 된다. DarkRoast 객체는 Beverage를 상속받은 클래스로 ConcreteComponent의 역할을 한다. 명칭과 가격을 정의했다.  마지막으로 Moca 클래스는 ConcreteDecorateA의 역할을 하며, 넘겨받는 DarkRoast 클래스에 Moca를 추가하고, 명칭과 가격을 꾸며주게 된다. 사용하는 코드는 다음과 같다. 자바 I/O 패키지에 데코레이터 패턴이 다수 적용되어 있다. FileInputStream → BufferedInputStream → LineNumberInputStream으로 이어지는 확장 구조를 가지고 있다. FileInputSream은 파일을 읽어들이는 기본 동작을 정의하며...

디자인 패턴 #2 - 옵저버 패턴 (observer pattern)

이미지
1. 옵저버 패턴 (observer pattern) 옵저버 패턴(observer pattern)은 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다(one-to-many) 의존성을 가지는 구조를 의미한다. 쉽게 말해 여행 준비를 할 때 '준비가 완료되었는지 매 순간 확인하지 않고, 준비가 완료되면 연락받는 것'과 같다. 옵저버 패턴은 느슨한 결합(loose coupling)으로 두 객체가 연결되는데, 이는 상호작용을 하긴 하지만 서로에 대해 잘 모른다는 것을 의미한다. 옵저버(연락을 받는 객체)가 연락을 받고 싶다면 주체(연락을 하는 객체)에서 정의한 interface를 구현한 뒤 주체에게 등록만 하면 된다. 주체는 옵저버가 무엇을하는지 어떤 클래스를 상속받고 있는지 등에 대해 전혀 알지 않아도 되며, 주체 또는 옵저버가 바뀌더라도 서로에게 영향을 미치지 않는다. 각각의 코드를 살펴보면 ConcretePublischer 객체는 연락을하는 주체로 Observer가 등록되면 ArrayList로 Observer관리하고 이벤트 발생 시 loop를 돌며 등록된 observer에 연락을 준다. registerObserver()를 통해 옵저버를 등록할 수 있으며, unregisterObserver()를 통해 등록된 옵저버를 제거할 수 있다. 연락을 받는 옵저버는 옵저버 interface를 구현한 뒤 Publisher에게 연락을 받기 위해 등록하면 연락받을 준비가 완료되며, 이 후 Publisher에서 Callback이 오면 해당 동작을 처리하면된다. 옵저버 패턴을 사용하면 어떤 정보가 필요한 경우 매 번 확인을 하지 않고, 이벤트가 발생하는 순간에 주체로부터 연락을 받을 수 있다. 옵저버 패턴의 일반적인 사례는 사용자 입력에 대한 이벤트(OnClickListener) 처리가 있으며, MVC(Model-View-Controller) 패턴과 자주 ...

디자인 패턴 #1 - 스트래티지 패턴 (strategy pattern)

이미지
1. 스트래티지 패턴 (strategy pattern) 스트래티지 패턴(strategy pattern)에서는 알고리즘군을 정의하고 각각을 캡슐화하여 교환해서 사용할 수 있도록 만든다. 스트래티지를 활용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할 수 있다. 쉽게 이야기하자면 지정된 알고리즘의 세부 구현을 변경할 필요없이 쉽게 교환할 수 있게 해주는 디자인 패턴이다. 클래스 다이어그램을 살펴보면 오리의 행동을 정의한 2가지 interface가 존재하고, Duck 클래스를 상속받은 오리(MallardDuck, RedheadDuck, RubberDuck)은 각자에게 맞는 행동 객체를 할당하여 사용할 수 있다. 만일 행위가 변경된다면 원하는 행동 객체를 할당하여 쉽게 교체하여 사용할 수 있다. 하기의 RubberDuck을 구현한 코드를 살펴보면 RubberDuck에 맞게 FlyBehavior와 QuackBehavior에 객체를 할당하여 사용하면 되며, 행위에 변경이 발생하더라도 해당 행위에 맞는 객체로 교체해주면 쉽게 동작을 변경할 수 있다. 위의 방식처럼 클래스를 합치는 것을 구성(composition) 관계라 한다. 구성(composition)이란 클래스를 상속하는 대신 기존 클래스(super class)의 인스턴스를 참조하는 private 필드를 서브 클래스로 만들고자 했던 클래스에 만드는 것이다. 즉 RubberDuck 클래스는 Duck 클래스를 상속하는 대신에 Duck 클래스에 추가된 FlyBehavior와 QuackBehavior를 RubberDuck 클래스에서 행동에 맞는 객체를 할당하여 사용하였다. 이와같이 구조를 잡을 때에는 각 클래스의 관계가 'is-a'인지, 'has-a'인지를 이해하고, 관계에 맞는 구조를 잡아주는 것이 좋다. 만일 두 클래스 간의 관계에 대해 자신있게 'is-a'가 맞다고 이야기할 수 없다면 상속(inheritance)은 피해야 하며, ...

이 블로그의 인기 게시물

아스키 코드(ASCII)와 유니코드(unicode)

Kubernetes 학습 노트

알고리즘의 성능과 빅 오 표기법