정의
복잡한 시스템에 대해서 간단한 인터페이스를 제공해서 시스템을 이용하는 사용 객체가 단일 진입점을 가지고도 모든 시스템을 시용할 수 있는 디자인 패턴을 말한다.
위에 있는사진과 같이 클라이언트는 facade 시스템의 자세한 코드를 알 필요가 없다. 단지 시스템의 코드를 추상화한 것을 사용할 수 있다.
어디서 본그림이지 않는가? 맞다 모듈화이다. 이는 다음과 같은 객체지향 설계 방법을 충족시킬 수 있다.
- 단일 책임 원칙
- 개방 폐쇄 원칙
- 로우 결합
- 높은 응집도
- 정보 은닉
- 인터페이스 분리
이는 다음과 같은 장점을 가진다.
- 단순화: 복잡한 서브시스템의 사용을 단순화
- 캡슐화: 서브시스템의 내부 구현을 감춰 사용자가 알 필요가 없게됨
- 독립성: 클라이언트와 서브시스템 간의 결합도를 낮춤
이로인해서 응집도는 높아지고 결합도가 낮아지는 결과를 만들수 있다.
예시
자바에서는 인터페이스를 적극 사용하게 된다. 이는 Factory Method 패턴을 사용하기 위해서이다.
그럼 함께 예시를 알아보자. 지금은 서브시스템 클레스들을 같은 코드란에 뒀지만 다른 파일로 관리하는 것이 좋다.
예시는 스마트 홈시스템이다.
기능은 다음과 같다.
- 불을 키고 끌수 있는 Light 기능
- 에어컨을 사용할 수 있는 기능
- 티비를 사용할 수 있는 기능
관계도는 다음과 같다 이제 코드로 한번 설명을 보자.
서브 시스템 인터페이스 정의
//각각의 서브 시스템
interface Light {
void turnOn();
void turnOff();
}
interface AirConditioner {
void turnOn();
void turnOff();
void setTemperature(int temperature);
}
interface TV {
void turnOn();
void turnOff();
void setChannel(int channel);
}
위 인터페이스에서 turnOn, turnOff가 겹치고 있지 않는가? 이를 해결하는 다른 디자인 패턴 접은글 참고
Template Method Pattern : 키고 끄는 것을 abstract class를 구현해서 확장 받도록함
abstract class Device {
public void turnOn() {
System.out.println(getClass().getSimpleName() + " turned on");
specificOn();
}
public void turnOff() {
System.out.println(getClass().getSimpleName() + " turned off");
specificOff();
}
protected abstract void specificOn();
protected abstract void specificOff();
}
Command Pattern : 키고 끄는 것을 interface로 구현해서 상속받도록 함
interface Command {
void execute();
void undo();
}
이는 나중에 기회가되면 자세히 설명하겠다.
서브 시스템 구현(Factory Method)
// Subsystem implementations
class LivingRoomLight implements Light {
@Override
public void turnOn() {
//키는 기능
}
@Override
public void turnOff() {
//끄는 기능
}
}
class HomeAirConditioner implements AirConditioner {
@Override
public void turnOn() {
//키는 기능
}
@Override
public void turnOff() {
//끄는 기능
}
@Override
public void setTemperature(int temperature) {
//온도를 정하는 기능
}
}
class LivingRoomTV implements TV {
@Override
public void turnOn() {
//키는 기능
}
@Override
public void turnOff() {
//끄는 기능
}
@Override
public void setChannel(int channel) {
//채널을 정하는 기능
}
}
Facade class 생성
class SmartHome {
private Light livingRoomLight;
private AirConditioner airConditioner;
private TV livingRoomTV;
//한개의 기능만 사용하게 할수도 있고
public void turnOffLights() {
livingRoomLight.turnOff();
}
//여러 기능을 조합하게 할수도 있다.
public SmartHomeFacade() {
this.livingRoomLight = new LivingRoomLight();
this.airConditioner = new HomeAirConditioner();
this.livingRoomTV = new LivingRoomTV();
}
public void activateEveningMode() {
livingRoomLight.turnOn();
airConditioner.turnOn();
airConditioner.setTemperature(22);
livingRoomTV.turnOn();
livingRoomTV.setChannel(5);
}
public void deactivateAll() {
livingRoomLight.turnOff();
airConditioner.turnOff();
livingRoomTV.turnOff();
}
}
이를 보면 이제 클라이언트가 SmartHome만 안다면 여러가지 기능들을 사용할 수 있게 된다. 세부로직을 전혀 몰라도된다.
근데좀 부족하지 않은가? turnOffLights같은 기능이 엄청많아서 그리고 담당 개발자가 이것을 까먹고 class SmartHome에 작성하지 않는다면 클라이언트는 작성하지 않은 기능을 사용할수 없을 것이다. 이를 해결해보자
응용
interface SmartHome extends Light, AirConditioner, TV {
//추가적으로 만들 메서드
public SmartHomeFacade()
public void activateEveningMode()
public void deactivateAll()
}
서브 기능들을 확장으로 상속받는 SamrtHome 인터페이스를 만든다. 그럼 SmartHome 인터페이스를 구현하는 class는 반드시 Light, AirConditioner, TV 인터페이스에 있는 메서드들을 구현해야만 한다. 이때 Light, AirConditioner, TV의 인터페이스를 상속받아서 미리 구현해논 class로직들을 가져와서 사용한다.
아레 코드를 보면 만드시 구현해야하는 메서드들의 로직을 SmartHomeImpl에서 구현하지 않는다.
이제 사용자는 smartHome의 서브 시스템의 메서드를 각각을 사용할 수 있음을 보장받으면서도 추가적으로 만드는 메서드들도 사용할수 있게된다!
class SmartHomeImpl implements SmartHome {
private Light livingRoomLight;
private AirConditioner airConditioner;
private TV livingRoomTV;
public SmartHomeFacade() {
this.livingRoomLight = new LivingRoomLight();
this.airConditioner = new HomeAirConditioner();
this.livingRoomTV = new LivingRoomTV();
}
// Light methods
@Override
public void turnOn() {
livingRoomLight.turnOn();
}
@Override
public void turnOff() {
livingRoomLight.turnOff();
}
// AirConditioner methods
@Override
public void turnOnAirConditioner() {
airConditioner.turnOn();
}
@Override
public void turnOffAirConditioner() {
airConditioner.turnOff();
}
@Override
public void setTemperature(int temperature) {
airConditioner.setTemperature(temperature);
}
// TV methods
@Override
public void turnOnTV() {
livingRoomTV.turnOn();
}
@Override
public void turnOffTV() {
livingRoomTV.turnOff();
}
@Override
public void setChannel(int channel) {
livingRoomTV.setChannel(channel);
}
//여러 기능을 조합하게 할수도 있다.
public SmartHomeFacade() {
this.livingRoomLight = new LivingRoomLight();
this.airConditioner = new HomeAirConditioner();
this.livingRoomTV = new LivingRoomTV();
}
public void activateEveningMode() {
livingRoomLight.turnOn();
airConditioner.turnOn();
airConditioner.setTemperature(22);
livingRoomTV.turnOn();
livingRoomTV.setChannel(5);
}
public void deactivateAll() {
livingRoomLight.turnOff();
airConditioner.turnOff();
livingRoomTV.turnOff();
}
}
Coding, Software, Computer Science 내가 공부한 것들 잘 이해했는지, 설명할 수 있는지 적는 공간