해당 포스팅은 한빛 미디어 헤드퍼스트 디자인 패턴(에릭 프리먼, 엘리자베스 롭슨 저)를 통해 공부한 내용을 정리한 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
좋은 프로그래머는 자기 두뇌를 사용한다. 그러나 좋은 가이드라인은 모든 케이스를 고려해야만 하는 노력을 줄여준다.
(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() 호출
- 어댑터는 타겟 인터페이스를 구현해 어뎁티 인스턴스의 메소드를 호출
💡 주의
타겟 인터페이스의 크기에 따라 복잡해지지만 변경사항을 매번 고려해서 바꾸는 것보다는
어댑터 패턴을 구현하는 것이 효과적일 때가 있음.
- 어댑터 패턴은 하나의 어댑터 적용하는 것.
- 서비스가 커지면 어댑터 패턴 적용이 어려움.
- 두개 이상의 어댑터를 적용하는 것은 파사드 패턴
정의
- 특정 클래스 인터페이스를 클라이언트에서 요구하는 다른 인터페이스로 변환
- 호환되지 않던 인터페이스도 같이 사용할 수 있음.
- 특정 클라이언트가 구상 클래스에 의존하는 것이 아닌 인터페이스에 의존
객체 어댑터와 클래스 어댑터
여태까지 구현했던 것이 객체 어댑터(상위 그림)
클래스 어댑터를 구현하기 위해선 다중 상속이 필요
- 객체 어댑터
- 구성 사용
- 어댑티 클래스와 그 서브 클래스에 대해서 어댑터 역할 가능
- 클래스 어댑터
- 특정 어댑티 클래스에만 적용
- 어댑티 전체를 다시 구현하지 않아도 된다는 장점
- 서브 클래스라서 어댑티의 행동을 오버라이드 할 수 있음.
- 어댑터 하나만 있으면 가능.
실전 적용
- 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 -> 겉모양이나 외관
- 인터페이스를 단순하게 바꾸기 위해서 인터페이스를 변경.
- 하나 이상의 클래스 인터페이스를 깔끔하면서도 효과적인 퍼사드로 덮기.
홈시어터 만들기
- 스크린, 팝콘기계, 조명, 음향 등… 너무 많은 클래스의 관리를 요함.
- 다른 시스템에 적용할 때에도 여러개의 클래스 관리.
- 시스템이 업그레이드 되면 코드 추가.
퍼사드 작동 원리 알아보기
- 퍼사드 클래스는 서브 시스템 클래스를 캡슐화 하지 않음.
- 그저 인터페이스 제공.
- 클라이언트 구현과 서브 시스템을 분리할 수 있음.
정의
- 서브시스템에 있는 일련의 인터페이스를 통합 인터페이스로 묶어준다.
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();
}
}
'기타 > 디자인패턴' 카테고리의 다른 글
[Design Pattern] Iterator Pattern & Composite Pattern (0) | 2024.06.09 |
---|---|
[Design Pattern] Template Method Pattern (0) | 2024.06.05 |
[Design Pattern] Command Pattern (2) | 2024.06.03 |
[Design Pattern] Singleton Pattern (0) | 2024.05.31 |
[Design Pattern] Factory Pattern (0) | 2024.05.29 |