1. 개요

이 사용방법(예제)에서는 Observer 패턴을 설명하고 몇 가지 Java 구현 대안을 살펴보겠습니다.

2. 관찰자 패턴이란 무엇입니까?

관찰자는 행동 디자인 패턴입니다. observableobservers 객체 간의 통신을 지정합니다 . Observable 은 관찰자 에게 상태 의 변화 를 알리는 객체입니다 .

예를 들어 뉴스 대행사는 뉴스를 수신할 때 채널에 알릴 수 있습니다. 뉴스 수신은 뉴스 에이전시의 상태를 변경하고 채널에 알림을 발생시킵니다.

직접 구현하는 방법을 살펴보겠습니다.

먼저 NewsAgency 클래스를 정의합니다.

public class NewsAgency {
    private String news;
    private List<Channel> channels = new ArrayList<>();

    public void addObserver(Channel channel) {
        this.channels.add(channel);
    }

    public void removeObserver(Channel channel) {
        this.channels.remove(channel);
    }

    public void setNews(String news) {
        this.news = news;
        for (Channel channel : this.channels) {
            channel.update(this.news);
        }
    }
}

NewsAgency 는 관찰 가능하며 뉴스 가 업데이트되면 NewsAgency 의 상태가 변경됩니다. 변경 사항이 발생하면 NewsAgency 는 update() 메서드 를 호출하여 관찰자에게 알립니다 .

그렇게 하기 위해서는 관찰 가능한 객체가 관찰자에 대한 참조를 유지해야 합니다. 우리의 경우에는 채널 변수입니다.

이제 채널 클래스인 관찰자가 어떻게 생겼는지 살펴보겠습니다. NewsAgency 의 상태가 변경 될 때 호출되는 update() 메서드 가 있어야 합니다 .

public class NewsChannel implements Channel {
    private String news;

    @Override
    public void update(Object news) {
        this.setNews((String) news);
    } 

    // standard getter and setter
}

Channel 인터페이스에는 하나 의 메서드만 있습니다.

public interface Channel {
    public void update(Object o);
}

이제 NewsChannel 인스턴스 를 observers List에 추가 하고 NewsAgency 상태를 변경하면 NewsChannel 인스턴스 가 업데이트됩니다.

NewsAgency observable = new NewsAgency();
NewsChannel observer = new NewsChannel();

observable.addObserver(observer);
observable.setNews("news");
assertEquals(observer.getNews(), "news");

Java 코어 라이브러리 에는 미리 정의된 Observer 인터페이스가 있어 관찰자 패턴을 훨씬 더 간단하게 구현할 수 있습니다. 그것을 보자.

3. 옵저버 로 구현

java.util.Observer 인터페이스는 update () 메서드를 정의하므로 이전 섹션에서 했던 것처럼 직접 정의할 필요가 없습니다.

구현에서 어떻게 사용할 수 있는지 봅시다.

public class ONewsChannel implements Observer {

    private String news;

    @Override
    public void update(Observable o, Object news) {
        this.setNews((String) news);
    }

    // standard getter and setter
}

여기에서 두 번째 인수는 아래에서 볼 수 있듯이 Observable 에서 가져옵니다.

observable 을 정의하려면 Java의 Observable 클래스를 확장해야 합니다.

public class ONewsAgency extends Observable {
    private String news;

    public void setNews(String news) {
        this.news = news;
        setChanged();
        notifyObservers(news);
    }
}

관찰자의 update() 메서드를 직접 호출할 필요가 없습니다 . setChanged()notifyObservers() 만 호출 하면 Observable 클래스가 나머지 작업을 수행합니다.

또한 관찰자 List을 포함하고 해당 List, addObserver()deleteObserver()를 유지 관리하는 메서드를 노출합니다.

결과를 테스트하려면 이 List에 관찰자를 추가하고 뉴스를 설정하기만 하면 됩니다.

ONewsAgency observable = new ONewsAgency();
ONewsChannel observer = new ONewsChannel();

observable.addObserver(observer);
observable.setNews("news");
assertEquals(observer.getNews(), "news");

Observer 인터페이스는 완벽하지 않으며 Java 9 이후로 더 이상 사용되지 않습니다. 단점 중 하나는 Observable 이 인터페이스가 아니라 클래스이며 하위 클래스를 observable로 사용할 수 없는 이유입니다.

또한 개발자는 Observable 의 동기화된 메서드 중 일부를 재정의 하고 스레드 안전성을 방해할 수 있습니다.

이제 Observer 보다 권장되는 ProperyChangeListener 인터페이스를 살펴보겠습니다 .

4. PropertyChangeListener 로 구현

이 구현에서 Observable은 PropertyChangeSupport 인스턴스에 대한 참조를 유지해야 합니다. 클래스의 속성이 변경될 때 관찰자에게 알림을 보내는 데 도움이 됩니다.

관찰 가능 항목을 정의해 보겠습니다.

public class PCLNewsAgency {
    private String news;

    private PropertyChangeSupport support;

    public PCLNewsAgency() {
        support = new PropertyChangeSupport(this);
    }

    public void addPropertyChangeListener(PropertyChangeListener pcl) {
        support.addPropertyChangeListener(pcl);
    }

    public void removePropertyChangeListener(PropertyChangeListener pcl) {
        support.removePropertyChangeListener(pcl);
    }

    public void setNews(String value) {
        support.firePropertyChange("news", this.news, value);
        this.news = value;
    }
}

지원 을 사용하여 관찰자를 추가 및 제거하고 관찰 가능 상태가 변경되면 알릴 수 있습니다.

support.firePropertyChange("news", this.news, value);

여기서 첫 번째 인수는 관찰된 속성의 이름입니다. 그에 따라 두 번째 및 세 번째 인수는 이전 값과 새 값입니다.

관찰자는 PropertyChangeListener 를 구현해야 합니다 .

public class PCLNewsChannel implements PropertyChangeListener {

    private String news;

    public void propertyChange(PropertyChangeEvent evt) {
        this.setNews((String) evt.getNewValue());
    }

    // standard getter and setter
}

우리를 위해 연결해주는 PropertyChangeSupport 클래스 로 인해 이벤트에서 새 속성 값을 복원할 수 있습니다.

구현이 제대로 작동하는지 테스트해 보겠습니다.

PCLNewsAgency observable = new PCLNewsAgency();
PCLNewsChannel observer = new PCLNewsChannel();

observable.addPropertyChangeListener(observer);
observable.setNews("news");

assertEquals(observer.getNews(), "news");

5. 결론

이 기사에서는 Java에서 Observer 디자인 패턴 을 구현하는 두 가지 방법을 살펴보았습니다 . PropertyChangeListener 접근 방식이 선호 된다는 것을 배웠습니다 .

이 기사 의 소스 코드는 GitHub에서 사용할 수 있습니다 .

Generic footer banner