클래스
와 객체
를 합성하는가와 관련된 패턴.재 사용성
을 발휘하지 못할 때가 발생.Shape
라는 클래스에 맞게끔 DrawingEditor
가 지원을 하고 있음.TextView
라는 클래스를 제작했을때, TextShape
는 이 둘의 기능을 모두 지원받고 싶음TextShape
이 DrawingEditor
,TextView
에 대해 지원을 할 수 있게끔 하는 방법이 2가지가 있음TextShape
을 구현Adapter 패턴
에 해당되고 전자를 클래스 버전 후자를 객체 버전이라고 할 수 있음.TextShape
을 적응자라고 할 수 있음.다시말해
이미 만든 것을 재사용하고자 하나 이 재사용 가능한 라이브러리를 수정할 수 없을 때Adaptee
인터페이스를 적응시키는 클래스사용자는 적응자에 해당하는 클래스의 인스턴스에게 연산을 호출하고, 적응자에 해당하는 해당 요청을 수행하기 위해 적응 대상자의 연산을 호출한다.
클래스 적응자와 객체 적응자는 각각 장단점이 있습니다.
먼저 클래스 적응자를 살펴보자면, Adapter 클래스는 Adaptee 클래스를 Target
클래스로 변형하는데, 이를 위해서 Adaptee 클래스를 상속받아야 하기 때문에, 하나의 클래스와 이 클래스의 모든 서브클래스들을 개조할 때라면 클래스 적응자 방식을 사용할 수 없음
즉 Adapter
는 명시적으로 Adaptee
를 상속받고 있을 뿐 Adaptee
의 서브클래스들을 상속받는 것은 아니므로, Adaptee
의 서브클래스에 정의된 기능들을 사용할 수 없음.
Adapter
클래스는 Adaptee
클래스를 상속하기 때문에 Adaptee
에 정의된 행동을 재정의할수도 있음.
한 개의 객체만 사용하며, Adaptee로 가기 위한 추가적인 포인터 간접화는 필요하지 않음
객체 적응자를 사용하면 다음과 같은 특징을 경험 가능함.
Adapter
클래스는 하나만 존재해도 수많은 Adaptee
클래스들과 동작할 수 있습니다. 왜냐하면 Adapter
객체가 포함하는 Adaptee
에 대한 참조자는 Adaptee
인스턴스를 관리할 수도 있고, Adaptee
클래스를 상속받는 다른 서브클래스들의 인터페이스를 관리할 수 있기 때문입니다.Adapter
클래스가 아닌 Adaptee
클래스의 해당 서브클래스를 참조하도록 해야함.Adapter 클래스가 실제 적응 작업을 위해 들어가는 작업량은 얼마나 되는가?
대체가능 적응자
Adapter 패턴의 구현은 매우간단하지만 몇 가지 염두에 두어야할 것이 있음
Adapter 클래스는 Target 클래스에서 public으로 상속받고, Adaptee 는 private로 상속 받음
대체 가능 적응자 : TreeDisplay 문제를 해결할 수 있는 적응자를 구현하는 방법은 총 3개가 됨.
먼저 Adaptee
에 정의된 인터페이스들 중 적응이 필요한 연산의 최소 집합을 만듬.
이렇게 인터페이스를 축소하는 이유는 수십 개 연산을 갖는 인터페이스를 적응시키기 보다는 한 두개 인터페이스만을 적응시키는 것이 더 쉽기 때문.
TreeDisplay를 위한 피적응자는 어떤 계층 구조도 될 수 있음.
abstract class Shape{
public Shape(){}
abstract public void boundingBox(Point bottomLeft, Point topRight);
abstract public Manipulator createManipulator();
}
abstract class TextView{
public TextView(){}
//시작점이 되는 x,y 좌표를 얻음
void getOrigin(Coord x, Coord y){
// 구현되어있겠지
}
//넓이와 길이를 가지는 상자를 구현할 수 있음.
void getExtent(Coord witdh, Coord height){
//뭐 구현되어있을꺼라 생각되는 부분.
}
abstract boolean isEmpty();
}
로 정의된 클래스가 있다고 침.
이를 TextView 클래스가 Shape가 원하는 형태의 연산 이름으로 서비스를 제공하고자 한다면 TextShape라는 클래스를 설계해서 해결할 수 있음.
public interface Shape{
public void boundingBox(Point bottomLeft, Point topRight);
public void Manipulator createManipulator();
}
abstract class TextView{
public TextView(){}
//시작점이 되는 x,y 좌표를 얻음
protected void getOrigin(Coord x, Coord y){
// 구현되어있겠지
}
//넓이와 길이를 가지는 상자를 구현할 수 있음.
protected void getExtent(Coord witdh, Coord height){
//뭐 구현되어있을꺼라 생각되는 부분.
}
abstract public boolean isEmpty();
}
class TextShape extends TextView implements Shape{
public TextShape(){};
@Override
public void boundingBox(Point bottomLeft, Point topRight){
Coordi bottom, left, width, height;
getOrigin(bottom, left);
getExtent(width, height);
bottomLeft = Point(bottom, left);
topRight = Point(bottom + height, left + width);
}
@Override
public boolean isEmpty(){
return super.isEmpty();
}
@Override
public Manipulator createManipulator{
return new TextManipulator(this);
}
}
TextShape
에서 재정의해서 DrawingEditor
에서 사용할 수 있는 상태가 됐다.class TextShape implements Shape{
private TextView textView;
public TextShaple(){
super();
}
@Override
public void boundingBox(Point bottomLeft, Point topRight){
Coord bottom, left, width, height;
textView.getOrigin(bottom,left);
textView.getExtent(width, height);
bottomRight = Point(bottom,left);
topRight = Point(bottom +height, left + width);
}
@Override
public boolean isEmpty(){
return textView.isEmpty();
}
@Override
public Manipulator createManipulator{
return new TextManipulator(this);
}
}
구현에서 추상(정의)를 분리하여 이들이 독립적으로 다양성을 가질 수 있도록 함.
인터페이스에 구현 분리
구현이 인터페이스에 얽매이지 않게 됩니다. 추상적 개념에 대한 어떤 방식의 구현을 택할지가 런타임에 결정될 수 있음
이는 런타임에 어떤 객체가 자신의 구현을 수시로 변경할 수 있음.
Abstraction
과 Implementor
의 분리는 컴타일 타임 의존성을 제거할 수 있음.
구현을 변경하더라도 추상적 개념에 대한 클래스를 다시 컴파일할 필요가 없음.
확장성 제고
Abstraction과 Implementor를 독립적으로 확장할 수 있음.
구현 세부 사항을 사용자에게서 숨기기
상세한 구현 내용을 사용자에게 은닉할 수 있음.
public interface Speaker{
public void turnOn();
public void turnDown();
public void volumeUp();
public void volumeDown();
}
public class SamsungSpeaker implements Speaker{
@Override
public void turnOn(){
System.out.println("삼성 Speaker를 켭니다.");
}
@Override
public void turnDown(){
System.out.println("삼성 Speaker를 끕니다.");
}
@Override
public void volumeUp(){
System.out.println("volume을 키자");
}
@Override
public void volumeDown(){
System.out.println("volumne을 다운하자");
}
}
public class LGSpeaker implements Speaker{
@Override
public void turnOn(){
System.out.println("LG Speaker를 켭니다.");
}
@Override
public void turnDown(){
System.out.println("LG Speaker를 끕니다.");
}
@Override
public void volumeUp(){
System.out.println("volume을 키자");
}
@Override
public void volumeDown(){
System.out.println("volumne을 다운하자");
}
}
부분과 전체의 계층을 표현하기 위해 객체들을 모아 트리 구조로 구성, 사용자로 사여금 개별 객체와 복합 객체를 모두 동일하게 다룰수 있도록 하는 패턴
Component
인터페이스를 통해 복합 구조내의 객체들을 조작플라이급 패턴
에 대해서 알 수 있음Composite
클래스가 보통 그 삭제의 책임을 집니다.public abstract class ComputerDevice {
public abstract int getPrice();
public abstract int getPower();
}
public class Keyboard extends ComputerDevice {
private int price;
private int power;
public Keyboard(int power, int price) {
this.power = power;
this.price = price;
}
public int getPrice() { return price; }
public int getPower() { return power; }
}
public class Body { 동일한 구조 }
public class Monitor { 동일한 구조 }
}
public class Computer extends ComputerDevice {
// 복수 개의 ComputerDevice 객체를 가리킴
private List<ComputerDevice> components = new ArrayList<ComputerDevice>();
// ComputerDevice 객체를 Computer 클래스에 추가
public addComponent(ComputerDevice component) { components.add(component); }
// ComputerDevice 객체를 Computer 클래스에서 제거
public removeComponent(ComputerDevice component) { components.remove(component); }
// 전체 가격을 포함하는 각 부품의 가격을 합산
public int getPrice() {
int price = 0;
for(ComputerDevice component : components) {
price += component.getPrice();
}
return price;
}
// 전체 소비 전력량을 포함하는 각 부품의 소비 전력량을 합산
public int getPower() {
int power = 0;
for(ComputerDevice component : components) {
price += component.getPower();
}
return power;
}
}
public class Client {
public static void main(String[] args) {
// 컴퓨터의 부품으로 Keyboard, Body, Monitor 객체를 생성
Keyboard keyboard = new Keyboard(5, 2);
Body body = new Body(100, 70);
Monitor monitor = new Monitor(20, 30);
// Computer 객체를 생성하고 addComponent()를 통해 부품 객체들을 설정
Computer computer = new Computer();
computer.addComponent(keyboard);
computer.addComponent(body);
computer.addComponent(monitor);
// 컴퓨터의 가격과 전력 소비량을 구함
int computerPrice = computer.getPrice();
int computerPower = computer.getPower();
System.out.println("Computer Price: " + computerPrice + "만원");
System.out.println("Computer Power: " + computerPower + "W");
}
}
https://gmlwjd9405.github.io/2018/08/10/composite-pattern.html
스프링 환경에서 GoF 패턴을 활용하는 방법도 소개해주실 수 있나요? :0