JavaRush /Blog Java /Random-ES /Plantilla de observador

Plantilla de observador

Publicado en el grupo Random-ES
Como escribe la Banda de los Cuatro (refiriéndose al libro "Patrones de diseño orientado a objetos" de 4 desarrolladores de primer nivel), el propósito de este patrón es definir una dependencia de uno a muchos entre objetos de tal manera que cuando el El estado de un objeto cambia, todos los que dependen de él son notificados sobre esto y se actualizan automáticamente. Este patrón también se llama: Dependientes (subordinados) o Publicar-Suscribir (editor - suscriptor). Pero intentemos resolverlo usando el ejemplo de la Iglesia Católica :) Tiene seguidores que creen en las enseñanzas de esta iglesia. Cuando aparecen nuevos dogmas (credos obligatorios) y más, estas personas deberían conocerlos. Pero, ¿cómo podría describirse esto en un lenguaje de programación usando este patrón? 1. Tenemos la “voz de la iglesia” (la propia iglesia o el Papa cuando transmite ex cathedra), es decir, un determinado locutor o sujeto que anuncia novedades en la iglesia. 2. Hay feligreses de esta iglesia, es decir, unos observadores que quieren estar al tanto de acontecimientos importantes. En consecuencia, hoy puede haber 1.300 millones de feligreses y mañana puede haber más o menos. Y solo necesitas avisar a los que están en esta iglesia (no hace falta volver a molestar a los ateos :). Así, todo esto podría expresarse de la siguiente manera: Hay una iglesia que le contará algo a su rebaño, en la cual podrá registrarse o, por el contrario, abandonarla:
public interface Church {
    void registerParishioner(Parishioner parishioner);
    void removeParishioner(Parishioner parishioner);
    void notifyParishioners();
}
Existe una Iglesia Católica específica con la implementación de estos métodos, así como noticias y una lista de personas a quienes se les debe transmitir esta noticia:
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);
    }
}
Hay un feligrés que puede entrar o salir de la iglesia (para simplificar el código, solo le permitiremos entrar :)
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);
    }
}
En consecuencia, así es como funcionará:
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 года");
    }
}
y el resultado del programa:
Мартин Лютер узнал новость: Инквизиция была ошибкой... месса Mea Culpa 12 марта 2000 года
Жан Кальвин узнал новость: Инквизиция была ошибкой... месса Mea Culpa 12 марта 2000 года
Aquellos. Tan pronto como aparezcan noticias en la iglesia, todos los que estén en el grupo de miembros registrados de esta iglesia serán notificados al respecto. ¿Cuáles son las desventajas de esta implementación? 1. En primer lugar, la interfaz donde puede registrarse y recibir noticias puede referirse no sólo a esta iglesia (esto puede ser necesario). Por lo tanto, sería posible mover esto inmediatamente a una interfaz Observable separada. 2. Se podría hacer lo mismo con los feligreses de la iglesia, es decir, mover el método de actualización a una interfaz separada e implementarlo para el feligrese deseado. Entonces este método podrá ser utilizado en general no por los feligreses de la Iglesia Católica, sino, por ejemplo, por aquellos que viven en la existencia de elfos (es decir, el movimiento "Camino al Unicornio"). Aquellos. cree una interfaz de Observer con un método de actualización. ¿Qué pasará al final?
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);
    }
}
Así: Hemos “debilitado la conexión” entre la iglesia y los feligreses, lo cual naturalmente es bueno solo en la programación :) El tema (iglesia católica) tiene solo una lista de oyentes (feligreses) y al recibir noticias (cambios), transmite esta noticia a sus oyentes. Ahora puedes crear cualquier otra entidad (por ejemplo, una iglesia protestante) y transmitir la noticia a "tus" oyentes. También hay que tener en cuenta que los datos de clase 2 (más precisamente, la clase Observable y la interfaz Observer) estaban disponibles en el paquete java java.util, pero ahora están en desuso con java 9 (https://docs.oracle. com/en/java/javase/15/docs/api/java.base/java/util/Observable.html): en desuso. Esta clase y la interfaz Observer han quedado obsoletas. El modelo de eventos admitido por Observer y Observable es bastante limitado, el orden de las notificaciones entregadas por Observable no está especificado y los cambios de estado no se corresponden uno por uno con las notificaciones. Para obtener un modelo de eventos más completo, considere utilizar el paquete java.beans. Para mensajes confiables y ordenados entre subprocesos, considere usar una de las estructuras de datos concurrentes en el paquete java.util.concurrent. Para programación de estilo de flujos reactivos, consulte la API Flow. Por tanto no es necesario utilizarlos. Y puedes usar otros en su lugar, pero la esencia del patrón no cambiará. Por ejemplo, intentemos utilizar PropertyChangeListener (para no escribir clases innecesarias que ya se han escrito) del paquete java.beans. Veamos cómo será: clase de materia:
public class CatholicChurch {
    private String news;
    // используя support мы можем добавлять o удалять наших прихожан (слушателей)
    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;
    }
}
y la clase de oyente:
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);
    }
}
Si ejecutamos el siguiente código:
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 год");
}
Obtenemos el siguiente resultado:
Martín Lutero se enteró de la noticia: La Virgen María es de la Inmaculada Concepción... bula Ineffabilis Deus... 8 de diciembre de 1854 El Papa Pío IX Juan Calvino se enteró de la noticia: La Virgen María es de la Inmaculada Concepción... Bula Ineffabilis Deus ... 8 de diciembre de 1854 El Papa Pío IX Martín Lutero se enteró de la noticia: El Papa es infalible... no siempre por supuesto, sino sólo cuando difunde las enseñanzas de la iglesia ex cathedra... Concilio Vaticano I 1869 Juan Calvino se enteró la noticia: El Papa es infalible... no siempre por supuesto, pero sólo cuando transmite la enseñanza de la iglesia ex cathedra... Concilio Vaticano I 1869
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION