먼저 이 글은 영문 위키의 글을 번역한 글임을 알려드립니다.

영어 실력이 부족한 관계로 오역이 있을 수 있습니다.

원문 주소 : http://en.wikipedia.org/wiki/Proxy_pattern



구조적 패턴 - 프록시 패턴



컴퓨터 프로그래밍(computer programming)에서 프록시 패턴은 소프트웨어 디자인 패턴(software design pattern) 중 하나이다. 


가장 일반적인 형태의 프록시는 뭔가 다른 것의 인터페이스로 동작하는 클래스의 형태이다. 프록시는 어떤 인터페이스든지 가능하다. : 네트웍 연결, 메모리의 큰 객체, 파일, 또는 비용이 많이 들거나 복제하기 거의 불가능한 자원.


프록시 패턴의 잘 알려진 예는 참조 카운팅 포인터(reference counting pointer)객체이다. 


복잡한 객체를 여러개 복사해야하는 상황이 있다면, 프록시 패턴은 애플리케이션의 메모리 공간을 줄이기 위해 flyweight pattern에 맞춰질 수도 있다. 일반적으로 복잡한 객체 하나와 여러개의 프록시 객체들이 만들어지면, 이들 모두는 하나의 원본 복합 객체에 대한 참조를 포함한다 .프록시에서 수행되는 어떠한 동작(operations)들도 원본 객체로 연결되어진다.(forwared.. 포워드한다는 말인데, 원본 객체에게 위임한다는 표현이 더 어울릴 수도 있겠네요.) 일단 프록시의 모든 객체들이 영역 밖이라면, 복합 객체의 메모리는 해제될지도 모른다.




예 Example

아래의 자바 예는 가상 프록시(virtual proxy) 패턴을 보여주고 있다. ProxyImage 클래스는 원격 메서드에 접근하기 위해서 사용된다.

interface Image {
    void displayImage();
}
 
// on System A 
class RealImage implements Image {
    private String filename;
 
    public RealImage(String filename) { 
        this.filename = filename;
        loadImageFromDisk();
    }
 
    private void loadImageFromDisk() {
        System.out.println("Loading   " + filename);
    }
 
    public void displayImage() { 
        System.out.println("Displaying " + filename); 
    }
 
}
 
//on System B 
class ProxyImage implements Image {
    private String filename;
    private RealImage image;
 
    public ProxyImage(String filename) { 
        this.filename = filename; 
    }
 
    public void displayImage() {
        if (image == null) {
           image = new RealImage(filename);
        } 
        image.displayImage();
    }
}
 
class ProxyExample  {
    public static void main(String[] args) {
        Image image1 = new ProxyImage("HiRes_10MB_Photo1");
        Image image2 = new ProxyImage("HiRes_10MB_Photo2");     
 
        image1.displayImage(); // loading necessary
        image1.displayImage(); // loading unnecessary
        image2.displayImage(); // loading necessary
        image2.displayImage(); // loading unnecessary
        image1.displayImage(); // loading unnecessary
    }
}

프로그램의 결과는 아래와 같다.

Loading   HiRes_10MB_Photo1
Displaying HiRes_10MB_Photo1
Displaying HiRes_10MB_Photo1
Loading   HiRes_10MB_Photo2
Displaying HiRes_10MB_Photo2
Displaying HiRes_10MB_Photo2
Displaying HiRes_10MB_Photo1


저작자 표시 비영리 변경 금지
신고
Posted by Code-Moon

댓글을 달아 주세요

먼저 이 글은 영문 위키의 글을 번역한 글임을 알려드립니다.

영어 실력이 부족한 관계로 오역이 있을 수도 있습니다.

원문 주소 : http://en.wikipedia.org/wiki/Flyweight_pattern



구조적 패턴 - 플라이웨이트 패턴



컴퓨터 프로그래밍에서 플라이웨이트는 소프트웨어 디자인 패턴 중 하나이다. 플라이웨이트는 가능한 많은 데이터는 다른 유사한 객체들과 공유함으로써 메모리 사용을 최소화하는 객체이다. ; 이는 어떤 단순 반복되는 표현(representation)이 받아들일 수 없을 정도의 메모리를 사용할 때 객체를 수없이 많이 사용하는 방법이다. 종종 객체 상태의 일부분은 공유될 수 있고, 이를 외부 자료구조(data structures)에 두고, 사용될 때에 플라이웨이트 객체에 전달하는 방법이 일반적이다.




플라이웨이트 패턴의 고전적인 사용예는 워드 프로세서(word processor)에서 문자들의 그래픽적 표현에 대한 자료구조이다. 한 문서, 폰트의 외곽선을 포함하는 글자모양(glyph) 객체(그냥 글자의 모양을 표현하는 외곽선이라 생각하면 쉬울 듯 합니다. ), 폰트 매트릭스, 그리고 다른 포맷 데이터 각각에 각 문자들을 가지고 있는 것이 바람직할지도 모르지만, 이렇게 하면 한 문자당 수백 또는 수천 바이트가 필요할지도 모른다. 대신에, 모든 문자들이 그 문서 안의 같은 문자 객체에 의해 공유되는 플라이웨이트 글자모양 객체에 대한 레퍼런스(reference)가 될 수 있다.; 오직 각 문자의 위치정보만이 내부적으로 저장될 필요가 있을 것이다.


다른 문맥에서 동일한 자료 구조를 공유하는 아이디어는 hash consing이라 불린다.

역사 History

Designe Patterns: Elements of Reusable Object-Oriented Software 책에 따르면, 플라이웨이트 패턴은 1990년에 Paul Calder와 Mark Linton이 WYSIWYG 문서 편집기의 글자모양 정보를 효율적으로 다루기 위해 처음 도입되고 널리 연구되어졌다. 비록 비슷한 기술이 다른 시스템에서 이미 사용되고 있었지만 말이다.(1988년에 Weinand 등의 한 애플리케이션 프레임웍에서..)

자바 예 Example in Java

저작자 표시 비영리 변경 금지
신고
Posted by Code-Moon

댓글을 달아 주세요

먼저 이 글은 영문 위키의 글을 번역한 글임을 알려드립니다.

영어 실력이 부족한 관계로 오역이 있을 수도 있습니다.

원문 주소 : http://en.wikipedia.org/wiki/Facade_pattern



구조적 패턴 - 퍼사드 패턴



퍼사드 패턴은 객체 지향 프로그래밍에서 종종 사용되는 소프트웨어 디자인 패턴이다. 그 이름은 건축학의 퍼사드(전면, facade)에서 유추되었다. 

퍼사드는 많은 양의 코드를 단순화된 인터페이스로 제공하는 객체이다. (예를들면 클래스 라이브러리가 있다.)

퍼사드는 다음과 같은 것들을 할 수 있다. 

  • 퍼사드는 공통적인 작업(tasks)에 대해 편리한 메서드를 가지기 때문에, 더 사용하기 쉽고, 이해하기 쉽고, 테스트하기 쉽게 소프트웨어 라이브러리를 만들 수 있다. 
  • 같은 이유로 라이브러리를 더 읽기 쉽게 만들 수 있다.
  • 대부분의 코드가 퍼사드를 사용하기 때문에, 바깥 코드와 라이브러리 안쪽 코드의 의존성(dependency)을 줄일 수 있고, 따라서 시스템을 개발하는데에 더 유연성을 제공한다.
  • 형편없이 설계된 API의 컬렉션을 하나의 잘 설계된 API로 감쌀 수 있다.


감싸는 클래스(wrapper)가 특정 인터페이스를 준수해야하고 다형성(polymorphic)을 지원해야만 한다면 어댑터(adapter)가 사용된다. 반면에, 퍼사드는 더 쉽거나 간단한 인터페이스를 원할 때 사용한다.

구조 Structure


Facde - 퍼사드 클래스는 Package 1, 2, 3을 나머지 애플리케이션으로부터 추상화시킨다.

Clients - Package들의 리소스에 접근하기 위해 퍼사드 패턴을 사용하는 객체

예 Example

이 추상적인 예는 복잡한 시스템에서(CPU와 하드 드라이브 같은 컴퓨터 내부) 클라이언트(당신)가 어떻게 퍼사드(컴퓨터)와 상호작용하는지 보여준다.

/* Complex parts */
 
class CPU {
    public void freeze() { ... }
    public void jump(long position) { ... }
    public void execute() { ... }
}
 
class Memory {
    public void load(long position, byte[] data) { ... }
}
 
class HardDrive {
    public byte[] read(long lba, int size) { ... }
}
 
/* Facade */
 
class Computer {
    private CPU cpu;
    private Memory memory;
    private HardDrive hardDrive;
 
    public Computer() {
        this.cpu = new CPU();
        this.memory = new Memory();
        this.hardDrive = new HardDrive();
    }
 
    public void startComputer() {
        cpu.freeze();
        memory.load(BOOT_ADDRESS, hardDrive.read(BOOT_SECTOR, SECTOR_SIZE));
        cpu.jump(BOOT_ADDRESS);
        cpu.execute();
    }
}
 
/* Client */
 
class You {
    public static void main(String[] args) {
        Computer facade = new Computer();
        facade.startComputer();
    }
}


저작자 표시 비영리 변경 금지
신고
Posted by Code-Moon

댓글을 달아 주세요

먼저 이글은 영문 위키의 글을 번역한 글임을 알려드립니다.
영어 실력이 부족한 관계로 오역이 있을 수도 있습니다.


구조적 패턴 - 데코레이터 패턴




객체 지향 프로그래밍에서 데코레이터 패턴은 이미 존재하는 객체에 동적으로 행위(동작, behaviour)를 추가할 수 있도록 하는 디자인 패턴이다.

도입 Introduction

데코레이터 패턴은 런타임에 특정 객체의 기능을 확장하는데 사용될 수 있는데, 이 때 같은 클래스의 다른 객체에는 무관하게 기능을 확장할 수 있고,  이는 기초작업이 설계할 때 이미 완료되었을 때 가능한 일이다. 이는 원래(original) 클래스를 감싸는 새로운 데코레이터 클래스를 설계함으로써 얻을 수 있는 성과이다. 이렇게 데코레이터로 감싸는 것은 아래의 순서에 따라 하게 된다.

  1. 원래의(original) "Decorator"클래스를 "Component"클래스로부터 서브 클래싱한다.(뒤에 나오는 UML 다이어그램을 보라.)
  2. Decorator 클래스에서 Component에 대한 포인터를 필드(멤버 변수)로 추가해라.
  3. Component를 Decorator의 생성자에 인자로 전달해서, Decorator클래스에서 Component 포인터를 초기화할 수 있도록 하라.
  4. Decorator클래스에서 모든 "Component" 메서드를 "Component" 포인터로 재전송하라(redirect. 이 말을 다시 설명하자면, Component 클래스에 있는 operation()이라는 메서드를 Decorator에서 오버라이드를 하게 되는데, 이 때 Decorator에서 포인터로 가지고 있는 Component객체의 operation()을 다시 호출하라는 의미입니다. )
  5. ConcreteDecorator 클래스에서 Component 클래스의 수정이 필요한 메서드를 오버라이드 하라.


이 패턴은  매번 오버라이드된 메서드에 새로운 기능이 추가될 때마다(새로운 데코레이터로 감쌀때마다), 여러개의 데코레이터들이 스택처럼 쌓일 수 있게 설계한다.

데코레이터 패턴은 서브 클래싱의 대안이다. 서브 클래싱은 컴파일할 때 행동을 추가하게 되고, 그때 클래스의 변화는 모든 원래의(original) 클래스의 객체에 대해 변화를 일으킨다. ; 꾸미는 것(decorating)은 런타임에 각각의 객체에 새로운 행위를 제공할 수 있다. 


이러한 차이는 기능을 확장하는 몇몇 독립적인 방법들이 있을 때 아주 중요하게 다가온다. 일부 객체 지향 프로그래밍 언어에서는 클래스가 런타임에 생성될 수 없고, 전형적으로(보통, typically) 이는 설계할 때에 어떤 확장의 조합이 필요한지 예측할 수 없게된다. 이는 새로운 클래스는 모든 가능한 조합에 대해 만들어져야 함을 의미할지도 모른다. 대조적으로 데코레이터는 런타임에 만들어지는 객체라서 사용할 때마다 조합될 수 있다. 자바와 .NET 프레임웍의 I/O Streams 구현은 데코레이터 패턴을 포함하고 있다.

동기 Motivation

한 예로써, 윈도우 시스템의 윈도우를 생각해보자. 윈도우의 컨텐츠를 스크롤할 수 있도록 하기 위해, 우리는 수평 스크롤바나 수직 스크롤바를 추가하고 싶을 수도 있다. 윈도우가 Window클래스의 객체에 의해 표현된다고 가정하고, 이 클래스는 스크롤바를 추가하는 기능이 없다고 가정하자. 우리는 스크롤 기능을 제공하는 ScrollingWindow를 Window로 부터 서브클래싱하여 만들거나, 이미 존재하는 Window객체에 스크롤 기능을 추가하는 ScrollingWindowDecorator를 만들 수 있다. 이 상황에서는 둘 중 어떤 해결책도 괜찮다.


그렇다면 이번에는 우리의 윈도우에 경계선(borders)을 추가하고 싶다고 가정해보자. 마찬가지로 원래의 Window클래스는 이러한 기능을 지원하지 않는다. ScrollingWindow 서브클래스는 효과적으로 새로운 윈도우를 만들었기 때문에 한가지 문제가 생겼다. 

만약 우리가 모든 윈도우에 경계선을 추가하고 싶다면 WindowWithBorder라는 서브클래스와 ScrollingWindowWithBorder라는 서브클래스를 만들면 된다. 확실히 이런 문제는 모든 새로운 기능이 추가될 때마다 더 문제가 된다. 

데코레이터 방법으로는 런타임에 간단히 BorderWindowDecorator를 만들면 된다. 우리는 이미 존재하는 윈도우를 ScrollingWindowDecorator나 BorderedWindowDecorator로 꾸밀 수 있다.


데코레이터가 사용되면 좋은 또다른 상황은 어떤 규칙에 따라 객체의 속성이나 메서드에 대한 접근을 한정지어야 할 필요가 있을 때 이다. (예를들면 다른 사용자 자격인증서.) 이러한 경우에 원래의 객체에 접근 제어를 구현하는 것보다는 원래 객체는 변화시키지 않고 속성이나 메서드 사용에 대해 아무것도 모르도록하고, 접근 제어를 할 수 있는 데코레이터 객체로 감싸면 오직 승인된 인터페이스만 제공하도록 할 수 있다.

구조 Structure


예 Examples

자바

첫번째 예 (window / scrolling 시나리오)

아래의 자바 예는 window/scrolling 시나리오에서 데코레이터의 사용을 설명하고 있다.

// the Window interface
interface Window {
    public void draw(); // draws the Window
    public String getDescription(); // returns a description of the Window
}
 
// implementation of a simple Window without any scrollbars
class SimpleWindow implements Window {
    public void draw() {
        // draw window
    }
 
    public String getDescription() {
        return "simple window";
    }
}


아래의 클래스는 모든 Window클래스(데코레이터 자신들을 포함한)에 대한 데코레이터들이다.

// abstract decorator class - note that it implements Window
abstract class WindowDecorator implements Window {
    protected Window decoratedWindow; // the Window being decorated
 
    public WindowDecorator (Window decoratedWindow) {
        this.decoratedWindow = decoratedWindow;
    }
    public void draw() {
        decoratedWindow.draw();
    }
}
 
// the first concrete decorator which adds vertical scrollbar functionality
class VerticalScrollBarDecorator extends WindowDecorator {
    public VerticalScrollBarDecorator (Window decoratedWindow) {
        super(decoratedWindow);
    }
 
    public void draw() {
        decoratedWindow.draw();
        drawVerticalScrollBar();
    }
 
    private void drawVerticalScrollBar() {
        // draw the vertical scrollbar
    }
 
    public String getDescription() {
        return decoratedWindow.getDescription() + ", including vertical scrollbars";
    }
}
 
// the second concrete decorator which adds horizontal scrollbar functionality
class HorizontalScrollBarDecorator extends WindowDecorator {
    public HorizontalScrollBarDecorator (Window decoratedWindow) {
        super(decoratedWindow);
    }
 
    public void draw() {
        decoratedWindow.draw();
        drawHorizontalScrollBar();
    }
 
    private void drawHorizontalScrollBar() {
        // draw the horizontal scrollbar
    }
 
    public String getDescription() {
        return decoratedWindow.getDescription() + ", including horizontal scrollbars";
    }
}

여기 아래에는 완전히 꾸며진(fully decorated) Window객체를 생성하는 테스트 프로그램이고, 실행해보면 설명을 출력한다.

public class DecoratedWindowTest {
    public static void main(String[] args) {
        // create a decorated Window with horizontal and vertical scrollbars
        Window decoratedWindow = new HorizontalScrollBarDecorator (
                new VerticalScrollBarDecorator(new SimpleWindow()));
 
        // print the Window's description
        System.out.println(decoratedWindow.getDescription());
    }
}

이 프로그램의 결과는 "simple window, including vertical scrollbars, including horizontal scrollbars"라고 나올 것이다. 두 데코레이터의 getDescription 메서드에서 어떻게 처음에 꾸며진(decorated) Window의 설명(description)을 가져오고 그 뒤에 꾸미는 작업(decorates)을 하게되는지 유심히 살펴보아라.




두번째 예(커피 만드는 시나리오)

다음의 자바 예는 커피를 만드는 시나리오에서 데코레이터를 사용하는 방법을 설명하고 있다. 이 예에서 시나리오는 가격과 재료만 포함하고 있다.

// The Coffee Interface defines the functionality of Coffee implemented by decorator
public interface Coffee {
    public double getCost(); // returns the cost of the coffee
    public String getIngredients(); // returns the ingredients of the coffee
}
 
// implementation of a simple coffee without any extra ingredients
public class SimpleCoffee implements Coffee {
    public double getCost() {
        return 1;
    }
 
    public String getIngredients() {
        return "Coffee";
    }
}

다음의 클래스들에는 모든 커피 클래스가 포함되어 있다.(데코레이터 자신도 포함해서 말이다.)

// abstract decorator class - note that it implements Coffee interface
abstract public class CoffeeDecorator implements Coffee {
    protected final Coffee decoratedCoffee;
    protected String ingredientSeparator = ", ";
 
    public CoffeeDecorator(Coffee decoratedCoffee) {
        this.decoratedCoffee = decoratedCoffee;
    }
 
    public double getCost() { // implementing methods of the interface
        return decoratedCoffee.getCost();
    }
 
    public String getIngredients() {
        return decoratedCoffee.getIngredients();
    }
}
 
// Decorator Milk that mixes milk with coffee
// note it extends CoffeeDecorator
public class Milk extends CoffeeDecorator {
    public Milk(Coffee decoratedCoffee) {
        super(decoratedCoffee);
    }
 
    public double getCost() { // overriding methods defined in the abstract superclass
        return super.getCost() + 0.5;
    }
 
    public String getIngredients() {
        return super.getIngredients() + ingredientSeparator + "Milk";
    }
}
 
// Decorator Whip that mixes whip with coffee
// note it extends CoffeeDecorator
public class Whip extends CoffeeDecorator {
    public Whip(Coffee decoratedCoffee) {
        super(decoratedCoffee);
    }
 
    public double getCost() {
        return super.getCost() + 0.7;
    }
 
    public String getIngredients() {
        return super.getIngredients() + ingredientSeparator + "Whip";
    }
}
 
// Decorator Sprinkles that mixes sprinkles with coffee
// note it extends CoffeeDecorator
public class Sprinkles extends CoffeeDecorator {
    public Sprinkles(Coffee decoratedCoffee) {
        super(decoratedCoffee);
    }
 
    public double getCost() {
        return super.getCost() + 0.2;
    }
 
    public String getIngredients() {
        return super.getIngredients() + ingredientSeparator + "Sprinkles";
    }
}

아래에는 완전히 꾸며진(fully decorated) Coffee의 객체를 생성하는 테스트 프로그램이고, 여기에서는 커피의 가격을 계산하고 재료를 출력한다.

public class Main
{
    public static void main(String[] args)
    {
        Coffee c = new SimpleCoffee();
        System.out.println("Cost: " + c.getCost() + "; Ingredients: " + c.getIngredients());
 
        c = new Milk(c);
        System.out.println("Cost: " + c.getCost() + "; Ingredients: " + c.getIngredients());
 
        c = new Sprinkles(c);
        System.out.println("Cost: " + c.getCost() + "; Ingredients: " + c.getIngredients());
 
        c = new Whip(c);
        System.out.println("Cost: " + c.getCost() + "; Ingredients: " + c.getIngredients());
 
        // Note that you can also stack more than one decorator of the same type
        c = new Sprinkles(c);
        System.out.println("Cost: " + c.getCost() + "; Ingredients: " + c.getIngredients());
    }
}

이 프로그램의 출력 결과는 다음과 같다.

Cost: 1.0; Ingredients: Coffee
Cost: 1.5; Ingredients: Coffee, Milk
Cost: 1.7; Ingredients: Coffee, Milk, Sprinkles
Cost: 2.4; Ingredients: Coffee, Milk, Sprinkles, Whip
Cost: 2.6; Ingredients: Coffee, Milk, Sprinkles, Whip, Sprinkles



동적인 언어들(Dynamic languages)

데코레이터 패턴은 동적인 언어들에서도 사용될 수 있는데, 이때에는 인터페이스와 전통적인 OOP 상속을 사용하지 않고 쓴다.

자바스크립트(커피 만드는 시나리오)

// Class to be decorated
function Coffee() {
 
}
 
Coffee.prototype.cost = function() {
        return 1;
};
 
// Decorator A
function Milk(coffee) {
        var currentCost = coffee.cost();
        coffee.cost = function() {
                return currentCost + 0.5;
        };
 
        return coffee;
}
 
// Decorator B
function Whip(coffee) {
        var currentCost = coffee.cost();
        coffee.cost = function() {
                return currentCost + 0.7;
        };
 
        return coffee;
}
 
// Decorator C
function Sprinkles(coffee) {
        var currentCost = coffee.cost();
        coffee.cost = function() {
                return currentCost + 0.2;
        };
 
        return coffee;
}
 
// Here's one way of using it
var coffee = new Milk(new Whip(new Sprinkles(new Coffee())));
alert( coffee.cost() );
 
// Here's another
var coffee = new Coffee();
coffee = new Sprinkles(coffee);
coffee = new Whip(coffee);
coffee = new Milk(coffee);
alert(coffee.cost());


저작자 표시 비영리 변경 금지
신고
Posted by Code-Moon

댓글을 달아 주세요

먼저 이 글은 영문위키의 글을 번역한 글임을 알려드립니다.

영어 실력이 부족한 관계로 오역이 있을 수도 있습니다.

원문 주소 : http://en.wikipedia.org/wiki/Composite_pattern



구조적 패턴 - 콤포짓 패턴



소프트웨어 공학에서 콤포짓 패턴은 분할하는(partitioning) 디자인 패턴이다. (왜 partitioning이라는 단어가 나왔는지 잘 이해가 안되네요.;;) 콤포짓 패턴은 객체 그룹이 단일 객체와 같이 취급되도록 한다. 컴포짓의 목적(의도, intent)은 객체들을 트리(tree) 구조로 "구성(compose)"하여 부분-전체 구조(part-whole hierarchies)를 표현하기 위함이다. 콤포짓 패턴을 구현하는 것은 단일 객체(individual objects)와 구성(composition, 여기에서는 그룹 정도로 이해해도 괜찮을 것 같네요.)을 동일하게 취급하도록 해준다.

동기 Motivation

트리 구조의 데이터를 다룰 때, 프로그래머는 종종 리프노드(leaf-node)와 브랜치(branch)를 구분해야만 한다. 이는 코드를 더욱 복잡하게 만들고 따라서 에러가 발생하기도 쉽다. 해결책은 복합객체(complex)와 원시객체(primitive)를 동일하게 다루는 인터페이스를 사용하는 것이다. 객체 지향 프로그래밍에서 컴포짓은 하나 이상의 비슷한 객체들을 가지고 하나의 구성(composition)으로 설계된 객체이다. 이는 객체 사이의 "has-a"관계로 알려져있다. 중요한 컨셉은 객체의 한 인스턴스를 다룰 때, 그 객체들의 그룹을 다루는 것처럼 다룰 수 있다는 것이다. 모든 컴포짓 객체들에서 당신이 수행할 수 있는 동작들(operations)은 종종 최소 공통 분모(least common denominator) 관계에 있다. 예를 들어, 그룹핑된(grouped) 도형(shapes)들을 화면에 그리는 시스템을 정의한다고 하면, 도형들의 그룹을 리사이징(resizing)하는 것이 하나의 도형을 리사이징하는 것과 같은 효과를 가지도록 정의하는 것이 유용할 것이다. 

언제사용하는가? When to use

컴포짓은 클라이언트 프로그램이 객체들의 구성(composition)과 개개의 객체 사이에 차이점을 무시해야할 때 사용될 수 있다. 만약 프로그래머가 여러개의 객체들을 같은 방법으로 사용하거나, 종종 그 객체 각각을 다루기 위해 거의 비슷한 코드를 사용하고 있는 자신의 모습을 발견한다면, 컴포짓이 좋은 선택이 될 수 있다. ; 이러한 상황에서 원시(primitive)객체와 구성 객체를 동일하게 다루는 것이 덜 복잡하다. 

구조 Structure

Component

  • 컴포넌트 자신을 포함한 모든 컴포넌트에 대한 추상화이다.
  • 구성(composition)에 있는 객체들의 인터페이스를 정의한다.
  • (선택적으로) component의 부모에 대한 접근하기 위한 재귀적인 구조(recursive structure)의 인터페이스를 정의하고, 적절하다면 구현한다.

Leaf

  • 구성(composition)에 있는 leaf 객체들을 표현한다.
  • component의 모든 메서드를 구현한다.

Composite

  • composite 컴포넌트를 표현한다.(자식들을 가지는 컴포넌트)
  • 자식들을 다루기 위한 메서드를 구현한다.
  • 모든 component 메서드를 구현한다.(일반적으로 메서드 기능을 자식에게 위임해서 구현한다. 여기에서 위임이라함은 자식의 기능을 호출한다고 보면 될 것 같네요.)

변형 Variation

Design Patterns에 묘사되어 있듯이,  이 패턴에는 Composite 클래스 뿐만 아니라 메인 Component 인터페이스에도 자식을 관리하는 메서드를 포함하고 있다. 더 최근의 몇몇 설명에서는 이러한 메서드를 누락시키고 있다. 

예 Example

자바로 작성된 다음의 예제는 Graphic 클래스를 구현하는데, 이 클래스는 Ellipse(타원)이나 다른 몇몇 그래픽의 구성(composition)이 될 수 있다. 모든 그래픽은 출력될 수 있다.


수학적으로 표현하면 다음과 같다.

Graphic = ellipse | GraphicList
GraphicList = empty | Graphic GraphicList


이는 직사각형과 같은 다른 도형들과 translate와 같은 다른 메서드들을 구현하게 끔 확장될 수도 있다.


저작자 표시 비영리 변경 금지
신고
Posted by Code-Moon

댓글을 달아 주세요

먼저 이 글은 영문 위키의 글을 번역한 글임을 알려드립니다.

영어 실력이 부족한 관계로 오역이 있을 수 있습니다.^^;

원문 주소 : http://en.wikipedia.org/wiki/Adapter_pattern



구조 패턴 - 어댑터(Apapter)



컴퓨터 프로그래밍에서 어댑터 패턴은(종종 wrapper pattern이라고 언급되기도 하고, 더 간단히 wrapper라고 불리기도 한다.) 한 클래스의 인터페이스를 호환 가능한 다른 인터페이스로 변환시키는 디자인 패턴이다. 

호환되지 않는 인터페이스때문에 일반적인 방법으로는 같이 사용할 수 없는 클래스들을 어댑터 패턴을 이용하면 같이 사용할 수 있다. 이는 클라이언트 코드에게 원래 클래스의 인터페이스를 노출시키는 대신에 어댑터의 인터페이스를 노출시킴으로써 가능해진다. 

어댑터는 어댑터 인터페이스에 대한 호출을 원래 클래스의 인터페이스 호출로 변환하고, 이를 위해 필요한 코드 량은 일반적으로 작다. 어댑터는 또한 데이터를 적절한 형태로 변환하는 역할도 한다. 

예를 들어, 여러개의 불(boolean) 값이 하나의 정수에 들어있는데(예를들면 flag같은 경우), 당신의 클라이언트 코드에서는 하나의 'true'나 'false' 값만 필요하다면 어댑터는 이 정수에서 적절한 값을 추출해내는 역할을 할 수 있다. 

다른 예는 날짜의 형태를 변경하는 것이다.(예를들면 YYYYMMDD를  MM/DD/YYYY 형태로 만들거나 DD/MM/YYYY 같은 형태로 만들 수 있다.)




구조 Structure

어댑터 패턴에는 두가지 종류가 있다. 

객체 어댑터 패턴 Object Adapter Pattern

이 타입의 어댑터 패턴에서 어댑터는 감싸야할 클래스의 객체를 포함하고 있다. 이러한 상황에서 어댑터는 감싸진 객체를 호출하는 일을 한다.






클래스 어댑터 패턴 Class Adapter Pattern

이 타입의 어댑터는 목표를 달성하기 위해 여러개의 다형성 인터페이스(polymorphic interfaces)를 사용한다. 어댑터는 구현될 예정인 인터페이스나 이미 구현된 인터페이스를 상속받아서 만들어진다. 구현될 예정인 인터페이스는 순수(pure) 인터페이스 클래스로 만들어지는 것이 일반적인데, 특히 자바같이 다중 상속(multiple inheritance)을 지원하지 않는 언어들에서 그러하다.




어댑터 패턴은 이미 존재하는 클래스가 당신이 필요로하는 일부 또는 전체 서비스를 제공하지만 당신에게 필요한 인터페이스는 제공하지 않는 상황에 유용할 수 있다. 어댑터의 좋은 실제 예는 XML 문서의 DOM(Document Object Model)의 인터페이스를 볼 수 있는  트리 구조로 바꾸는 것이다. 어댑터 디자인 패턴을 사용하는 튜토리얼 링크는 아래에 나열해 놓았다. 



더 발전된 형태의 실시간 어댑터 패턴(?) A further form of runtime Adapter Pattern

아래에 나오는 것과 같이 더 발전된 형태의 실시간으로 사용될 수 있는 어댑터 패턴이 있다 : 

classA가 약간의 데이터를 가지고(String데이터라 가정하고) classB를 지원하도록 요구되어진다. 컴파일 시의 해결책은 다음과 같다.



classB.setStringData(classA.getStringData());


string데이터의 포맷(형식)이 다양해져야 한다고 가정해보자. 컴파일 시의 해결책은 상속을 사용하는 것이다.:
Format1ClassA extends ClassA {
   public String getStringData() {
      return format(toString());
   }
}


그리고 팩토리 패턴의 도움으로 런타임시에 정확하게 표현할 형식(formatting) 객체를 만들 수 있다.



어댑터를 사용하는 방법의 진행 절차는 다음과 같다.:



(i) 중개하는 Provider 인터페이스를 정의하고, 데이터의 소스를 감싸는 Provider 인터페이스의 구현을 작성해라. 이 예에서는 ClassA가 데이터 소스가 되고, 적절한 형태를 갖춘 데이터를 만들어낸다.


public interface StringProvider {
    public String getStringData();
}
 
public class ClassAFormat1 implements StringProvider {
    ClassA classA;
 
    public ClassAFormat1(ClassA classA) {
        this.classA = classA;
    }
 
    public String getStringData() {
        return format(classA.toString());
    }
}



(ii) Provider의 특정 구현을 반환하는 Adapter클래스를 작성한다.
public class ClassAFormat1Adapter extends Adapter {
   public Object adapt(Object o) {
      return new ClassAFormat1((ClassA) o);
   }
}


(iii) Adatper를 전역 레지스트리에 등록해서 Adapter가 런타임 시에 사용될 수(can be loocked up) 있도록 한다.
AdapterFactory.getInstance().registerAdapter(ClassA.class, ClassAFormat1Adapter.class, "format1");



(iv) 당신의 코드에서 ClassA의 데이터를 ClassB로 옮기고 싶다면 다음과 같이 작성한다.


Adapter adapter = AdapterFactory.getInstance().getAdapterFromTo(ClassA.class, StringProvider.class, "format1");
StringProvider provider = (StringProvider) adapter.adapt(classA);
String string = provider.getStringData();
classB.setStringData(string);


또는 더 간결하게 하자면,

classB.setStringData(((StringProvider) AdapterFactory.getInstance().getAdapterFromTo(ClassA.class, StringProvider.class, "format1").adapt(classA)).getStringData());


(v) 만약 데이터를 두번째 형식으로 전송하고 싶다면 다른 Adapter와 Provider를 찾아서(look up) 쓸 수 있다는 것이 장점이라 볼 수 있다.


Adapter adapter = AdapterFactory.getInstance().getAdapterFromTo(ClassA.class, StringProvider.class, "format2");



(vi) 그리고 만약 ClassA로부터 ClassC의 이미지 데이터로 출력하고 싶다면 :
Adapter adapter = AdapterFactory.getInstance().getAdapterFromTo(ClassA.class, ImageProvider.class, "format1");
ImageProvider provider = (ImageProvider) adapter.adapt(classA);
classC.setImage(provider.getImage());



(vii) 이런 방법으로 Adapter와 Provider를 사용하는 것은 여러개의 뷰(views)를 제공할 수 있다. 일반적으로, 기존 객체 구조에 새로 장착할 수 있는 객체들 사이를 중개 데이터가 들어갈 수 있는 메카니즘을 허용한다.(그러니까 기존 객체 구조를 유지하고 어댑터를 이용한 메카니즘을 사용한다는 의미인 것 같은데,,, 해석하려니 말이 어렵네요-_-)




어댑터 패턴의 구현 Implementation of Adapter pattern

어댑터 패턴을 구현할 때, 명료성을 위해서 클래스 이름을 "[AdapteeClassName]To[Interface]Adapter"와 같은 식으로 만든다. 예를들면 DAOToProviderAdapter(여기에서 DAO가 AdapteeClassName에 해당하고, Provider가 Interface에 해당하겠죠?). 

이를 위해서는 adaptee클래스를 인자로 받는 생성자가 필요하다. 이 인자는 "[AdapteeClassName]To[Interface]Adapter"의 객체 멤버로 전달될 것이다.


Class SampleAdapter implements ClientClass
{
    private AdapteeClass mInstance;
    public SampleAdapter(AdapteeClass instance)
    {
         mInstance=instance;
    }
    @Override
    public void ClientClassMethod()
    {
       // call AdapteeClass's method to implement ClientClassMethod
    }
 
}


저작자 표시
신고
Posted by Code-Moon

댓글을 달아 주세요

티스토리 툴바