JavaRush /בלוג Java /Random-HE /תבנית צופה

תבנית צופה

פורסם בקבוצה
כפי שכותבת כנופיית הארבעה (בהתייחסות לספר "דפוסי עיצוב מונחה אובייקט" מאת 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. ניתן לעשות את אותו הדבר עם חברי הקהילה, כלומר להעביר את שיטת העדכון לממשק נפרד וליישם אותה עבור חבר הקהילה הרצוי. אז שיטה זו תוכל לשמש באופן כללי לא על ידי חברי הקהילה של הכנסייה הקתולית, אלא, למשל, על ידי אלה שחיים בקיומם של אלפים (כלומר תנועת "הדרך לחד הקרן"). הָהֵן. ליצור ממשק Observer עם שיטת עדכון. מה יקרה בסוף:
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 9 (https://docs.oracle. com/en/java/javase/15/ docs/api/java.base/java/util/Observable.html): הוצא משימוש. מחלקה זו וממשק Observer הוצאו משימוש. מודל האירועים הנתמכים על ידי 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 בדצמבר 1854 האפיפיור פיוס התשיעי יוחנן קלווין למד את החדשות: מריה הבתולה היא בת התעברות ללא רבב... שור Ineffabilis Deus ... 8 בדצמבר 1854 האפיפיור פיוס התשיעי מרטין לותר למד את החדשות: האפיפיור אינו בר טעות... לא תמיד כמובן, אלא רק כשהוא משדר את תורתו של הכנסייה לשעבר הקתדרה... מועצת הוותיקן הראשונה 1869 למד ג'ון קלווין החדשות: האפיפיור אינו בר טעות... לא תמיד כמובן, אלא רק כשהוא משדר הוראה של הכנסייה לשעבר הקתדרה... מועצת הוותיקן הראשונה 1869
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION