[JAVA] OOP 디자인 패턴(3)

INHEES·2023년 8월 21일
0

Design Pattern

목록 보기
3/3

JAVA 객체지향 디자인 패턴 : Decorator, Factory-method, Mediator, Composite

Decorator 패턴

객체의 결합을 통해 기능을 동적으로 유연하게 확장 하게 해주는 패턴

  • 기본 기능에 추가할 수 있는 기능의 종류가 많은 경우 각 추가 기능을 Decorator 클래스로 정의한 후 Decorator 객체를 조합함으로 써 별도의 클래스 선언 없이 추가 기능의 조합을 설계하는 방식이다.
  • 객체가 생성자 변수로 다른 객체 안에 들어감으로 써 그 실행되는 메서드의 행동이 추가 되도록 한다.
  • Component
    • 기본기능을 뜻하는 ConcreteComponent, 추가 기능을 뜻하는 Decorator 공통 기능을 정의
    • Client 는 Component 를 통해 실제 객체를 사용
  • ConcreteDecoratorA, ConcreteDecoratorB
    • Decorator 하위 클래스로 기본 기능에 추가되는 개별적인 기능을 뜻함
    • ConcreteDecorator 클래스는 ConcreteConponent 객체에 대한 참조가 필요하며, Decorator 클래스에서 Component 클래스로의 "합성" 을통해 표현된다.

Decorator 패턴 구현 예시

public class DecoratorPattern {
  public static void main(String[] args) {

    new XWingFighter().attack();
    // 탄환 발사

    new LaserDecorator(new XWingFighter()).attack();
    // 탄환 발사
    // 레이저 발사

    new PlasmaDecorator(
      new MissileDecorator(
        new LaserDecorator(
          new XWingFighter()
          ))).attack();
    // 탄환 발사
    // 레이저 발사
    // 미사일 발사
    // 플라즈마 발사
  }
}
///////////////////////////////////////////////////////////
public interface Fighter {
  public void attack ();
}

public class XWingFighter implements Fighter {
  @Override
  public void attack () {
    System.out.println("탄환 발사");
  }
}
///////////////////////////////////////////////////////////
public abstract class FighterDecorator implements Fighter {

  private Fighter decoratedFighter;
  public FighterDecorator(Fighter _decoratedFighter) {
    decoratedFighter = _decoratedFighter;
  }

  @Override
  public void attack () {
    decoratedFighter.attack();
  }
}

public class LaserDecorator extends FighterDecorator {
  public LaserDecorator (Fighter _decoratedFighter) {
    super(_decoratedFighter);
  }
  @Override
  public void attack () {
    super.attack();
    System.out.println("레이저 발사");
  }
}

코드를 살펴보면 추상클래스로 선언된 FighterDecorator 는 LaserDecorator 가 상속하는 형태이다. 때문에 attack() 함수는 오버라이딩 되어있다.

DecoratorPattern 클래스에서 new LaserDecorator(new XWingFighter()).attack(); 이부분은 생성자 부분에 XwingFighter 넣고 attack() 함수를 실행하게 되면 XwingFighter 부분의 attack() 함수 실행후 LaserDecorator 부분의 attack() 함수가 실행되는 것을 볼 수 있다.


Factory Method

팩토리 메소드 패턴은 객체를 생성할 때 어떤 클래스의 인스턴스를 만들지 서브 클래스에서 결정하게 하는 것이다.

  • 즉 부모 추상 클래스는 인터페이스에만 의존하고 서브 클래스를 호출한다.
  • 사용할 객체의 조건들만 인자로 넘겨주면 이에 적절한 클래스를 찾아 객체로 생성해 넘겨주는 일은 팩토리 클래스에 위임한다.

Factory Method 구현 예시

abstract class Component {
  protected abstract String getCompName ();
  public Component () {
    System.out.println(this.getCompName() + " 생성");
  }
}

class Button extends Component {
  @Override
  protected String getCompName() { return "버튼"; }
}
class Switch extends Component {
  @Override
  protected String getCompName() { return "스위치"; }
}
class Dropdown extends Component {
  @Override
  protected String getCompName() { return "드랍다운"; }
}
////////////////////////////////////////////////////
class Console {

  private CompFactory compFactory = new CompFactory();

  Component comp1;
  Component comp2;
  Component comp3;

  void withFactory () {
    comp1 = compFactory.getComp(Usage.PRESS);
    comp2 = compFactory.getComp(Usage.TOGGLE);
    comp3 = compFactory.getComp(Usage.EXPAND);
  }
}

enum Usage {
  PRESS, TOGGLE, EXPAND
}

코드를 살펴보면 withFactory() 함수가 실행되면 인자로 생성될 객체의 조건을 넣어주며 Compfactyory 클래스에서 객체를 생성해 반환해 주는 것을 볼 수 있다.

Component 추상클래스로 정의되어 자식클래스들은 getCompname() 을 오버라이드 하고 있다.


Mediator 패턴

모든 클래스 간의 복잡한 로직을 캡슐화하여 하나의 클래스에 위임하여 처리하는 패턴이다.

  • 한 클래스에서의 이벤트가 연결된 다른 클래스의 객체에 영향을 미칠 때 Mediator 패턴으로 설계가 가능하다.

즉, M:N 관계에서 M:1 관계로 복잡도를 떨어뜨려 유지 보수 및 재사용의 확장성에 유리한 패턴이다.

중재자 패턴 클래스 다이어그램

Mediator 패턴 구현 예시

public class ModeMediator {
  ArrayList<ModeListener> listeners = new ArrayList<>();

  public void addListener(ModeListener listener) {
    listeners.add(listener);
  }
  public void onModeChange (Mode mode) {
    for (ModeListener listener : listeners) {
      listener.onModeChange(mode);
    }
  }
}
public class ModeSwitch {
  Mode mode = Mode.LIST;  

  ModeMediator modeMediator;

  public void setModeMediator (ModeMediator _modeMediator) {
    modeMediator = _modeMediator;
  }

  public void toggleMode () {
    mode = mode == Mode.LIST ? Mode.GALLERY : Mode.LIST;

    if (modeMediator != null) {
      modeMediator.onModeChange(mode);
    }
  }
}

enum Mode { LIST, GALLERY }
public interface ModeListener {
  public void onModeChange (Mode mode);
}

class ListView implements ModeListener {
  @Override
  public void onModeChange(Mode mode) {
    System.out.println(
      "리스트뷰 " + (mode == Mode.LIST ? "보여줌" : "감춤")
      );
  }
}

class GalleryView implements ModeListener {
  @Override
  public void onModeChange(Mode mode) {
    System.out.println(
      "갤러리뷰 " + (mode == Mode.GALLERY ? "보여줌" : "감춤")
      );
  }
}

class DataDownloader implements ModeListener {
  @Override
  public void onModeChange(Mode mode) {
    System.out.println(
      (mode == Mode.LIST ? "리스트" : "갤러리")
    + "뷰용 데이터 다운로드");
  }
}

코드를 살펴보면 ListView, GalleryView, DataDownloader 클래스들을 ModeListener 인터페이스를 상속한다.

ModeMediator 클래스에서는 onModeChange() 함수를 통해서 ModeListener 인터페이스를 상속한 하위 클래스들을 MediatorPattern 클래스의 addListener(new ListView()) 함수를 통해 기능을 호출하고 있다.

ModeSwitch 클래스는 ModeMediator setModeMediator 함수를 통해 객체를 넣어준다.


Composite 패턴

Composite 패턴이란 특정 클래스의 객체들을 트리 관계로 다루고 포함하는 객체와 포함되는 객체를 같은 인터페이스로 다루는 패턴이다.

, 다음과 같이 정의된다. 복합 객체와 단일 객체의 처리방법이 다르지 않을경우

  • 전체-부분 관계인 대표적인 Directroy-File 관계에 쓰인다.
  • 리스트 형태로 저장된 객체들은 Tree 형태로 내부가지를 탐색 및 삭제를 진행한다.

코드의 구현예시는 설명으로 충분하기에 생략한다.

profile
이유를 찾아보자

0개의 댓글