나는 GOF에 문서화 된 데코레이터 패턴 을 연구하고있었습니다 .
데코레이터 패턴을 이해하도록 도와주세요 . 누군가가 이것이 실제 세계에서 유용한 곳의 사용 사례를 제시 할 수 있습니까?
나는 GOF에 문서화 된 데코레이터 패턴 을 연구하고있었습니다 .
데코레이터 패턴을 이해하도록 도와주세요 . 누군가가 이것이 실제 세계에서 유용한 곳의 사용 사례를 제시 할 수 있습니까?
답변:
데코레이터 패턴은 객체에 책임을 동적으로 추가하는 단일 목표를 달성합니다.
피자 가게의 경우를 생각해보십시오. 피자 가게에서는 피자 종류를 거의 판매하지 않으며 메뉴에 토핑을 제공합니다. 이제 피자 가게가 피자와 토핑의 각 조합에 대해 가격을 제공해야하는 상황을 상상해보십시오. 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;
}
}
기존 객체에 동적으로 새 동작을 추가하거나 데코레이터 패턴을 추가하는 간단한 예입니다. 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');
switch
또는 simple 내부에서 if
이것이 클래스에 동작을 동적으로 추가하는 훌륭한 예라고 주장 할 수 있습니다. 그러나이 패턴으로 데코레이터와 데코 레이팅 된 객체를 정의하려면 적어도 두 개의 클래스가 필요합니다.
Java i / o 모델이 데코레이터 패턴을 기반으로한다는 점은 주목할 가치가 있습니다. 이 독자의 위에 독자의 계층은 ... 실제로 데코레이터의 실제 예입니다.
예-시나리오-암호화 모듈을 작성한다고 가정 해 봅시다. 이 암호화는 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 다이어그램-클래스 표현입니다. 코드를 건너 뛰고 디자인 측면에 중점을 두려는 경우가 있습니다.
strategy pattern
않습니까?
데코레이터 패턴을 사용하면이 객체의 다른 유사한 서브 클래스와 연결하여 객체의 기능을 변경하거나 구성 할 수 있습니다.
가장 좋은 예는 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.
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());
}
}
나는 일할 때 데코레이터 패턴을 광범위하게 사용했다. 내가 만든 내 블로그에 게시물을 로깅을 사용하는 방법에 대한.
데코레이터 패턴을 사용하면 객체에 동작을 동적으로 추가 할 수 있습니다.
다양한 종류의 버거 가격을 계산하는 앱을 구축해야하는 경우를 예로 들어 보겠습니다. "대형"또는 "치즈 포함"과 같이 버거의 다양한 변형을 처리해야합니다. 각 버거는 기본 버거와 비교하여 가격이 다릅니다. 예를 들어 치즈 버거의 경우 $ 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라는 새로운 클래스 하나만 만들 수 있습니다. 각각의 새로운 속성은 모든 순열을 다루기 위해 더 많은 데코레이터가 필요합니다.
추신. 이 글은 루비에서 데코레이터 패턴 사용에 관해 쓴 기사의 짧은 버전으로, 보다 자세한 예제를 찾으려면 읽을 수 있습니다.
데코레이터 :
자세한 내용은 소스 제작 기사를 참조하십시오.
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 질문을 살펴보십시오.
유용한 기사 :
데코레이터 패턴의 실제 예 : VendingMachineDecorator 는 @
Beverage beverage = new SugarDecorator(new LemonDecorator(new Tea("Assam Tea")));
beverage.decorateBeverage();
beverage = new SugarDecorator(new LemonDecorator(new Coffee("Cappuccino")));
beverage.decorateBeverage();
위의 예에서 차 또는 커피 (음료)는 설탕과 레몬으로 장식되었습니다.
스크롤바로 창을 꾸미는 것에 관한 Wikipedia의 예가 있습니다 :
http://en.wikipedia.org/wiki/Decorator_pattern
다음은 "팀원, 팀장 및 관리자"의 매우 실제적인 예입니다. 데코레이터 패턴이 단순한 상속으로 대체 될 수 없음을 보여줍니다.
https://zishanbilal.wordpress.com/2011/04/28/design-patterns-by-examples-decorator-pattern/
얼마 전 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");
PubG를 예로 들어 보겠습니다. 어썰트 라이플은 4 배 줌에서 가장 잘 작동하며, 그 위에있는 동안 보정기와 억제 장치도 필요합니다. 반동을 줄이고 발사 소리와 에코를 줄입니다. 플레이어가 좋아하는 총과 액세서리를 구입할 수 있도록이 기능을 구현해야합니다. 플레이어는 총이나 일부 액세서리 또는 모든 액세서리를 구입할 수 있으며 그에 따라 청구됩니다.
데코레이터 패턴이 어떻게 적용되는지 봅시다 :
누군가 위에서 언급 한 세 가지 액세서리와 함께 SCAR-L을 구매하려고한다고 가정 해 봅시다.
이것은 다음과 같은 클래스 다이어그램으로 이어질 것입니다.
이제 다음과 같은 클래스를 가질 수 있습니다.
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/
데코레이터 디자인 패턴 :이 패턴은 런타임시 오브젝트의 특성을 수정하는 데 도움이됩니다. 그것은 객체에 다른 풍미를 제공하고 그 풍미에 어떤 성분을 사용하고자 하는지를 유연하게 제공합니다.
실제 사례 : 비행기에 메인 캐빈 좌석이 있다고 가정하겠습니다. 이제 좌석으로 여러 편의 시설을 선택할 수 있습니다. 각 편의 시설에는 관련 비용이 있습니다. 이제 사용자가 Wifi 및 프리미엄 음식을 선택하면 좌석 + wifi + 프리미엄 음식에 대한 요금이 부과됩니다.
이 경우 데코레이터 디자인 패턴이 실제로 우리를 도울 수 있습니다. 위의 링크를 방문하여 데코레이터 패턴과 실제 예제 구현에 대한 자세한 내용을 알아보십시오.