컴퓨터과학/0 + 소프트웨어 아키텍처(디자인 패턴)

[소프트웨어 아키텍처] 15. 어댑터 패턴, 퍼사드 패턴(Adapter Pattern, Facade Pattern -java)

힘들면힘을내는쿼카 2022. 12. 8. 21:44
728x90
반응형

어댑터 패턴(Adapter Pattern)

데코레이터 패턴이 기억나시나요? 객체를 래퍼로 감싸서 새로운 역할을 부여했습니다.

StarBeverage beverage = new Americano();
beverage = new Ice(beverage); //이런식으로 말이죠!

 

어댑터 패턴은 조금 다릅니다.!
이번에는 실제와 다른 인터페이스를 가진 것처럼 보이도록 객체를 감싸겠습니다.
어댑터 패턴을 사용하면 특정 인터페이스가 필요한 디자인을 다른 인터페이스를 구현하는 클래스에 적응시킬 수 있습니다.

어댑터 패턴은 특정 클래스 인터페이스를 클라이언트에서 요구하는 다른 인터페이스로 변환합니다. 인터페이스가 호환되지 않아 같이 사용할 수 없었던 클래스를 사용할 수 있게 도와줍니다.


어댑터 패턴 구조

어댑터 패턴은 2가지로 구성됩니다.

객체 어댑터

구성(Composition)을 사용하여 동작을 구현 합니다.
어댑티의 모든 서브클래스에 어댑터를 사용할 수 있다는 장점이 있습니다.

클래스 어댑터

상속을 사용하여 동작을 구현 합니다.

  • Target
    • 클라이언트가 요구하는 타깃
  • Adapter
    • 어댑터에서 타킷 인터페이스를 구현 합니다.
  • Adaptee
    • 모든 요청은 어댑터에 위임

라인프렌즈 브라운이 라이언으로 분장하기

라인프렌즈의 브라운이 카카오 아지토에 놀러갔습니다.

이때 브라운이 카카오프렌즈를 놀래키기 위해서 라이언으로 분장하려고 합니다.!

방금 배운 어댑터 패턴을 사용해서 브라운을 도와줘 볼까요?

KakaoFriends

public interface KakaoFirends {
    void hello();
    void action();
}

Ryan

public class Ryan implements KakaoFirends {

    @Override
    public void hello() {
        System.out.println("나는 라이언 입니다.");
    }

    @Override
    public void action() {
        System.out.println("라이언은 따봉을 했습니다.");
    }
}

LineFriends

public interface LineFriends {
    void hi();
    void dance();
}

Brown

public class Brown implements LineFriends {
    @Override
    public void hi() {
        System.out.println("안녕 나는 브라운이야!");
    }

    @Override
    public void dance() {
        System.out.println("춤신춤왕 브라운!");
    }
}

BrownAdapter

public class BrownAdapter implements KakaoFirends {

    private LineFriends lineFriends;

    public BrownAdapter(LineFriends lineFriends) {
        this.lineFriends = lineFriends;
    }

    @Override
    public void hello() {
        lineFriends.hi();
        System.out.println("아닙니다. 브..라이언 입니다.!");
    }

    @Override
    public void action() {
        lineFriends.dance();
        System.out.println("아닙니다. 브..라이언 입니다.!");
    }
}

KakaoFriends

public class Client {
    public static void main(String[] args) {
        KakaoFirends ryan = new Ryan();
        LineFriends brown = new Brown();
          //브라운이 카카오프렌즈 소속으로 바꼈습니다..!
        KakaoFirends brownAdapter = new BrownAdapter(brown);

          ryan.hello();
        ryan.action();

        System.out.println("=======================");

        brown.hi();
        brown.dance();

        System.out.println("=======================");

        brownAdapter.hello();
        brownAdapter.action();

        System.out.println("=======================");

        System.out.println("ryan instanceof KakaoFirends ? "+(ryan instanceof KakaoFirends));
        System.out.println("brown instanceof KakaoFirends ? "+(brown instanceof KakaoFirends));
        System.out.println("brownAdapter instanceof KakaoFirends ? "+(brownAdapter instanceof KakaoFirends));
    }
}

결과

나는 라이언 입니다.
라이언은 따봉을 했습니다.
=======================
안녕 나는 브라운이야!
춤신춤왕 브라운!
=======================
안녕 나는 브라운이야!
아닙니다. 브..라이언 입니다.!
춤신춤왕 브라운!
아닙니다. 브..라이언 입니다.!
=======================
ryan instanceof KakaoFirends ? true
brown instanceof KakaoFirends ? false
brownAdapter instanceof KakaoFirends ? true

퍼사드 패턴(Facade Pattern)

https://heygaso.tistory.com/1892

인터페이스를 단순하게 바꾸려고 인터페이스를 변경하는 패턴
서브시스템에 있는 일련의 인터페이스를 통합 인터페이스로 묶어 줍니다.
고수준 인터페이스도 정의하므로 서브시스템을 더 편리하게 사용할 수 있습니다.

앞에서 배운 어댑터 패턴은 인터페이스를 반환하고, 퍼사드 패턴은 단순하게 인터페이스를 통합 합니다.

728x90

퍼사드 패턴 구조

무슨말인지 잘 모르겠다구요?

음…
예를 들어서 다음과 같은 시스템이 있다고 합시다.
당신의 집은 스마트홈시스템이 적용되어 있습니다.
그래서 몇가지 패턴을 적용했습니다.

  1. 아침 8시가 되면 알람이 울린다.
  2. 커튼이 열립니다.
  3. 어제 내렸던 스크린이 올라갑니다.
  4. 조명도 켜집니다.
  5. 조명 밝기는 최대로~
  6. 그리고 플레이리스트에 있는 음악이 흘러나옵니다.

이걸 코드로 나타내 볼까요?

alarm.on();
curtain.open();
screen.up();
light.on();
light.dim(100);
musicPlayer.play();
//클래스가 5개나 필요합니다!!

그런데 당신이 출근하고 집을 비우면 어떻게 해야할까요?
방금 했던 동작을 모두 종료해야 하지 않을까요?

😖 으.. 너무 복잡하군요…

이러한 복잡한 과정을 사용하기 퍼사드 클래스를 구현함으로써 복잡한 시스템을 훨씬 편리하게 사용할 수 있습니다.!

public class HomeSystem {
    Alaram alaram;
    Curtain curtain;
    Screen screen;
    Light light;
    MusicPlayer musicPlayer;

    public GoodMorningHomeSystem(Alaram alaram, Curtain curtain, Screen screen, Light light, MusicPlayer musicPlayer) {
        this.alaram = alaram;
        this.curtain = curtain;
        this.screen = screen;
        this.light = light;
        this.musicPlayer = musicPlayer;
    }

    public void GoodMorning() {
        System.out.println("아침모드 동작");
        alarm.on();
        curtain.open();
        screen.up();
        light.on();
        light.dim(100);
        musicPlayer.play();
    }
}

퍼사드 패턴을 사용하려면 어떤 서브시스템에 속한 일련의 복잡한 클래스를 단순하게 바꿔서 통합한 클래스를 만들어야 합니다.^^
퍼사드 패턴을 사용하면 클라이언트와 서브시스템이 서로 긴밀하게 연결되지 않아도 됩니다.
퍼사드 패턴은 단순화된 인터페이스로 서브시스템을 더 편리하게 사용하려고 사용된 것을 알 수 있습니다.

반응형

최소 지식 원칙(Principle of Least Knowledge)

결합도가 낮은 설계를 위한 원칙입니다…!!

최소 지식 원칙에 따르면 객체 사이의 상호작용은 될 수 있으면
아주 가까운 친구 사이에만 허용하는 편이 좋습니다.!

진짜 절친한테만 이야기하세요!

이말은 시스템을 디자인할 때 어떤 객체든 그 객체와 상호작용을 하는 클래스의 개수와 상호작용 방식에 주의를 기울여야한다는 의미 입니다.!

최소 지식 원칙을 잘 지키면 여러 클래스가 복잡하게 얽혀 있어, 시스템의 한 부분을 수정 했을 때 다른 부분까지 수정하는 상황을 예방합니다.

여러 객체와 친구가 되는 것을 피할수 있는 최소 지식 원칙 가이드 라인

모든 종류의 메소드는 가이드라인에 해당하는 메소드들로만 호출이 가능합니다.^^

  • 객체 자체
  • 메소드에 매개변수로 전달된 객체
  • 메소드를 생성하거나 인스턴스를 만든 객체
  • 객체에 속하는 구성 요소

원칙을 따르지 않는 경우

station, themometer 에 의존하고 있습니다.

public int getTemp() {
    Themometer themometer = station.getTemperature();
    return themometer.getTemperature();
}

원칙을 준수한 경우

themometer에게 요청을 전달하는 메소드를 Station 클래스에 추가 했습니다.
이러면 의존해야하는 클래스의 갯수를 줄일 수 있습니다.
station 에만 의존하고 있습니다.

public int getTemp() {
    return station.getTemperature();
}

정리

  • 어댑터 패턴은 객체를 감싸서 인터페이스를 바꾸고 반환하는 용도로 사용합니다.
  • 데코레이터 패턴은 객체를 감싸서 새로운 행동을 추가하는 용도로 사용합니다.
  • 퍼사드 패턴은 일련의 객체를 감싸서 단순하게 만드는 용도로 사용합니다.
728x90
반응형