JavaRush /Blog Java /Random-VI /Mẫu người quan sát

Mẫu người quan sát

Xuất bản trong nhóm
Như Gang of Four viết (tham khảo cuốn sách “Các mẫu thiết kế hướng đối tượng” của 4 nhà phát triển hàng đầu), mục đích của mẫu này là xác định sự phụ thuộc một-nhiều giữa các đối tượng theo cách mà khi trạng thái của một đối tượng thay đổi, tất cả những người phụ thuộc vào nó sẽ được thông báo về điều này và được cập nhật tự động. Mẫu này còn được gọi là: Người phụ thuộc (cấp dưới) hoặc Publish-Subscribe (nhà xuất bản - người đăng ký). Nhưng chúng ta hãy thử tìm hiểu nó bằng ví dụ của Giáo hội Công giáo :) Nó có những tín đồ tin vào những lời dạy của nhà thờ này. Khi có bất kỳ giáo điều mới nào (tín ngưỡng bắt buộc) và nhiều giáo điều khác xuất hiện, những người này nên biết về chúng. Nhưng làm thế nào điều này có thể được mô tả bằng ngôn ngữ lập trình sử dụng mẫu này? 1. Chúng ta có “tiếng nói của nhà thờ” (chính nhà thờ hoặc Giáo hoàng khi phát sóng ex cathedra), tức là một đài phát thanh hoặc chủ thể nào đó thông báo tin tức trong nhà thờ. 2. Có những giáo dân của nhà thờ này, tức là có một số người quan sát muốn biết về những sự kiện quan trọng. Theo đó, hôm nay có thể có 1,3 tỷ giáo dân, ngày mai có thể có ít nhiều. Và bạn chỉ cần thông báo cho những người đang ở trong nhà thờ này (không cần phải làm phiền những người vô thần nữa :). Vì vậy, tất cả những điều này có thể được diễn đạt như sau: Có một nhà thờ sẽ nói với đàn chiên của mình về điều gì đó mà bạn có thể đăng ký hoặc ngược lại, bỏ nó:
public interface Church {
    void registerParishioner(Parishioner parishioner);
    void removeParishioner(Parishioner parishioner);
    void notifyParishioners();
}
Có một Giáo hội Công giáo cụ thể thực hiện các phương pháp này, cũng như tin tức và danh sách những người sẽ được phổ biến tin tức này:
public class CatholicChurch implements Church {
    private List<parishioner> parishioners;
    private String newsChurch;

    public CatholicChurch() {
        parishioners = new ArrayList<>();
    }

    public void setNewsChurch(String news) {
        this.newsChurch = news;
        notifyParishioners();
    }

    @Override
    public void registerParishioner(Parishioner parishioner) {
        parishioners.add(parishioner);
    }

    @Override
    public void removeParishioner(Parishioner parishioner) {
        parishioners.remove(parishioner);
    }

    @Override
    public void notifyParishioners() {
        for (Parishioner parishioner : parishioners)
            parishioner.update(newsChurch);
    }
}
Có một giáo dân có thể vào hoặc ra khỏi nhà thờ (để đơn giản hóa mã, chúng tôi sẽ chỉ cho phép anh ta vào :)
public class Parishioner {

    private String name;

    public Parishioner(String name, Church church) {
        this.name = name;
        church.registerParishioner(this);
    }

    void update(String newsChurch) {
        System.out.println(name + "узнал новость: " + newsChurch);
    }
}
Theo đó, đây là cách nó sẽ hoạt động:
public class Main {
    public static void main(String[] args) {
        var catholicChurch = new CatholicChurch();

        new Parishioner("Мартин Лютер", catholicChurch);
        new Parishioner("Жан Кальвин", catholicChurch);

        catholicChurch.setNewsChurch("Инквизиция была ошибкой... месса Mea Culpa 12 марта 2000 года");
    }
}
và kết quả của chương trình:
Мартин Лютер узнал новость: Инквизиция была ошибкой... месса Mea Culpa 12 марта 2000 года
Жан Кальвин узнал новость: Инквизиция была ошибкой... месса Mea Culpa 12 марта 2000 года
Những thứ kia. ngay khi có tin tức xuất hiện trong nhà thờ, tất cả những người trong danh sách thành viên đã đăng ký của nhà thờ này sẽ được thông báo về điều đó. Nhược điểm của việc triển khai này là gì: 1. Thứ nhất, giao diện nơi bạn có thể đăng ký và nhận tin tức có thể không chỉ liên quan đến nhà thờ này (điều này có thể được yêu cầu). Do đó, có thể chuyển ngay giao diện này sang một giao diện Có thể quan sát riêng. 2. Điều tương tự có thể được thực hiện với giáo dân trong nhà thờ, cụ thể là chuyển phương thức cập nhật sang một giao diện riêng và triển khai nó cho giáo dân mong muốn. Sau đó, phương pháp này nói chung sẽ có thể được sử dụng không phải bởi các giáo dân của Giáo hội Công giáo, mà, chẳng hạn, bởi những người sống trong sự tồn tại của yêu tinh (có nghĩa là phong trào “Con đường đến Kỳ lân”). Những thứ kia. tạo giao diện Observer với phương thức cập nhật. Điều gì sẽ xảy ra cuối cùng:
interface Observable {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}
public class CatholicChurch implements Observable {
    private List<observer> parishioners;
    private String newsChurch;

