Три способа реализации шаблона наблюдателя и модели программирования событий в Spring

Java

Можно сказать, что шаблон наблюдателя является одним из самых простых для понимания шаблонов проектирования среди множества шаблонов проектирования. Шаблон наблюдателя можно увидеть повсюду в Spring. Во время интервью интервьюер может спросить: «Эй, раз уж вы читали источник Spring». код, то вы говорите о шаблонах проектирования, используемых в Spring, вы можете с уверенностью сказать ему, что ApplicationListener в Spring использует шаблон наблюдателя.

Давайте рассмотрим это шаг за шагом. Прежде всего, нам нужно знать, что такое шаблон наблюдателя и как его реализовать в Java. Здесь я реализую шаблон наблюдателя тремя способами.

Что такое шаблон наблюдателя

В реальной жизни шаблон наблюдателя можно увидеть повсюду, например,

  • Просмотр новостей, пока новости начинают воспроизводиться, новости будут доведены до пользователей, которые подписались на новости.Здесь новость — это [наблюдаемый], а пользователь — [наблюдатель].

  • Публичная учетная запись WeChat, если пользователь подписывается на публичную учетную запись, то он будет получать сообщения от публичной учетной записи, тогда публичная учетная запись — это [наблюдаемый], а пользователь — [наблюдатель].

  • Водонагреватель, предполагая, что водонагреватель состоит из трех частей, водонагреватель, сигнализация и дисплей, водонагреватель отвечает только за кипячение воды.Когда температура воды достигает установленной температуры, сигнал тревоги будет уведомлен, и сигнализация будет звучать сигнал тревоги.Дисплей также должен подписаться на событие нагрева воды водонагревателя.таким образом получая температуру воды и отображая ее. Водонагреватель — [наблюдаемый], сигнализация и дисплей — [наблюдатель].

Здесь видно, что [наблюдатель] утратил право на автономию и может лишь пассивно получать события от [наблюдаемого] и не может активно наблюдать. [Наблюдатель] стал «шоу», а [наблюдаемый] стал «атакой». [Наблюдаемый] просто уведомляет [наблюдателя], и ему все равно, какие действия [наблюдатель] выполнит после получения уведомления.

В режиме дизайна «наблюдаемое» называется «темой».

В шаблоне проектирования наблюдателя обычно выделяют четыре роли:

  • Абстрактная роль субъекта (Subject)
  • БетонТема
  • Абстрактная роль наблюдателя (наблюдатель)
  • Роль конкретного наблюдателя (ConcreteObserver)

Среди них [Subject] должен иметь поле списка для сохранения ссылки на [Observer], предоставить два метода (виртуальные методы), а именно [Delete Observer] [Add Observer], а также должен предоставить один для вызова клиентом. способ уведомить каждого [наблюдателя]: события, о которых вы заботитесь (подпишитесь), были отправлены вам.

Ниже я использую три способа реализации шаблона наблюдателя.

классический

public class News {
    private String title;
    private String content;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

Этот класс не является необходимым для шаблона наблюдателя и используется для хранения информации о событиях.

public interface Subject {
    List<People> peopleList = new ArrayList<>();

    default void add(People people) {
        peopleList.add(people);
    }

    default void remove(People people) {
        peopleList.remove(people);
    }

    void update();
}

Роль абстрактной темы, в этой роли есть поле peopleList, которое используется для сохранения ссылки [observer] и одновременно определяет два интерфейса, что является способом написания реализации интерфейса по умолчанию в Java8. Эти два интерфейса вызываются клиентом для [удаления наблюдателей] [добавления наблюдателей], а также предоставляют метод, который необходимо переопределить [конкретными ролями субъекта] для уведомления каждого [наблюдателей].

public class NewsSubject implements Subject{
    public void update() {
        for (People people : peopleList) {
            News news = new News();
            news.setContent("今日在大街上,有人躲在草丛中袭击路人,还大喊“德玛西亚万岁”");
            news.setTitle("德玛西亚出现了");
            people.update(news);
        }
    }
}

Для определенных ролей субъекта метод [Абстрактная роль субъекта] переписан, и список зациклен, чтобы уведомить каждого [Наблюдателя].

public interface People {
    void update(News news);
}

Роль абстрактного наблюдателя определяет интерфейс, а [конкретная роль наблюдателя] должна переопределить этот метод.

Ниже приведена [конкретная роль наблюдателя]:

public class PeopleA implements People {
    @Override
    public void update(News news) {
        System.out.println("这个新闻真好看");
    }
}
public class PeopleB implements People {
    @Override
    public void update(News news) {
        System.out.println("这个新闻真无语");
    }
}
public class PeopleC implements People {
    @Override
    public void update(News news) {
        System.out.println("这个新闻真逗");
    }
}

Клиент:

public class Main {
    public static void main(String[] args) {
        Subject subject = new NewsSubject();
        subject.add(new PeopleA());
        subject.add(new PeopleB());
        subject.add(new PeopleC());
        subject.update();
    }
}

бегать:

image.png

Когда мы изучаем шаблоны проектирования, мы должны знать преимущества и недостатки шаблонов проектирования, так каковы преимущества и недостатки шаблонов проектирования наблюдателя?

преимущество:

  • [Субъект] и [Наблюдатель] устанавливают слабосвязанные отношения через абстракцию.[Субъект] знает только, какие [Наблюдатели] существуют в данный момент, и отправляет уведомления, но не знает, какие конкретные действия [Наблюдатель] будет выполнять. Это тоже очень понятно.Например, публичный аккаунт WeChat проталкивает сообщение, и он не знает, какое действие вы предпримете, открыть ли его с улыбкой, или открыть его с гневом, или удалить сообщение напрямую, или кинуть телефон в щетке для стиральной машины.

  • В соответствии с принципом открытого-закрытого, если вам нужно добавить [наблюдателя], вам нужно только написать класс для реализации [роли абстрактного наблюдателя] без изменения исходного кода.

недостаток:

  • Клиент должен знать всех [наблюдателей] и выполнять операции [добавления наблюдателей] и [удаления наблюдателей].

  • Если [наблюдателей] много, уведомление всех [наблюдателей] может занять много времени.

Конечно, вышеперечисленные преимущества и недостатки являются наиболее интуитивно понятными, и их легко понять и испытать на себе. Другие преимущества и недостатки вы можете Baidu.

Lambda

Прежде чем вводить этот способ письма, необходимо представитьфункциональный интерфейс,Концепция функционального интерфейса имеет долгую историю. Вообще говоря, интерфейс, который определяет только один виртуальный метод, называется функциональным интерфейсом. В Java 8 появление лямбда-выражений делает функциональные интерфейсы яркими.

Нам просто нужно изменить код клиента на:

    public static void main(String[] args) {
        Subject subject = new NewsSubject();
        subject.add(a -> System.out.println("已阅这新闻"));
        subject.add(a -> System.out.println("假的吧"));
        subject.add(a -> System.out.println("昨天就看过了"));
        subject.update();
    }

результат операции:

image.png

Используя лямбда-выражения и функциональные интерфейсы, определение [конкретная роль наблюдателя] можно опустить, но лично я считаю, что это не относится к режиму наблюдателя в строгом смысле, и недостатки очевидны:

  • Клиент должен знать конкретную реализацию наблюдателя.
  • Если конкретная реализация наблюдателя сложнее, код может быть не таким понятным.

Таким образом, этот способ записи имеет определенные ограничения.

Занять слово у Бога

Появление шаблонов проектирования призвано компенсировать недостатки языка.

Именно в связи с обновлением языка некоторые шаблоны проектирования претерпели определенные изменения.Помимо шаблона наблюдателя, есть также шаблон метода шаблона и шаблон цепи ответственности, все они претерпели некоторые изменения из-за внешний вид лямбда-выражений.

JDK

В Java предоставляется интерфейс: Observer, подкласс: Observable, где Observer означает [наблюдатель], Observable означает [субъект], и эти два подкласса и интерфейса можно использовать для реализации шаблона наблюдателя:

public class NewsObservable extends Observable {
    public void update() {
       setChanged();
       notifyObservers();
    }
}
public class People1 implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        System.out.println("小编真无聊");
    }
}
public class People2 implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        System.out.println("开局一张图,内容全靠编");
    }
}

Клиент:

  public static void main(String[] args) {
        NewsObservable newsObservable = new NewsObservable();
        newsObservable.addObserver(new People1());
        newsObservable.addObserver(new People2());
        newsObservable.update();
    }

результат операции:

image.png

Здесь я не собираюсь подробно вводить этот метод реализации, потому что Java не рекомендует этот способ записи, начиная с Java9, и для его реализации рекомендуется использовать очередь сообщений. Вы очень рады, что нашли предлог не изучать Observable и Observer.

Модель программирования событий в Spring

Модель программирования событий в Spring является реализацией шаблона наблюдателя SpringBoot использует модель программирования событий Spring для выполнения некоторых операций, которые пока не перечислены здесь.

Интерфейс ApplicationListener определен в Spring. Из названия мы знаем, что это слушатель и слушает события приложения. Тогда что такое приложение? Это ApplicationContext. ApplicationContext имеет несколько встроенных событий.

  • ContextRefreshedEvent
  • ContextStartedEvent
  • ContextStoppedEvent
  • ContextClosedEvent

Из названия вы знаете, когда запускаются эти события.

Ниже я продемонстрирую конкретное использование, например, я хочу прослушать событие ContextRefreshedEvent и, если событие произойдет, вывести предложение.

@Component
public class MyListener implements ApplicationListener{
    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        if(applicationEvent  instanceof ContextRefreshedEvent){
            System.out.println("刷新了");
        }
    }
}
@Configuration
@ComponentScan
public class AppConfig {
}
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(AppConfig.class);
    }

результат операции:

image.png

В то время я изучал Spring, и, видя, что Spring предоставляет множество интерфейсов, позволяющих программистам расширять Spring без какого-либо вмешательства, я должен восхищаться разработчиками Spring. Здесь также мы видим, что мы не можем найти никакой тени о «подписке на события» на стороне клиента.

Эта реализация не очень хороша, вы можете видеть, что мы сделали суждение внутри метода: является ли полученное событие ContextRefreshedEvent.

Великий Spring также предоставляет общий ApplicationListener, мы можем использовать общий ApplicationListener для улучшения приведенного выше кода:

@Component
public class MyListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("刷新了");
    }
}

Мы также можем использовать модель программирования событий Spring для настройки событий и публикации событий:

Во-первых, нам нужно определить событие для реализации интерфейса ApplicationEvent, что означает, что это событие Application.Фактически четыре встроенных события, упомянутых выше, также реализуют интерфейс ApplicationEvent:

public class MyEvent extends ApplicationEvent {
    public MyEvent(Object source) {
        super(source);
    }
}

Вам также необходимо определить прослушиватель.Конечно, вам нужно прослушивать событие MyEvent здесь:

@Component
public class MyListener implements ApplicationListener<MyEvent> {
    @Override
    public void onApplicationEvent(MyEvent event) {
        System.out.println("我订阅的事件已经到达");
    }
}

Теперь, когда есть события и слушатели, не хватает ли издателей или тех, кто будет публиковать события?

@Component
public class MyEventPublish implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher publisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }

    public void publish(Object obj) {
        this.publisher.publishEvent(obj);
    }
}

Издатель должен реализовать интерфейс ApplicationEventPublisherAware и переписать метод публикации. Как следует из названия, это метод публикации, так что же такое параметр obj метода? Как издатель, вы должны знать, какое событие я хочу опубликовать, и источник события (кто срабатывает), этот объект используется для хранения таких данных, естественно, этот параметр нужно передавать вручную. setApplicationEventPublisher активно вызывается внутри Spring, что можно просто понимать как инициализацию издателя.

Теперь осталась только одна роль, есть слушатели, издатели и события.Да, верно, одним триггером меньше.Ведь должны быть триггеры для запуска событий:

@Component
public class Service {

    @Autowired
    private  MyEventPublish publish;

    public void publish() {
        publish.publish(new MyEvent(this));
    }
}

Метод публикации вызывается у клиента, чтобы инициировать событие.Вы можете ясно видеть, что новое MyEvent(this) передается, чтобы издатель мог знать, какое событие я хочу инициировать и кто инициировал событие.

Разумеется, вам также нужно передать все управление Spring:

@Configuration
@ComponentScan
public class AppConfig {
}

Клиент:

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(AppConfig.class);
        context.getBean(Service.class).publish();;
    }

результат операции:

image.png

Этот блог относительно прост, простое приложение, но только после приложения можно говорить об исходном коде.

На этом блог заканчивается, всем спасибо.