뻔뻔하게 4일차 갑니당..
어디서 주워들은 잘 쓰이는 패턴~~ 퍼사드 패턴
어제 정리하려다가 못했던 어댑터~
퍼사드 패턴
서브 시스템의 일련의 인터페이스에 대한 통합된 인터페이스를 제공한다.
즉, 서브 시스템들을 사용하기 쉽도록 통합해주는 역할을 한다.
Facade pattern 구성요소
- Facade
서브 시스템들의 창구 역할을 해서 높은 레벨에서 단순히 인터페이스를 제공한다.
실제 코드
참고한 블로그에서는 집에서 영화를 보려고 자동화 시스템을 만들었을 때의 코드를 작성했는데, 그럼 나는..재택을 하기 위한 자동화 시스템이라고 해보자.
- 샤워하기
- 커피 사오기
- 주변 정리하기
- 원격 컴퓨터 연결하기
재택을 하기 위해서는 위의 서브 시스템들이 작동해야 한다.
퍼사드 패턴을 적용하지 않으면 아래와 같이 코드를 작성해야 한다.
class Main {
public static void main(String[] args) {
Shower shower = new Shower();
shower.done();
Coffee coffee = new Coffee();
coffee.goOut();
Cleaner cleaner = new Cleaner();
cleaner.done();
RemoteComputer remoteComputer = new RemoteComputer();
remoteComputer.turnOn();
// 그 밖의 서브 시스템들의 동작들..
}
}
작성된 코드들만으로 일단 재택을 시작하긴 했는데, 재택을 종료하려면 위의 코드(클라이언트)에 서브 시스템들을 모두 작성해야한다.
클라이언트에 많은 수정이 일어나는 문제를 막기 위해 퍼사드 패턴을 사용한다.
적용해보자면..
class WorkingFromHomeFacade {
Shower shower;
Coffee coffee;
Cleaner cleaner;
RemoteComputer remoteComputer;
public WorkingFromHomeFacade(Shower shower, Coffee coffee, Cleaner cleaner, RemoteComputer remoterComputer) {
this.shower = shower;
this.coffee = coffee;
this.cleaner = cleaner;
this.remoteComputer = remoteComputer;
}
public void startWorking(String companyName) {
System.out.println(companyName + "을 위해.. 일 중입니다..");
this.shower.done();
this.coffee.goOut();
this.cleaner.done();
this.remoteComputer.turnOn();
}
}
위와 같이 퍼사드 패턴으로 서브 시스템들의 동작을 startWorking 메소드로 묶을 수 있다.
class Main {
public static void main(String[] args) {
WorkingFromHomeFacade workingFromHomeFacade = new WorkingFromHomeFacade(
new Shower(), new Coffee(), new Cleaner(), new RemoteComputer());
workingFromHomeFacade.startWorking("마녀김밥");
}
}
😆 사용하면 뭐가 좋을까?
클라이언트가 복잡한 서브 시스템 동작을 관리하지 않아도 된다.
-> 단순화된 인터페이스를 통해 편리하게 서브 시스템을 사용할 수 있다.
서버 시스템에 변경이 있어도 클라이언트의 수정이 없다.
옵저버 패턴
한 객체의 상태가 변경되면 그 객체에 의존하고 있는 다른 객체들에게 변경 내용을 알려주고 자동으로 내용이 갱신되는 방식으로 일대다 의존성을 정의한다.
-> 상태 변화에 대한 처리가 필요할 때 사용한다.
-> 느슨한 결합을 통해 의존성을 제거한다.
Observer pattern 구성요소
- Subject
관찰되는 대상을 관리하는 역할을 한다.
- ConcreteSubject
구체적으로 Subject를 구현하고, 통보하는 역할을 한다.
- Observer
'상태가 변화되었음'을 전달받는 역할을 한다.
- ConcreteObserver
구체적으로 Observer를 구현하고, 변화를 전달받는 역할을 한다.
Subject와 Observer와 같은 인터페이스를 ConcreteSubject와 ConcreteObserver 사이에 끼워넣는 것은
-> '객체 간의 의존성을 제거하기 위함'이다.
실제 코드
Subject
'관찰되는 대상'을 관리하는 인터페이스로 Observer를 저장, 제거, 관리하는 메소드를 가진다.
interface Subject {
public void registerObserver(Observer o); // 옵저버 등록
public void removeObserver(Observer o); // 옵저버 제거
public void notifyObservers(); // 변화를 알려주기 위한 메소드
}
Observer
상태가 변화되었음을 관리하는 인터페이스로 상태가 변경되었다는 통보를 받으면 실행시킬 메소드를 가지고 있다.
interface Observer {
public void update(float temp, float humidity);
}
ConcreteSubject
옵저버들의 추가, 삭제를 편리하게 하기 위해 observers라는 list 멤버변수로 옵저버들을 관리한다.
또한, 변경을 받아들일 메소드들을 차례로 정의한다.
class WeatherDataSubject implements Subject {
private ArrayList<Observer> observers;
private float temperature;
private float humidity;
public WeatherDataSubject() {
observers = new ArrayList<>();
}
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
@Override
public void removeObserver(Observer o) {
observers.remove(o);
}
// 변화된 상태를 알려주는 메소드
@Override
public void notifyObservers() {
for(Observer o: observers) {
o.update(this.temperature, this.humidity);
}
}
// 상태가 변화됨을 감지하는 메소드
public void measurementsChanged() {
notifyObservers();
}
// 상태를 변화시키는 메소드
public void setMeasurements(float temperature, float humidity) {
this.temperature = temperature;
this.humidity = humidity;
measurementsChanged();
}
}
ConcreteObserver
Weather 객체의 변동사항을 받기 위해 Observer 인터페이스를 구현한다.
같은 Subject를 받기 위해 생성자를 통해 Subject 객체를 전달받고, 필드의 객체에 참조시킨다.
또한, registerObserver 메소드를 호출함으로써 Subject의 observer들에 포함시킨다.
class CurrentWeatherDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private Subject weatherData;
public CurrentWeatherDisplay(WeatherDataSubject weatherDataSubject) {
this.weatherData = weatherDataSubject;
weatherData.register(this);
}
@Override
public void update(float temperature, float humidity) {
this.temperature = temperature;
this.humidity = humidity;
display();
}
@Override
public void display() {
System.out.println("current weather display~ " + temperature + ", " + humidity);
}
}
interface DisplayElement {
public void display();
}
Main
setMeasurements를 호출하면 상태가 변화됨을 감지하는 메소드인 measurementsChanged가 호출되고 notifyObservers 메소드를 호출하면서 Subject의 observers에 포함된 옵저버들이 업데이트된다.
class Main {
public static void main(String[] args) {
WeatherData weatherData = new WeatherDataSubject();
ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
CurrentWeatherDisplay currentWeatherDisplay = new CurrentWeatherDisplay(weatherData);
forecaseDisplay.setMeasurements(28, 30);
currentWeatherDisplay.setMeasurements(23, 31);
}
}
😆 사용하면 뭐가 좋을까?
Main 클래스와 같은 클라이언트는 옵저버를 등록만 하면, 알아서 변화된 상태를 감지하고 동작한다.
위에서 이야기했던 객체간의 의존성을 해결했기 때문에 자유롭게 옵저버를 등록하고 관리할 수 있다.
만약 전혀 다른 ConcreteObserver를 구현해도 수정이 필요없고 클라이언트가 사용하는 데에 전혀 문제가 없다.
참고 사이트
1. https://lee1535.tistory.com/94
2. https://lee1535.tistory.com/98