    public CatholicChurch() {
        parishioners = new ArrayList<>();
    }

    public void setNewsChurch(String news) {
        this.newsChurch = news;
        notifyObservers();
    }

    @Override
    public void registerObserver(Observer o) {
        parishioners.add(o);
    }

    @Override
    public void removeObserver(Observer o) {
        parishioners.remove(o);
    }

    @Override
    public void notifyObservers() {
        for (Observer o : parishioners)
            o.update(newsChurch);
    }
}
interface Observer {
    void update (String news);
}
public class Parishioner implements Observer {
    private String name;

    public Parishioner(String name, Observable o) {
        this.name = name;
        o.registerObserver(this);
    }

    @Override
    public void update(String news) {
        System.out.println(name + " узнал новость: " + news);
    }
}
Như vậy: Chúng ta đã “làm suy yếu sự kết nối” giữa nhà thờ và giáo dân, điều này đương nhiên chỉ tốt trong lập trình :) Chủ thể (nhà thờ Công giáo) chỉ có danh sách thính giả (giáo dân) và khi nhận được tin tức (thay đổi) sẽ phát tin này tới người nghe của nó. Giờ đây, bạn có thể tạo bất kỳ thực thể nào khác (ví dụ: nhà thờ Tin lành) và phát tin tức đến những người nghe “của bạn”. Bạn cũng cần lưu ý rằng dữ liệu lớp 2 (chính xác hơn là lớp Observable và giao diện Observer) đã có sẵn trong gói java.util java, nhưng hiện tại chúng không được dùng nữa với java 9 (https://docs.oracle. com/en/java/javase/15/ docs/api/java.base/java/util/Observable.html): Không được dùng nữa. Lớp này và giao diện Observer không còn được dùng nữa. Mô hình sự kiện được Observer và Observable hỗ trợ khá hạn chế, thứ tự thông báo do Observable gửi không được chỉ định và các thay đổi trạng thái không tương ứng một đối một với thông báo. Để có mô hình sự kiện phong phú hơn, hãy cân nhắc sử dụng gói java.beans. Để gửi tin nhắn theo thứ tự và đáng tin cậy giữa các luồng, hãy cân nhắc sử dụng một trong các cấu trúc dữ liệu đồng thời trong gói java.util.concurrent. Để biết cách lập trình kiểu luồng phản ứng, hãy xem Flow API. Vì vậy không cần thiết phải sử dụng chúng. Và bạn có thể sử dụng những mẫu khác để thay thế, nhưng bản chất của mẫu sẽ không thay đổi. Ví dụ: hãy thử sử dụng PropertyChangeListener (để không viết thêm các lớp đã được viết) từ gói java.beans. Hãy xem nó sẽ như thế nào: lớp chủ đề:
public class CatholicChurch {
    private String news;
    // используя support мы можем добавлять or удалять наших прихожан (слушателей)
    private PropertyChangeSupport support;

    public CatholicChurch() {
        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;
    }
}
và lớp người nghe:
public class Parishioner implements PropertyChangeListener {
    private String name;

    public Parishioner(String name) {
        this.name = name;
    }

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

    public void setNews(String news) {
        System.out.println(name + " узнал новость: " + news);
    }
}
Nếu chúng ta thực thi đoạn mã sau:
public static void main(String[] args) {
    CatholicChurch observable = new CatholicChurch();

    observable.addPropertyChangeListener(new Parishioner("Мартин Лютер"));
    observable.addPropertyChangeListener(new Parishioner("Жан Кальвин"));

    observable.setNews("Дева Мария имеет непорочное зачатие... булла Ineffabilis Deus... 8 декабря 1854 года Папа Пий IX");
    observable.setNews("Папа непогрешим... не всегда конечно, а только когда транслирует учение церкви ex cathedra... Первый Ватиканский собор 1869 год");
}
Chúng tôi nhận được kết quả sau:
Martin Luther được biết tin: Đức Trinh Nữ Maria Vô Nhiễm Nguyên Tội... Bull Ineffabilis Deus... Ngày 8 tháng 12 năm 1854 Đức Giáo Hoàng Piô IX John Calvin được biết tin: Đức Trinh Nữ Maria Vô Nhiễm Nguyên Tội... Bull Ineffabilis Deus ... Ngày 8 tháng 12 năm 1854 Giáo hoàng Pius IX Martin Luther biết được tin: Giáo hoàng không thể sai lầm... tất nhiên không phải lúc nào cũng vậy, nhưng chỉ khi ông truyền đi những lời dạy của nhà thờ ex cathedra... Công đồng Vatican I 1869 John Calvin đã biết tin tức: Giáo hoàng là không thể sai lầm... tất nhiên không phải lúc nào cũng vậy, nhưng chỉ khi ông phát sóng giáo huấn của nhà thờ ex cathedra... Công đồng Vatican I 1869
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION