해당 포스팅은 한빛 미디어 헤드퍼스트 디자인 패턴(에릭 프리먼, 엘리자베스 롭슨 저)를 통해 공부한 내용을 정리한 블로그입니다.

아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
좋은 프로그래머는 자기 두뇌를 사용한다. 그러나 좋은 가이드라인은 모든 케이스를 고려해야만 하는 노력을 줄여준다.
(Francis Glassborow, 개발자)

객체지향


  • 새로운 클래스의 인터페이스가 기존 클래스의 인터페이스와 다를 때
  • 새로운 클래스의 인터페이스가 생긴다면 기존 시스템의 인터페이스를 사용할 수 없음.
  • 새로운 클래스와 기존 인터페이스를 연결해 줄 클래스 => Adapter
    • 두코드 모두 변화를 안가져도 됨

사용방법


public interface Duck {
    public void quack();
    public void fly();
}

public class MallardDuck implements Duck{
    @Override
    public void quack() {
        System.out.println("꽥");
    }

    @Override
    public void fly() {
        System.out.println("i can fly");
    }
}


public interface Turkey {
    public void gobble();
    public void fly();
}

public class WildTurkey implements Turkey {
    @Override
    public void gobble() {
        System.out.println("골골");
    }

    @Override
    public void fly() {
        System.out.println("짧은 거리를 날아요");
    }
}
  • 기존의 Duck 인터페이스가 존재했지만 Turkey 인터페이스가 새로 등장
  • 기존 Duck 인터페이스를 사용하는 시스템에서 Turkey 인터페이스를 사용할 수 없음. => Adapter 패턴을 적용

Adapter 적용

public class TurkeyAdapter implements Duck {
	Turkey turkey;
 
	public TurkeyAdapter(Turkey turkey) {
		this.turkey = turkey;
	}
    
	public void quack() {
		turkey.gobble();
	}
  
	public void fly() {
		for(int i=0; i < 5; i++) {
			turkey.fly();
		}
	}
}


public class DuckTestDrive {
	public static void main(String[] args) {
		Duck duck = new MallardDuck();

		Turkey turkey = new WildTurkey();
		Duck turkeyAdapter = new TurkeyAdapter(turkey);

		System.out.println("The SimpleAdapter.Turkey says...");
		turkey.gobble();
		turkey.fly();

		System.out.println("\nThe SimpleAdapter.Duck says...");
		testDuck(duck);

		System.out.println("\nThe SimpleAdapter.TurkeyAdapter says...");
		testDuck(turkeyAdapter);
	}

	static void testDuck(Duck duck) {
		duck.quack();
		duck.fly();
	}
}

Adapter Pattern


  • 클라이언트에게 타겟 인터페이스에게 request() 호출
  • 어댑터는 타겟 인터페이스를 구현해 어뎁티 인스턴스의 메소드를 호출
💡 주의
타겟 인터페이스의 크기에 따라 복잡해지지만 변경사항을 매번 고려해서 바꾸는 것보다는
어댑터 패턴을 구현하는 것이 효과적일 때가 있음.
  • 어댑터 패턴은 하나의 어댑터 적용하는 것.
    • 서비스가 커지면 어댑터 패턴 적용이 어려움.
    • 두개 이상의 어댑터를 적용하는 것은 파사드 패턴

정의

  • 특정 클래스 인터페이스를 클라이언트에서 요구하는 다른 인터페이스로 변환
  • 호환되지 않던 인터페이스도 같이 사용할 수 있음.
  • 특정 클라이언트가 구상 클래스에 의존하는 것이 아닌 인터페이스에 의존

Adapter Pattern

객체 어댑터와 클래스 어댑터


여태까지 구현했던 것이 객체 어댑터(상위 그림)
클래스 어댑터를 구현하기 위해선 다중 상속이 필요

객체 어댑터 VS 클래스 어댑터

  • 객체 어댑터
    • 구성 사용
    • 어댑티 클래스와 그 서브 클래스에 대해서 어댑터 역할 가능
  • 클래스 어댑터
    • 특정 어댑티 클래스에만 적용
    • 어댑티 전체를 다시 구현하지 않아도 된다는 장점
    • 서브 클래스라서 어댑티의 행동을 오버라이드 할 수 있음.
    • 어댑터 하나만 있으면 가능.

실전 적용


  • Enumeration 인터페이스
    • Enumeration을 리턴하는 elements() 메소드가 구현되어 있음.
    • 각 컬렉션의 모든 항목에 접근 가능
  • Iterator인터페이스
    • 컬렉션의 항목에 접근하고 그 항목을 제거할 수 있는 메소드 사용

Enumeration을 Iterator에 적응하기.

  • Iterator ⇒ Target Interface
    • hasNext()
    • next()
  • Enumeration ⇒ Adaptee Interface
    • hasMoreElements()
    • nextElement()
  • 각각의 요소에 대응. (hasNext()hasMoreElements() || next()nextElement())
  • 기존 코드에 있던 Enumeration도 새로운 코드에서는 Iterator처럼 보임.
  • 타겟 인터페이스와 어댑티 인터페이스의 메소드가 완벽히 1대1 매칭이 되지 않는 상황에서는 완벽하게 적용 불가능.

Facade Pattern


Facade -> 겉모양이나 외관

  • 인터페이스를 단순하게 바꾸기 위해서 인터페이스를 변경.
  • 하나 이상의 클래스 인터페이스를 깔끔하면서도 효과적인 퍼사드로 덮기.

홈시어터 만들기

  • 스크린, 팝콘기계, 조명, 음향 등… 너무 많은 클래스의 관리를 요함.
  • 다른 시스템에 적용할 때에도 여러개의 클래스 관리.
  • 시스템이 업그레이드 되면 코드 추가.

퍼사드 작동 원리 알아보기

  • 퍼사드 클래스는 서브 시스템 클래스를 캡슐화 하지 않음.
    • 그저 인터페이스 제공.
  • 클라이언트 구현과 서브 시스템을 분리할 수 있음.

정의

  • 서브시스템에 있는 일련의 인터페이스를 통합 인터페이스로 묶어준다.

Facade Pattern

example

public class Test {
    public static void main(String[] args) {
	    // 각각의 객체는 생략
        Amplifier amp = new Amplifier("amp");
        Tuner tuner = new Tuner("tuner", amp);
        StreamingPlayer player = new StreamingPlayer("cd", amp);
        Projector projector = new Projector("proj", player);
        Screen screen = new Screen("screen");
        TheaterLights lights = new TheaterLights("light");
        PopcornPopper popper = new PopcornPopper("popcorn");

        HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade(amp, tuner, player, projector, lights, screen, popper);

        homeTheaterFacade.watchMovie("어바웃타임");
        homeTheaterFacade.endMovie();
    }
}

public class HomeTheaterFacade {
    Amplifier amp;
    Tuner tuner;
    StreamingPlayer player;
    Projector projector;
    TheaterLights lights;
    Screen screen;
    PopcornPopper popper;

    public HomeTheaterFacade(Amplifier amp, Tuner tuner, StreamingPlayer player, Projector projector, TheaterLights lights, Screen screen, PopcornPopper popper) {
        this.amp = amp;
        this.tuner = tuner;
        this.player = player;
        this.projector = projector;
        this.lights = lights;
        this.screen = screen;
        this.popper = popper;
    }

    public void watchMovie(String movie) {
        System.out.println("start to watch movie");

        popper.on();
        popper.pop();

        lights.dim(10);

        screen.down();

        projector.on();
        projector.wideScreenMode();

        amp.on();
        amp.setStereoSound();
        amp.setStreamingPlayer(player);
        amp.setVolume(5);

        player.on();
        player.play(movie);
    }

    public void endMovie() {
        System.out.println("turn off movie");

        popper.off();

        lights.on();

        screen.up();

        projector.off();

        amp.off();

        player.stop();
        player.off();
    }
}

+ Recent posts