실제 예를 들어 "데코레이터 패턴"이해


167

나는 GOF에 문서화 된 데코레이터 패턴 을 연구하고있었습니다 .

데코레이터 패턴을 이해하도록 도와주세요 . 누군가가 이것이 실제 세계에서 유용한 곳의 사용 사례를 제시 할 수 있습니까?


8
Java API에서 실제 예제를 찾을 수 있습니다 : stackoverflow.com/questions/1673841/…
BalusC

간단한 예제를 통해 데코레이터 패턴의 장점을 보여주는 기사 : dzone.com/articles/is-inheritance-dead
nbilal

답변:


226

데코레이터 패턴은 객체에 책임을 동적으로 추가하는 단일 목표를 달성합니다.

피자 가게의 경우를 생각해보십시오. 피자 가게에서는 피자 종류를 거의 판매하지 않으며 메뉴에 토핑을 제공합니다. 이제 피자 가게가 피자와 토핑의 각 조합에 대해 가격을 제공해야하는 상황을 상상해보십시오. 4 개의 기본 피자와 8 개의 다른 토핑이 있더라도 피자와 토핑의 모든 구체적인 조합을 유지하면 응용 프로그램이 열광 할 것입니다.

여기 데코레이터 패턴이 있습니다.

데코레이터 패턴에 따라 데코레이터로 토핑을 구현하고 피자는 토핑 데코레이터로 장식됩니다. 실제로 각 고객은 자신이 원하는 토핑을 원할 것이며 최종 청구 금액은 기본 피자와 추가 주문 토핑으로 구성됩니다. 각 토핑 데코레이터는 장식하고있는 피자에 대해 알고 가격입니다. Topping 객체의 GetPrice () 메서드는 피자와 토핑의 누적 가격을 반환합니다.

편집하다

위의 코드 예제는 다음과 같습니다.

public abstract class BasePizza
{
    protected double myPrice;

    public virtual double GetPrice()
    {
        return this.myPrice;
    }
}

public abstract class ToppingsDecorator : BasePizza
{
    protected BasePizza pizza;
    public ToppingsDecorator(BasePizza pizzaToDecorate)
    {
        this.pizza = pizzaToDecorate;
    }

    public override double GetPrice()
    {
        return (this.pizza.GetPrice() + this.myPrice);
    }
}

class Program
{
    [STAThread]
    static void Main()
    {
        //Client-code
        Margherita pizza = new Margherita();
        Console.WriteLine("Plain Margherita: " + pizza.GetPrice().ToString());

        ExtraCheeseTopping moreCheese = new ExtraCheeseTopping(pizza);
        ExtraCheeseTopping someMoreCheese = new ExtraCheeseTopping(moreCheese);
        Console.WriteLine("Plain Margherita with double extra cheese: " + someMoreCheese.GetPrice().ToString());

        MushroomTopping moreMushroom = new MushroomTopping(someMoreCheese);
        Console.WriteLine("Plain Margherita with double extra cheese with mushroom: " + moreMushroom.GetPrice().ToString());

        JalapenoTopping moreJalapeno = new JalapenoTopping(moreMushroom);
        Console.WriteLine("Plain Margherita with double extra cheese with mushroom with Jalapeno: " + moreJalapeno.GetPrice().ToString());

        Console.ReadLine();
    }
}

public class Margherita : BasePizza
{
    public Margherita()
    {
        this.myPrice = 6.99;
    }
}

public class Gourmet : BasePizza
{
    public Gourmet()
    {
        this.myPrice = 7.49;
    }
}

public class ExtraCheeseTopping : ToppingsDecorator
{
    public ExtraCheeseTopping(BasePizza pizzaToDecorate)
        : base(pizzaToDecorate)
    {
        this.myPrice = 0.99;
    }
}

public class MushroomTopping : ToppingsDecorator
{
    public MushroomTopping(BasePizza pizzaToDecorate)
        : base(pizzaToDecorate)
    {
        this.myPrice = 1.49;
    }
}

public class JalapenoTopping : ToppingsDecorator
{
    public JalapenoTopping(BasePizza pizzaToDecorate)
        : base(pizzaToDecorate)
    {
        this.myPrice = 1.49;
    }
}

104
이 패턴은 1 비트를 좋아하지 않습니다. 어쩌면 그것은 예일 것입니다. OOD와 관련하여 내가 가진 주요 문제는 토핑 이 피자가 아니라는 것입니다 . 그것이 적용되는 피자의 가격에 대한 토핑을 요구하는 것은 단지 나와 함께 앉아 있지 않습니다. 그것은 매우 사려 깊고 상세한 예이므로, 나는 당신을 노크한다는 의미는 아닙니다.
Tom W

39
@ TomWm 문제의 일부는 이름 지정이라고 생각합니다. 모든 "Topping"클래스는 "PizzaWith <Topping>"이라고합니다. 예를 들어 "PizzaWithMushrooms"입니다.
Josh Noe

2
제 생각에 데코레이터는 가능한 한 평평하게 사용하는 것이 가장 좋습니다. 그것은 가능한 한 "데코레이터를 감싸는 데코레이터"를 의미합니다. 아마도이 예는 가장 적합하지 않습니다. 그러나 그것은 매우 철저합니다.
thekingoftruth

17
다른 관점에서 이것은 "실제 세계"에 가깝지 않습니다. 현실에서는 메뉴에 새 토핑을 추가하거나 가격을 변경할 때마다 다시 컴파일하지 않아야합니다. 토핑은 (보통) 데이터베이스에 저장되므로 위의 예를 쓸모 없게 만듭니다.
Stelios Adamantidis

4
^ 이것. 나는 이것이이 패턴을 연구하는 동안 나를 귀찮게 한 것이라고 생각합니다. 소프트웨어 회사에서 피자 전문점 소프트웨어를 작성했다면 매번 다시 컴파일하고 다시 보내지 않아도됩니다. 백엔드의 테이블에 행을 추가하거나 요구 사항을 쉽게 처리 할 수있는 항목을 추가하고 싶습니다. 잘 말했다, @Stelios Adamantidis. 제 3 자 클래스를 수정하는 것이 가장 큰 패턴이라고 생각합니다.
Canucklesandwich

33

기존 객체에 동적으로 새 동작을 추가하거나 데코레이터 패턴을 추가하는 간단한 예입니다. Javascript와 같은 동적 언어의 특성으로 인해이 패턴은 언어 자체의 일부가됩니다.

// Person object that we will be decorating with logging capability
var person = {
  name: "Foo",
  city: "Bar"
};

// Function that serves as a decorator and dynamically adds the log method to a given object
function MakeLoggable(object) {
  object.log = function(property) {
    console.log(this[property]);
  }
}

// Person is given the dynamic responsibility here
MakeLoggable(person);

// Using the newly added functionality
person.log('name');


간단하고 정확하게! 좋은 예입니다!
nagendra547

1
데코레이터 패턴의 개념이 여기에 적용된다고 생각하지 않습니다. 실제로 그것은 전혀 패턴이 아닙니다!. 예, 런타임에 새로운 메소드를 추가하고 있습니다. 그리고 아마도 a switch또는 simple 내부에서 if이것이 클래스에 동작을 동적으로 추가하는 훌륭한 예라고 주장 할 수 있습니다. 그러나이 패턴으로 데코레이터와 데코 레이팅 된 객체를 정의하려면 적어도 두 개의 클래스가 필요합니다.
Iman

1
@ Zich 내 예제에는 데코레이터가 없다는 것을 알고 있지만 데코레이터 역할을하는 함수를 추가하여 쉽게 고칠 수 있습니다. 그러나 내 예에는 장식 된 물건이 있습니다. 이 패턴은 두 클래스 가 필요하다고 말하는 곳은 어디 입니까?
Anurag

18

Java i / o 모델이 데코레이터 패턴을 기반으로한다는 점은 주목할 가치가 있습니다. 이 독자의 위에 독자의 계층은 ... 실제로 데코레이터의 실제 예입니다.


실제 공개 API에 다른 예가 있습니까? 이것이 내가 아는 유일한 것입니다.
Josiah Yoder

자연의 모든 래퍼 함수에는 일종의 데코레이터 패턴이 내장되어 있다고 생각합니다.
Harvey Lin

좋은 예 !!
nagendra547

8

예-시나리오-암호화 모듈을 작성한다고 가정 해 봅시다. 이 암호화는 DES-데이터 암호화 표준을 사용하여 일반 파일을 암호화 할 수 있습니다. 마찬가지로 시스템에서 AES-고급 암호화 표준으로 암호화 할 수 있습니다. 또한 암호화-First DES와 AES를 조합하여 사용할 수 있습니다. 또는 먼저 AES와 DES를 가질 수 있습니다.

토론-이 상황을 어떻게 충족시킬 것인가? 이러한 조합의 개체 (예 : AES 및 DES)를 총 4 개의 조합으로 계속 만들 수는 없습니다. 따라서 4 개의 개별 개체가 필요합니다. 암호화 유형이 증가함에 따라 복잡해집니다.

솔루션-런타임에 필요에 따라 스택 조합을 유지하십시오. 이 스택 방식의 또 다른 장점은 쉽게 풀 수 있다는 것입니다.

C ++의 해결책은 다음과 같습니다.

먼저, 스택의 기본 단위 인 기본 클래스가 필요합니다. 스택의 기본으로 생각할 수 있습니다. 이 예에서는 파일이 명확합니다. 항상 다형성을 따르자. 우선이 기본 유닛의 인터페이스 클래스를 만드십시오. 이런 식으로 원하는대로 구현할 수 있습니다. 또한이 기본 단위를 포함하면서 종속성을 생각할 필요가 없습니다.

인터페이스 클래스는 다음과 같습니다.

class IclearData
{
public:

    virtual std::string getData() = 0;
    virtual ~IclearData() = 0;
};

IclearData::~IclearData()
{
    std::cout<<"Destructor called of IclearData"<<std::endl;
}

이제이 인터페이스 클래스를 구현하십시오.

class clearData:public IclearData
{
private:

    std::string m_data;

    clearData();

    void setData(std::string data)
        {
            m_data = data;
        }

public:

    std::string getData()
    {
        return m_data;
    }

    clearData(std::string data)
    {
        setData(data);
    }

    ~clearData()
    {
        std::cout<<"Destructor of clear Data Invoked"<<std::endl;
    }

};

이제 데코레이터 추상 클래스를 만들어 보자. 어떤 종류의 풍미를 만들기 위해 확장 할 수있다. 여기서 풍미는 암호화 유형이다. 이 데코레이터 추상 클래스는 기본 클래스와 관련이 있습니다. 따라서 데코레이터는 일종의 인터페이스 클래스입니다. 따라서 상속을 사용해야합니다.

class encryptionDecorator: public IclearData
{

protected:
    IclearData *p_mclearData;

    encryptionDecorator()
    {
      std::cout<<"Encryption Decorator Abstract class called"<<std::endl;
    }

public:

    std::string getData()
    {
        return p_mclearData->getData();
    }

    encryptionDecorator(IclearData *clearData)
    {
        p_mclearData = clearData;
    }

    virtual std::string showDecryptedData() = 0;

    virtual ~encryptionDecorator() = 0;

};

encryptionDecorator::~encryptionDecorator()
{
    std::cout<<"Encryption Decorator Destructor called"<<std::endl;
}

이제 구체적인 데코레이터 클래스를 만들어 봅시다-암호화 유형-AES-

const std::string aesEncrypt = "AES Encrypted ";

class aes: public encryptionDecorator
{

private:

    std::string m_aesData;

    aes();

public:

    aes(IclearData *pClearData): m_aesData(aesEncrypt)
    {
        p_mclearData = pClearData;
        m_aesData.append(p_mclearData->getData());
    }

    std::string getData()
        {
            return m_aesData;
        }

    std::string showDecryptedData(void)
    {
        m_aesData.erase(0,m_aesData.length());
        return m_aesData;
    }

};

이제 데코레이터 유형이 DES라고 가정 해 봅시다.

const std :: string desEncrypt = "DES 암호화";

class des: public encryptionDecorator
{

private:

    std::string m_desData;

    des();

public:

    des(IclearData *pClearData): m_desData(desEncrypt)
    {
        p_mclearData = pClearData;
        m_desData.append(p_mclearData->getData());
    }

    std::string getData(void)
        {
            return m_desData;
        }

    std::string showDecryptedData(void)
    {
        m_desData.erase(0,desEncrypt.length());
        return m_desData;
    }

};

이 데코레이터 클래스를 사용하기 위해 클라이언트 코드를 만들어 봅시다-

int main()
{
    IclearData *pData = new clearData("HELLO_CLEAR_DATA");

    std::cout<<pData->getData()<<std::endl;


    encryptionDecorator *pAesData = new aes(pData);

    std::cout<<pAesData->getData()<<std::endl;

    encryptionDecorator *pDesData = new des(pAesData);

    std::cout<<pDesData->getData()<<std::endl;

    /** unwind the decorator stack ***/
    std::cout<<pDesData->showDecryptedData()<<std::endl;

    delete pDesData;
    delete pAesData;
    delete pData;

    return 0;
}

다음과 같은 결과가 나타납니다.

HELLO_CLEAR_DATA
Encryption Decorator Abstract class called
AES Encrypted HELLO_CLEAR_DATA
Encryption Decorator Abstract class called
DES Encrypted AES Encrypted HELLO_CLEAR_DATA
AES Encrypted HELLO_CLEAR_DATA
Encryption Decorator Destructor called
Destructor called of IclearData
Encryption Decorator Destructor called
Destructor called of IclearData
Destructor of clear Data Invoked
Destructor called of IclearData

다음은 UML 다이어그램-클래스 표현입니다. 코드를 건너 뛰고 디자인 측면에 중점을 두려는 경우가 있습니다.

여기에 이미지 설명을 입력하십시오


1
예가 더 적합하지 strategy pattern않습니까?
exexzian

@exexzian 예, 제 학생들은 이러한 유형의 문제에 대한 전략 목록을 일관되게 제안하고 나에게 가장 깨끗한 해결책 인 것 같습니다.
Josiah Yoder

아니요, 전략 패턴으로는 암호화 방법을 결합 할 수 없습니다. 따라서 가능한 모든 조합에 대해 전략 클래스를 작성해야합니다.
deetz

4

데코레이터 패턴을 사용하면이 객체의 다른 유사한 서브 클래스와 연결하여 객체의 기능을 변경하거나 구성 할 수 있습니다.

가장 좋은 예는 java.io 패키지의 InputStream 및 OutputStream 클래스입니다.

    File file=new File("target","test.txt");
    FileOutputStream fos=new FileOutputStream(file);
    BufferedOutputStream bos=new BufferedOutputStream(fos);
    ObjectOutputStream oos=new ObjectOutputStream(bos);


    oos.write(5);
    oos.writeBoolean(true);
    oos.writeBytes("decorator pattern was here.");


//... then close the streams of course.

이 경우, 호출 체인은 ObjectOutputStream에서 시작하여 File 클래스까지 올라간 다음 File 클래스가 값을 리턴 한 다음 다른 3 개의 서브 클래스가이를 추가하고 마지막으로 ObjectOutputStream의 메소드 값이 리턴합니다. 맞습니까?
Harvey Lin

3

Java에서 데코레이터 디자인 패턴이란 무엇입니까?

GoF 책 (Design Patterns : Elements of Reusable Object-Oriented Software, 1995, Pearson Education, Inc. Pearson Addison Wesley로 출판)의 데코레이터 패턴에 대한 공식적인 정의는 다음과 같이 말합니다.

"객체에 추가 책임을 동적으로 부여합니다. 데코레이터는 기능 확장을 위해 서브 클래 싱에 대한 유연한 대안을 제공합니다."

피자가 있고 Chicken Masala, Onion 및 Mozzarella Cheese와 같은 토핑으로 장식하고 싶다고 가정 해 봅시다. Java로 구현하는 방법을 보자 ...

Java로 Decorator Design Pattern을 구현하는 방법을 보여주는 프로그램입니다.

Pizza.java :

<!-- language-all: lang-html -->

package com.hubberspot.designpattern.structural.decorator;

public class Pizza {

public Pizza() {

}

public String description(){
    return "Pizza";
}

}



package com.hubberspot.designpattern.structural.decorator;

public abstract class PizzaToppings extends Pizza {

public abstract String description();

}

package com.hubberspot.designpattern.structural.decorator;

public class ChickenMasala extends PizzaToppings {

private Pizza pizza;

public ChickenMasala(Pizza pizza) {
    this.pizza = pizza;
}

@Override
public String description() {
    return pizza.description() + " with chicken masala, ";
}

}



package com.hubberspot.designpattern.structural.decorator;

public class MozzarellaCheese extends PizzaToppings {

private Pizza pizza;

public MozzarellaCheese(Pizza pizza) {
    this.pizza = pizza;
}

@Override
public String description() {
    return pizza.description() + "and mozzarella cheese.";
}
}



package com.hubberspot.designpattern.structural.decorator;

public class Onion extends PizzaToppings {

private Pizza pizza;

public Onion(Pizza pizza) {
    this.pizza = pizza;
}

@Override
public String description() {
    return pizza.description() + "onions, ";
}

}



package com.hubberspot.designpattern.structural.decorator;

public class TestDecorator {

public static void main(String[] args) {

    Pizza pizza = new Pizza();

    pizza = new ChickenMasala(pizza);
    pizza = new Onion(pizza);
    pizza = new MozzarellaCheese(pizza);

    System.out.println("You're getting " + pizza.description());

}

}

3

나는 일할 때 데코레이터 패턴을 광범위하게 사용했다. 내가 만든 내 블로그에 게시물을 로깅을 사용하는 방법에 대한.


나는 당신이 답으로 링크를 던진 것을 좋아하지 않습니다. 그러나 귀하의 블로그 기사는 매우 유용하여 방금 공표해야했습니다. :). 이제 정말 이해합니다. 모두 피자와 함께오고, 당신은 완벽한 모범을 보입니다.
Niklas Raab

2

데코레이터 패턴을 사용하면 객체에 동작을 동적으로 추가 할 수 있습니다.

다양한 종류의 버거 가격을 계산하는 앱을 구축해야하는 경우를 예로 들어 보겠습니다. "대형"또는 "치즈 포함"과 같이 버거의 다양한 변형을 처리해야합니다. 각 버거는 기본 버거와 비교하여 가격이 다릅니다. 예를 들어 치즈 버거의 경우 $ 10, 큰 버거의 경우 $ 15를 추가하십시오.

이 경우이를 처리하기 위해 서브 클래스를 작성하려고 할 수 있습니다. 우리는 이것을 Ruby로 다음과 같이 표현할 수 있습니다.

class Burger
  def price
    50
  end
end

class BurgerWithCheese < Burger
  def price
    super + 15
  end
end

위의 예에서 BurgerWithCheese 클래스는 Burger에서 상속하고 price 메소드를 대체하여 수퍼 클래스에 정의 된 가격에 $ 15를 추가합니다. LargeBurger 클래스를 만들고 Burger에 상대적인 가격을 정의 할 수도 있습니다. 그러나 "큰"과 "치즈와"의 조합을위한 새로운 클래스를 정의해야합니다.

"튀김이 든 햄버거"를 제공해야한다면 어떻게됩니까? 우리는 이미 그 조합을 처리하기 위해 4 개의 클래스를 가지고 있으며, "큰", "치즈와"그리고 "튀김"과 같은 3 가지 속성의 모든 조합을 처리하기 위해 4 개를 더 추가해야합니다. 이제 8 개의 수업이 필요합니다. 다른 속성을 추가하면 16이 필요합니다. 이것은 2 ^ n으로 커집니다.

대신 Burger 객체를 사용하는 BurgerDecorator를 정의 해 보겠습니다.

class BurgerDecorator
  def initialize(burger)
    self.burger = burger
  end
end

class BurgerWithCheese < BurgerDecorator
  def price
    self.burger.price + 15
  end
end

burger = Burger.new
cheese_burger = BurgerWithCheese.new(burger)
cheese_burger.price   # => 65

위의 예에서 BurgerWithCheese 클래스가 상속하는 BurgerDecorator 클래스를 작성했습니다. LargeBurger 클래스를 만들어 "대형"변형을 나타낼 수도 있습니다. 이제 런타임에 치즈가 들어간 큰 햄버거를 다음과 같이 정의 할 수 있습니다.

b = LargeBurger.new(cheese_burger)
b.price  # => 50 + 15 + 20 = 85

상속을 사용하여 "튀김 포함"변형을 추가하면 4 개의 서브 클래스 추가가 필요하다는 것을 기억하십니까? 데코레이터를 사용하면 새로운 변형을 처리하고 런타임에이를 처리하기 위해 BurgerWithFries라는 새로운 클래스 하나만 만들 수 있습니다. 각각의 새로운 속성은 모든 순열을 다루기 위해 더 많은 데코레이터가 필요합니다.

추신. 이 글은 루비에서 데코레이터 패턴 사용에 관해 쓴 기사의 짧은 버전으로, 보다 자세한 예제를 찾으려면 읽을 수 있습니다.


2

데코레이터 :

  1. 런타임에 객체에 동작을 추가합니다 . 상속은이 기능을 달성하는 열쇠이며,이 패턴의 장단점입니다.
  2. 인터페이스 의 동작 을 향상시킵니다 .
  3. 데코레이터는 하나의 구성 요소 만 있는 축퇴 합성물 로 볼 수 있습니다 . 그러나 데코레이터는 추가 책임을 추가합니다. 객체 집계를위한 것이 아닙니다.
  4. Decorator 클래스는 LCD (Lowest Class Denominator) 인터페이스에 컴포지션 관계를 선언하고이 데이터 멤버는 생성자에서 초기화됩니다.
  5. Decorator는 서브 클래스없이 객체에 책임을 추가 할 수 있도록 설계되었습니다.

자세한 내용은 소스 제작 기사를 참조하십시오.

Decorator (Abstract) : 추상 클래스 / 인터페이스로, 컴포넌트 인터페이스를 구현합니다. 컴포넌트 인터페이스를 포함합니다. 이 클래스가 없으면 다양한 조합을 위해 ConcreteDecorators의 많은 하위 클래스가 필요합니다. 컴포넌트의 구성은 불필요한 서브 클래스를 줄입니다.

JDK 예 :

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("a.txt")));
while(bis.available()>0)
{
        char c = (char)bis.read();
        System.out.println("Char: "+c);;
}

UML 다이어그램 및 코드 예제에 대한 SE 질문을 살펴보십시오.

IO 데코레이터 패턴

유용한 기사 :

journaldev

위키 백과

데코레이터 패턴의 실제 예 : VendingMachineDecorator 는 @

데코레이터 패턴을 사용하는시기

Beverage beverage = new SugarDecorator(new LemonDecorator(new Tea("Assam Tea")));
beverage.decorateBeverage();

beverage = new SugarDecorator(new LemonDecorator(new Coffee("Cappuccino")));
beverage.decorateBeverage();

위의 예에서 차 또는 커피 (음료)는 설탕과 레몬으로 장식되었습니다.


2

데코레이터 패턴은 객체에 책임을 동적으로 추가 하는 단일 목표를 달성 합니다 .

Java I / O 모델 은 데코레이터 패턴을 기반으로합니다.

데코레이터 패턴으로서의 Java IO



1

얼마 전 Decorator 패턴을 사용하여 코드베이스를 리팩터링 했으므로 유스 케이스를 설명하려고 노력할 것입니다.

서비스 세트가 있다고 가정하고 사용자가 특정 서비스의 라이센스를 획득했는지 여부에 따라 서비스를 시작해야합니다.

모든 서비스에는 공통 인터페이스가 있습니다

interface Service {
  String serviceId();
  void init() throws Exception;
  void start() throws Exception;
  void stop() throws Exception;
}

사전 리팩토링

abstract class ServiceSupport implements Service {
  public ServiceSupport(String serviceId, LicenseManager licenseManager) {
    // assign instance variables
  }

  @Override
  public void init() throws Exception {
    if (!licenseManager.isLicenseValid(serviceId)) {
       throw new Exception("License not valid for service");
    }
    // Service initialization logic
  }
}

주의 깊게 관찰하면 ServiceSupport에 의존합니다 LicenseManager. 그러나 왜 의존해야 LicenseManager합니까? 라이센스 정보를 확인할 필요가없는 백그라운드 서비스가 필요한 경우 어떻게해야합니까? 현재 상황에서 우리는 어떻게 든 백그라운드 서비스 LicenseManager로 돌아 가기 true위해 훈련 해야 합니다. 이 접근법은 나에게 잘 보이지 않았습니다. 저에 따르면 라이센스 확인과 다른 논리는 서로 직교합니다.

따라서 Decorator Pattern 이 구조에 들어오고 여기에서 TDD로 리팩토링을 시작합니다.

포스트 리팩토링

class LicensedService implements Service {
  private Service service;
  public LicensedService(LicenseManager licenseManager, Service service) {
    this.service = service;
  }

  @Override
  public void init() {
    if (!licenseManager.isLicenseValid(service.serviceId())) {
      throw new Exception("License is invalid for service " + service.serviceId());
    }
    // Delegate init to decorated service
    service.init();
  }

  // override other methods according to requirement
}

// Not concerned with licensing any more :)
abstract class ServiceSupport implements Service {
  public ServiceSupport(String serviceId) {
    // assign variables
  }

  @Override
  public void init() {
    // Service initialization logic
  }
}

// The services which need license protection can be decorated with a Licensed service
Service aLicensedService = new LicensedService(new Service1("Service1"), licenseManager);
// Services which don't need license can be created without one and there is no need to pass license related information
Service aBackgroundService = new BackgroundService1("BG-1");

테이크 아웃

  • 코드 응집력이 좋아졌습니다
  • ServiceSupport를 테스트 할 때 라이센스를 조롱 할 필요가 없으므로 단위 테스트가 쉬워졌습니다.
  • 백그라운드 서비스에 대한 특별 검사를 통해 라이센스를 우회 할 필요가 없습니다.
  • 적절한 책임 분담

1

PubG를 예로 들어 보겠습니다. 어썰트 라이플은 4 배 줌에서 가장 잘 작동하며, 그 위에있는 동안 보정기와 억제 장치도 필요합니다. 반동을 줄이고 발사 소리와 에코를 줄입니다. 플레이어가 좋아하는 총과 액세서리를 구입할 수 있도록이 기능을 구현해야합니다. 플레이어는 총이나 일부 액세서리 또는 모든 액세서리를 구입할 수 있으며 그에 따라 청구됩니다.

데코레이터 패턴이 어떻게 적용되는지 봅시다 :

누군가 위에서 언급 한 세 가지 액세서리와 함께 SCAR-L을 구매하려고한다고 가정 해 봅시다.

  1. SCAR-L의 대상을 취하십시오
  2. 4 배 줌 객체로 SCAR-L을 장식 (또는 추가)
  3. 억제 객체로 SCAR-L을 장식하십시오
  4. 압축기 객체로 SCAR-L을 장식
  5. 비용 방법을 호출하고 각 객체 대리자가 액세서리의 비용 방법을 사용하여 비용을 추가하도록합니다.

이것은 다음과 같은 클래스 다이어그램으로 이어질 것입니다.

직장에서 데코레이터 패턴

이제 다음과 같은 클래스를 가질 수 있습니다.

public abstract class Gun {     
    private Double cost;    
    public Double getCost() {           
        return cost;        
       }    
    }

public abstract class GunAccessories extends Gun {  }

public class Scarl extends Gun {    
    public Scarl() {            
        cost = 100;
        }   
     }

public class Suppressor extends GunAccessories {        
    Gun gun;        
    public Suppressor(Gun gun) {            
    cost = 5;           
    this.gun = gun;     
    }               
    public double getCost(){            
        return cost + gun.getCost();
    }
}

public class GunShop{   
    public static void main(String args[]){         
    Gun scarl = new Scarl();                
    scarl = new Supressor(scarl);
    System.out.println("Price is "+scarl.getCost());
    }      
}

마찬가지로 다른 액세서리도 추가하고 총을 장식 할 수 있습니다.

참고:

https://nulpointerexception.com/2019/05/05/a-beginner-guide-to-decorator-pattern/


0

데코레이터 디자인 패턴 :이 패턴은 런타임시 오브젝트의 특성을 수정하는 데 도움이됩니다. 그것은 객체에 다른 풍미를 제공하고 그 풍미에 어떤 성분을 사용하고자 하는지를 유연하게 제공합니다.

실제 사례 : 비행기에 메인 캐빈 좌석이 있다고 가정하겠습니다. 이제 좌석으로 여러 편의 시설을 선택할 수 있습니다. 각 편의 시설에는 관련 비용이 있습니다. 이제 사용자가 Wifi 및 프리미엄 음식을 선택하면 좌석 + wifi + 프리미엄 음식에 대한 요금이 부과됩니다.

여기에 이미지 설명을 입력하십시오

이 경우 데코레이터 디자인 패턴이 실제로 우리를 도울 수 있습니다. 위의 링크를 방문하여 데코레이터 패턴과 실제 예제 구현에 대한 자세한 내용을 알아보십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.