
A. 인터페이스가 아닌 구체적인 구현을 바탕으로 코딩함. 
B. 새로운 디스플레이 항목이 추가될 때마다 코드를 변경해야 함. 
C. 실행 중에는 디스플레이 항목을 추가하거나 제거할 수 없음. 
E. 바뀌는 부분을 캡슐화하지 않음. 
p.78
public interface DisplayElement {
    public void display();
} 
public interface Observer {
    public void update();
    //기상 정보가 변경되었을 때 옵저버에게 전달되는 상태값들.
    //이 인터페이스는 모든 옵저버 클래스에서 구현되어야 함. 그래서 모든 옵저버들은 update() 메소드를 구현해야 한다.
}
public interface Subject {
    public void registerObserver(Observer o);
    public void removeObserver(Observer o);
    //위 두 메소드들은 Observers을 인자로 받음. 각각 옵저버를 등록하거나 제거하는 역할.
    public void notifyObservers();
    //주제의 상태가 변경되었을 때 모든 옵저버에게 변경 내용을 알리려고 호출되는 메소드.
}
public class WeatherData implements Subject{
    //인스턴스 변수 선언
    private List<Observer> observers;
    private float temperature;
    private float humidity;
    private float pressure;
    public WeatherData(){
        observers = new ArrayList<Observer>();
    }
    //Subject 인터페이스를 구현하는 부분
    public void registerObserver(Observer o){//옵저버가 둥록 요청 시 목록 맨 뒤에 추가하기만 하면 됨
        observers.add(o);
    }
    public void removeObserver(Observer o){//옵저버가 탈퇴 요청 시 목록에서 빼기만 하면 됨
        observers.remove(o);
    }
    public void notifyObserver(){
        for(Observer observer : observers){
            observer.update();
        }
    }
    /* 중요한 부분 - 모든 옵저버에게 상태 변화를 알려줌*/
    // 모두 Observer 인터페이스를 구현하는 update() 메소드가 있는 객체들이므로 상태 변화를 쉽게 알려줄 수 있음.
    public void measurementsChanged(){
        //가상 스테이션으로부터 갱신된 측정 값을 받으면 옵저버들에게 알림.
        notifyObserver();
    }
    public void setMeasurements(float temperature, float humidity, float pressure){
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
}
public class CurrentConditionsDisplay implements Observer, DisplayElement{
    // Observer - WeatherData 객체로부터 변경 사항을 받으려면 구현해야 함.
    // DisplayElement - API 구조상 모든 디스플레이 항목에서 DisplayElement를 구현하기로 했기에 이 인터페이스를 구현함.
    private float temperature;
    private float humidity;
    private WeatherData weatherData;
    public CurrentConditionsDisplay(WeatherData weatherData){
        //생성자에 weatherData라는 주제가 전달되며, 그 객체를 써서 디스플레이를 옵저버에 등록함.
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }
    public void update(){
        this.temperature = weatherData.getTemperature();
        this.humidity = weatherData.getHumidity();
        //Subjcet의 게터 메소드 사용
        display();
        //update가 호출되면 온도와 습도를 저장하고 display()를 호출
    }
    public void display(){
        System.out.println("현재 상태: 온도" + temperature + 'F, 습도' + humidity + "%");
        //display() 메소드는 가장 최근의 온도와 습도를 출력
    }
}
public class WeatherStation{
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
        weatherData.setMeasurements(80, 65, 30.4f);
        weatherData.setMeasurements(82, 70, 29.2f);
        weatherData.setMeasurements(78 , 90, 29. 2f);
    }
}
public void measurementsChanged(){
    float temp = getTemperature();
    float humidity = getHumidity();
    float pressure = getPressure();
    //아래 부분들은 바뀔 수 있으므로 캡슐화 필요!
    currentConditionsDisplay.update(temp, humidity, pressure);
    statisticsDisplay.update(temp, humidity, pressure);
    forecastDisplay.update(temp, humidity, pressure);
}
public class SwingObserverExample{
    JFrame frame;
    public static void main(String[] args) {
        //프레임을 만들고 그 안에 버튼을 추가하는 간단한 스윙 애플리케이션
        SwingObserverExample example = new SwingObserverExample();
        example.go();
    }
    public void go(){
        frame = new JFrame();
        JButton button = new JButton("할까?말까?");
        //람다식으로 구현
        button.addActionListener(event->
                System.out.println("하지마! 아마 후회할 걸?"));
        button.addActionListener(event->
                System.out.println("그냥 해 봐"));
    }
}