JavaRush /จาวาบล็อก /Random-TH /แม่แบบผู้สังเกตการณ์

แม่แบบผู้สังเกตการณ์

เผยแพร่ในกลุ่ม
ดังที่ Gang of Four เขียน (อ้างอิงถึงหนังสือ “รูปแบบการออกแบบเชิงวัตถุ” โดยนักพัฒนาชั้นนำ 4 คน) จุดประสงค์ของรูปแบบนี้คือเพื่อกำหนดการพึ่งพาแบบหนึ่งต่อกลุ่มระหว่างวัตถุในลักษณะที่เมื่อ สถานะของวัตถุหนึ่งมีการเปลี่ยนแปลง ทุกสิ่งที่ขึ้นอยู่กับวัตถุนั้นจะได้รับแจ้งเกี่ยวกับสิ่งนี้และจะได้รับการอัปเดตโดยอัตโนมัติ รูปแบบนี้เรียกอีกอย่างว่า: ผู้อยู่ในอุปการะ (ผู้ใต้บังคับบัญชา) หรือ Publish-Subscribe (ผู้เผยแพร่ - ผู้สมัครสมาชิก) แต่ลองคิดดูโดยใช้ตัวอย่างของคริสตจักรคาทอลิก :) มีผู้ติดตามที่เชื่อในคำสอนของคริสตจักรแห่งนี้ เมื่อมีหลักคำสอนใหม่ๆ (หลักคำสอนบังคับ) และอื่นๆ ปรากฏขึ้น คนเหล่านี้ควรรู้เกี่ยวกับสิ่งเหล่านั้น แต่สิ่งนี้สามารถอธิบายในภาษาโปรแกรมโดยใช้รูปแบบนี้ได้อย่างไร? 1. เรามี “เสียงของคริสตจักร” (ตัวคริสตจักรเองหรือพระสันตปาปาเมื่อออกอากาศนอกโบสถ์) นั่นคือผู้ประกาศข่าวหรือหัวเรื่องที่ประกาศข่าวในคริสตจักร 2. มีนักบวชในคริสตจักรนี้ คือ ผู้สังเกตการณ์บางคนที่ต้องการทราบเหตุการณ์สำคัญๆ ดังนั้นวันนี้อาจมีนักบวช 1.3 พันล้านคน และพรุ่งนี้อาจมีไม่มากก็น้อย และคุณเพียงแค่ต้องแจ้งผู้ที่อยู่ในคริสตจักรนี้เท่านั้น (ไม่จำเป็นต้องไปรบกวนผู้ที่ไม่เชื่อพระเจ้าอีกต่อไป :) ดังนั้นทั้งหมดนี้จึงสามารถแสดงได้ดังนี้: มีคริสตจักรแห่งหนึ่งที่จะบอกฝูงแกะเกี่ยวกับบางสิ่งบางอย่างซึ่งคุณสามารถลงทะเบียนหรือปล่อยไว้ในทางตรงกันข้าม:
public interface Church {
    void registerParishioner(Parishioner parishioner);
    void removeParishioner(Parishioner parishioner);
    void notifyParishioners();
}
มีคริสตจักรคาทอลิกบางแห่งที่นำวิธีการเหล่านี้ไปใช้ ตลอดจนข่าวสารและรายชื่อบุคคลที่ควรเผยแพร่ข่าวนี้:
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);
    }
}
มีนักบวชที่สามารถเข้าหรือออกจากโบสถ์ได้ (เพื่อให้รหัสง่ายขึ้นเราจะอนุญาตให้เขาเข้าเท่านั้น :)
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);
    }
}
มันจะทำงานอย่างไร:
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 года");
    }
}
และผลลัพธ์ของโปรแกรม:
Мартин Лютер узнал новость: Инквизиция была ошибкой... месса Mea Culpa 12 марта 2000 года
Жан Кальвин узнал новость: Инквизиция была ошибкой... месса Mea Culpa 12 марта 2000 года
เหล่านั้น. ทันทีที่มีข่าวปรากฏในคริสตจักร ทุกคนที่อยู่ในกลุ่มสมาชิกที่ลงทะเบียนของคริสตจักรนี้จะได้รับแจ้งเกี่ยวกับเรื่องนี้ อะไรคือข้อเสียของการดำเนินการนี้: 1. ประการแรก อินเทอร์เฟซที่คุณสามารถลงทะเบียนและรับข่าวสารอาจไม่เกี่ยวข้องกับคริสตจักรแห่งนี้เท่านั้น (อาจจำเป็น) ดังนั้นจึงเป็นไปได้ที่จะย้ายสิ่งนี้ไปยังอินเทอร์เฟซที่สังเกตได้แยกต่างหากทันที 2. เช่นเดียวกันนี้สามารถทำได้กับนักบวชในคริสตจักร กล่าวคือ ย้ายวิธีการอัพเดตไปยังอินเทอร์เฟซที่แยกจากกัน และนำไปใช้กับนักบวชที่ต้องการ จากนั้นวิธีนี้จะสามารถใช้ได้โดยทั่วไปไม่ใช่โดยนักบวชของคริสตจักรคาทอลิก แต่โดยผู้ที่อาศัยอยู่ในการดำรงอยู่ของเอลฟ์ (หมายถึงขบวนการ "ถนนสู่ยูนิคอร์น") เหล่านั้น. สร้างอินเทอร์เฟซผู้สังเกตการณ์ด้วยวิธีการอัปเดต จะเกิดอะไรขึ้นในที่สุด:
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);
    }
}
ดังนั้น: เราได้ "ลดการเชื่อมต่อ" ระหว่างคริสตจักรและนักบวชซึ่งโดยธรรมชาติแล้วจะดีเฉพาะในการเขียนโปรแกรมเท่านั้น :) หัวข้อ (คริสตจักรคาทอลิก) มีเพียงรายชื่อผู้ฟัง (นักบวช) และเมื่อได้รับข่าว (การเปลี่ยนแปลง) ออกอากาศข่าวนี้ แก่ผู้ฟัง ตอนนี้คุณสามารถสร้างองค์กรอื่นๆ (เช่น โบสถ์โปรเตสแตนต์) และเผยแพร่ข่าวไปยังผู้ฟัง "ของคุณ" คุณต้องคำนึงด้วยว่าข้อมูลคลาส 2 (แม่นยำยิ่งขึ้นคือคลาส Observable และอินเทอร์เฟซ Observer) มีอยู่ในแพ็คเกจ java.util java แต่ตอนนี้ข้อมูลเหล่านั้นเลิกใช้แล้วด้วย java 9 (https://docs.oracle com/en/java/javase/15/ docs/api/java.base/java/util/Observable.html): เลิกใช้แล้ว คลาสนี้และอินเทอร์เฟซผู้สังเกตการณ์เลิกใช้แล้ว โมเดลเหตุการณ์ที่ Observer และ Observable รองรับนั้นค่อนข้างจำกัด ลำดับของการแจ้งเตือนที่ส่งโดย Observable ไม่ได้ระบุ และการเปลี่ยนแปลงสถานะไม่ได้อยู่ในการโต้ตอบแบบตัวต่อตัวกับการแจ้งเตือน หากต้องการโมเดลเหตุการณ์ที่สมบูรณ์ยิ่งขึ้น ให้พิจารณาใช้แพ็กเกจ java.beans สำหรับการส่งข้อความที่เชื่อถือได้และเป็นระเบียบระหว่างเธรด ให้พิจารณาใช้หนึ่งในโครงสร้างข้อมูลที่เกิดขึ้นพร้อมกันในแพ็คเกจ java.util.concurrent สำหรับการเขียนโปรแกรมสไตล์สตรีมเชิงโต้ตอบ โปรดดู Flow API ดังนั้นจึงไม่จำเป็นต้องใช้มัน และคุณสามารถใช้สิ่งอื่นแทนได้ แต่แก่นแท้ของรูปแบบจะไม่เปลี่ยนแปลง ตัวอย่างเช่น ลองใช้ PropertyChangeListener (เพื่อไม่ให้เขียนคลาสที่ไม่จำเป็นซึ่งได้ถูกเขียนไปแล้ว) จากแพ็คเกจ java.beans มาดูกันว่ามันจะเป็นอย่างไร: วิชา:
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;
    }
}
และคลาสผู้ฟัง:
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);
    }
}
หากเรารันโค้ดต่อไปนี้:
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 год");
}
เราได้รับผลลัพธ์ดังต่อไปนี้:
มาร์ติน ลูเทอร์ ทราบข่าว: พระแม่มารีเป็นผู้ปฏิสนธินิรมล... วัว Ineffabilis Deus... 8 ธันวาคม พ.ศ. 2397 สมเด็จพระสันตะปาปาปิอุสที่ 9 จอห์น คาลวินทราบข่าว: พระแม่มารีเป็นผู้ปฏิสนธินิรมล... กระทิง Ineffabilis Deus ... 8 ธันวาคม 1854 สมเด็จพระสันตะปาปาปิอุสที่ 9 มาร์ติน ลูเทอร์ ทราบข่าว: สมเด็จพระสันตะปาปาไม่มีข้อผิดพลาด... ไม่แน่นอนเสมอไป แต่เฉพาะเมื่อเขาถ่ายทอดคำสอนของคริสตจักร อดีตมหาวิหาร... สภาวาติกันครั้งแรก พ.ศ. 2412 จอห์น คาลวิน เรียนรู้ ข่าว: สมเด็จพระสันตะปาปาไม่มีข้อผิดพลาด... ไม่แน่นอนเสมอไป แต่เฉพาะเมื่อเขาถ่ายทอดคำสอนของคริสตจักร อดีตมหาวิหาร... สภาวาติกันครั้งแรก 1869
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION