초보자를 위한 Java 디자인 패턴에 대한 참고 사항 - 데코레이터 패턴

데코레이터 모드는 래퍼 모드라고도 합니다.

 

1. 목적은
객체에 몇 가지 추가 책임을 동적으로 추가하는 것입니다.

 

2. 참여자
• 추상 컴포넌트(Component): 이러한 객체에 책임을 동적으로 추가할 수 있는 객체 인터페이스를 정의합니다.
• 구체적인 구성요소: 객체를 정의하고 이 객체에 일부 책임을 추가합니다.
• 추상 데코레이터(Decorator): 구성 요소 개체 의 인스턴스를 보유 하고 추상 구성 요소 인터페이스와 일치하는 인터페이스를 정의합니다 .

• 콘크리트 데코레이터: 구성요소에 책임을 추가합니다.

 

3. 구조


 


Yan Hong의 디자인 패턴에는 "장식 패턴은 클라이언트에게 투명한 방식으로 객체의 기능을 확장하고 상속 관계에 대한 대안"이라고  언급되어 있습니다 .

 

먼저 데코레이션 모드의 목적 , 즉 객체에 몇 가지 추가 책임을 동적 으로 추가하는 방법 을 이해해 보겠습니다 . 

소위 동적이란 코드를 수정하거나 재컴파일할 필요 없이 시스템 런타임(런타임) 동안 객체에 다른 책임을 동적으로 추가할 수 있음을 의미합니다.

소위 정적이라는 것은 객체에 책임을 추가하기 위해 코드(DesignTime)를 조정해야 하고 시스템도 다시 컴파일해야 함을 의미합니다.

특정 기술 수준에서 개체의 결합 및 상속은 이전의 동적 및 정적 에 해당합니다.

시스템의 런타임 확장성을 유지하려면 객체 구성을 더 많이 사용해야 하고, 상속은 프로그램을 경직되게 만들기 때문에 상속을 가능한 한 적게 사용해야 합니다!

이것은 상속보다 구성을 선호하는 것입니다.

 

 

"데코레이션 모드는 클라이언트에게 투명한 방식으로 개체의 기능을 확장 하고 상속 관계의 대안입니다."라는 문장을 이해해 보겠습니다. 

Decorator는 Decorator 패턴에서 매우 특별한 클래스로 Component로부터 상속받을 뿐만 아니라 (IS A 관계) Component 인스턴스에 대한 참조를 유지합니다(HAS A 관계). 또 다른 관점에서 보면 Decorator와 Component 사이에는 둘 다 동적 결합입니다. 관계에는 정적 상속 관계도 있습니다.

왜 이렇게 설계되었나요?

구성의 장점은 런타임에 객체에 책임을 추가할 수 있다는 것입니다. 데코레이터에는 구체적인 데코레이터(Concrete Decorator)가 런타임에 구체적인 구성 요소(Concrete Component) 에 책임을 동적으로 추가할 수 있도록 설계된 ( HAS A ) 구성 요소가 있습니다 . 비교적 이해하기 쉽습니다. 

그렇다면 Component에서 상속받은 Decorator의 목적은 무엇일까요?

여기서 상속의 목적은 오직 하나, 즉 데코레이터와 데코레이팅된 컴포넌트의 인터페이스를 통합하는 것뿐이고 , 다른 관점에서 보면 콘크리트 컴포넌트(Concrete Component)콘크리트 데코레이터(Concrete Decorator) 든 둘 다 컴포넌트입니다. 사용자 코드는 이들을 컴포넌트로 취급할 수 있습니다. 이것의 추가 이점은 데코레이팅된 객체 에 대한 데코레이터 객체 의 기능적 책임 확장이 사용자 코드에 완전히 투명하다는 것입니다 . 왜냐하면 사용자 코드는 모든 컴포넌트를 참조하기 때문입니다. 데코레이팅된 객체를 참조하는 사용자 코드에는 오류가 없으며 데코레이션 전후에 사용자 코드가 Component 유형의 객체를 참조하므로 실제로 영향이 없습니다.

 

데코레이터 패턴은 상속을 통해 데코레이터와 데코레이팅된 객체의 인터페이스를 통합 하고 구성을 통해 런타임에 데코레이팅된 객체를 동적으로 확장하는 기능을 얻습니다 .

 

 

예를 들어 설명해 보겠습니다.

먼저 장식 모드를 사용하는 대신 다음 요구 사항을 충족하는 방법을 생각해 보세요.

1. 자동차 구매를 예로 들어보겠습니다. 고객이 자동차 판매점에 가서 자동차를 구매하면 에이전트가 몇 가지 서비스를 제공합니다. 초기에는 일부 애프터 서비스 유지 관리 서비스를 제공합니다. 

2. 이후에는 부품 공급 서비스도 제공하고, 컨설팅 서비스(고객이 자동차를 구입하기 전 문의하는 사항에 대해 상담원이 답변)를 제공했으며, 마지막으로 고객에게 자동차 보험 서비스도 제공했습니다.  

3. 대리인이 상황에 따라 다양한 서비스 조합을 제공해야 하는 경우.

 

(여기서는 이를 구현하기 위한 코드를 작성하지 않겠습니다)

 

대부분의 경우 상속이 사용되며 이로 인해 주로 다음과 같은 문제가 발생합니다.

1. 시스템의 확장성이 좋지 않습니다.

2. 다양한 서비스 조합을 제공해야 할 경우 많은 수의 클래스를 생성해야 하므로 클래스 폭발이 발생합니다.

 

그럼 장식 모드가 위의 요구 사항을 어떻게 충족하는지 살펴보겠습니다. 자바 코드는 다음과 같습니다.

 추상 구성요소: 자동차 딜러 

/**
 * 抽象构件(Component)
 * 
 * 汽车销售商
 */
public interface CarSeller {
	
	/*
	 * 销售汽车
	 */
	String sellCars();

}

 

콘크리트 부품: 아우디 자동차 딜러 

/**
 * 具体构件(Concrete Component)
 * 
 * 奥迪汽车经销商
 */
public class AudiCarAgency implements CarSeller {

	/*
	 * 销售奥迪汽车
	 */
	public String sellCars() {

		System.out.println("销售奥迪汽车");
		
		return "奥迪汽车";
	}

}

 

데코레이터: 서비스를 제공하는 아우디 딜러 

/**
 * 抽象装饰者(Decorator)
 * 
 * 提供服务的奥迪销售商
 */
public class AudiCarAgencyWithServices implements CarSeller {

	private CarSeller carSeller = null;

	/*
	 * 构造函数
	 */
	public AudiCarAgencyWithServices(CarSeller carSeller) {
		this.carSeller = carSeller;
	}

	/*
	 * 装饰
	 * @see myDecoratorPattern.sellCars.CarSeller#sellCars()
	 */
	public String sellCars() {
		String carName = null;
		carName = carSeller.sellCars();
		return carName;
	}

}

 

콘크리트 장식업체: 수리 서비스를 제공하는 아우디 딜러 

/**
 * 具体装饰者(Concrete Decorator)
 * 
 * 提供维修服务的奥迪销售商
 *
 */
public class AudiCarAgencyWithMaintenance extends AudiCarAgencyWithServices {
	
	/*
	 * 构造函数
	 */
	public AudiCarAgencyWithMaintenance(CarSeller carSeller) {
		super(carSeller);
	}
	
	/*
	 * 添加了其他的服务
	 */
	public String sellCars(){		
		String carName = super.sellCars();
		System.out.println("提供维修服务");
		return carName;
	}

}

 

콘크리트 장식업체: 예비 부품 공급을 제공하는 아우디 딜러 

/**
 * 具体装饰者(Concrete Decorator)
 * 
 * 提供零配件供应的奥迪销售商
 *
 */
public class AudiCarAgencyWithSparepart extends AudiCarAgencyWithServices {

	/*
	 * 构造函数
	 */
	public AudiCarAgencyWithSparepart(CarSeller carSeller) {
		super(carSeller);
	}
	
	/*
	 * 添加了其他的服务
	 */
	public String sellCars(){
		String carName = super.sellCars();
		System.out.println("提供零配件供应");
		return carName;
	}

}

 

콘크리트 데코레이터: 사전 판매 컨설팅 서비스를 제공하는 아우디 딜러  

/**
 * 具体装饰者(Concrete Decorator)
 * 
 * 提供售前咨询服务的奥迪销售商
 *
 */
public  class AudiCarAgencyWithConsultation extends AudiCarAgencyWithServices {
	
	/*
	 * 构造函数
	 */
	public AudiCarAgencyWithConsultation(CarSeller carSeller){
		super(carSeller);
	}
	
	/*
	 * 添加了其他的服务
	 */
	public String sellCars() {
		System.out.println("提供了售前咨询服务");
		return super.sellCars();
	}

}

 

콘크리트 장식가: 자동차 보험 서비스를 제공하는 아우디 딜러

 

/**
 * 具体装饰者(Concrete Decorator)
 * 
 * 提供车险服务的奥迪销售商
 *
 */
public class AudiCarAgencyWithInsurance extends AudiCarAgencyWithServices {

	/*
	 * 构造函数
	 */
	public AudiCarAgencyWithInsurance(CarSeller carSeller) {
		super(carSeller);
	}
	
	/*
	 * 添加了其他的服务
	 */
	public String sellCars(){
		
		String carName = super.sellCars();
		System.out.println("提供车险的购买");
		return carName;
	}

}

 

구조는 다음과 같습니다.

 

 

 

고객 전화: 

public class Customer {
	
	public static void main(String[] args){
		
		String car = null;
		
		System.out.println("--------------只卖车,没有服务------------------");	
//		汽车经销商
		CarSeller carSeller = new AudiCarAgency();
		car = carSeller.sellCars();
		System.out.println("买到了" + car);
		
		System.out.println("--------------第一种组合-维修服务------------------");		
//		提供了维修服务
		CarSeller carSellerWithMaintenance = new AudiCarAgencyWithMaintenance(carSeller);
		car = carSellerWithMaintenance.sellCars();
		System.out.println("买到了" + car);
		
		System.out.println("--------------第二种组合-添加了零部件供应服务---------");		
//		提供了零部件供应		
		CarSeller carSellerWithSparepart = new AudiCarAgencyWithSparepart(carSellerWithMaintenance);
		car = carSellerWithSparepart.sellCars();
		System.out.println("买到了" + car);

		System.out.println("--------------第三种组合-添加了咨询服务--------------");		
//		提供了咨询服务		
		CarSeller carSellerWithConsultation = new AudiCarAgencyWithConsultation(carSellerWithSparepart);
		car = carSellerWithConsultation.sellCars();	
		System.out.println("买到了" + car);

		System.out.println("--------------第四种组合-添加了车险服务--------------");		
//		提供了车险	
		CarSeller carSellerWithInsurance = new AudiCarAgencyWithInsurance(carSellerWithConsultation);
		car = carSellerWithInsurance.sellCars();
		System.out.println("买到了" + car);
		
		
		System.out.println("#########################################");	
//		还可以有其他的组合方式
		System.out.println("还可以尝试其他形式自由的组合。。。。。。");
		
	}

}

 

 

작업 결과:

--------------只卖车,没有服务------------------
销售奥迪汽车
买到了奥迪汽车
--------------第一种组合-维修服务------------------
销售奥迪汽车
提供维修服务
买到了奥迪汽车
--------------第二种组合-添加了零部件供应服务---------
销售奥迪汽车
提供维修服务
提供零配件供应
买到了奥迪汽车
--------------第三种组合-添加了咨询服务--------------
提供了售前咨询服务
销售奥迪汽车
提供维修服务
提供零配件供应
买到了奥迪汽车
--------------第四种组合-添加了车险服务--------------
提供了售前咨询服务
销售奥迪汽车
提供维修服务
提供零配件供应
提供车险的购买
买到了奥迪汽车
#########################################
还可以尝试其他形式自由的组合。。。。。。

 

데코레이션 모드를 사용하면 다음과 같은 이점이 있음을 알 수 있습니다. 

1. 데코레이션 모드와 상속 관계의 목적은 객체의 기능을 확장하는 것이지만 데코레이션 모드는 상속보다 더 많은 유연성을 제공할 수 있습니다.

2. 다양한 특정 데코레이션 클래스와 이러한 데코레이션 클래스의 순열 및 조합을 사용함으로써 디자이너는 매우 적은 클래스를 사용하여 다양한 동작 조합을 만들 수 있습니다.

 

위 내용은 Yan Hong의 "Java and Patterns" 및 커피 한잔 - 데코레이터 패턴(Decorator) 예제 응용 프로그램 세부 사항 에서 일부 참조되었습니다.

 

데코레이션 모드의 호출을 주의 깊게 관찰하면(예: 두 번째 조합 - 부품 공급 서비스 추가 ) 데코레이션 모드프록시 모드가 동일한 효과를 갖는 것처럼 느껴집니다 (둘 다 객체에 추가 기능을 추가함). , 비교해 보면 이 두 모드의 구조에는 차이가 없는 것 같습니다. 그렇다면 그들 사이의 차이점은 무엇입니까?

 

첫째, 그들의 의도는 다릅니다.

프록시 패턴은 객체에 대한 액세스를 제어합니다 .

데코레이션 모드는 클래스에 책임(기능)을 추가하는 것 입니다.

 

구조만으로 판단해보면,

프록시 모드를 사용할 때 객체에 액세스하기 전이나 후에 몇 가지 추가 작업을 추가할 수도 있습니다. 이런 점에서 데코레이션 모드는 프록시 모드와 유사합니다. 두 모드가 동일하다고 말할 수도 있습니다. 클래스(객체) 기타 기능.

그러나 "통제"와 "증가"라는 두 단어를 주의 깊게 이해한다면 다음과 같은 사실을 발견할 수 있습니다.

프록시 모드에서는 액세스된 개체에 액세스할 수 있는지 여부를 확인하기 위해 추가 작업이 추가됩니다.

장식 모드에서는 이러한 추가 기능이 원본 개체의 작동에 영향을 주지 않으며 장식된 개체에 확실히 액세스할 수 있습니다.

 

추천

출처blog.csdn.net/louis_lee7812/article/details/83807894