Асинхронное уведомление и обратный вызов события на основе API Guava

Java задняя часть

Эта статья взята из статьи «Вот как следует изучать шаблоны проектирования».

1 Реализовать механизм уведомления на основе Java API

Когда друзья задают вопросы в сообществе, если назначенный пользователь настроен на ответ, соответствующий пользователь получит уведомление по электронной почте, что является сценарием применения режима наблюдателя. Некоторые мелкие партнеры могут подумать о MQ, асинхронной очереди и т. д. На самом деле JDK сам предоставляет такой API. Мы используем код для восстановления такого сценария приложения, сначала создадим класс GPer.


/**
 * JDK提供的一种观察者的实现方式,被观察者
 */
public class GPer extends Observable{
    private String name = "GPer生态圈";
    private static GPer gper = null;
    private GPer(){}

    public static GPer getInstance(){
        if(null == gper){
            gper = new GPer();
        }
        return gper;
    }
    public String getName() {
        return name;
    }
    public void publishQuestion(Question question){
        System.out.println(question.getUserName() + "在" + this.name + "上提交了一个问题。");
        setChanged();
        notifyObservers(question);
    }
}

Затем создайте класс вопросов.


public class Question {
    private String userName;
    private String content;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getContent() {
        return content;
    }

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

Затем создайте класс Учитель.


public class Teacher implements Observer {

    private String name;

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

    public void update(Observable o, Object arg) {
        GPer gper = (GPer)o;
        Question question = (Question)arg;
        System.out.println("======================");
        System.out.println(name + "老师,你好!\n" + 
"您收到了一个来自" + gper.getName() + "的提问,希望您解答。问题内容如下:\n" +
                   question.getContent() + "\n" + "提问者:" + question.getUserName());
    }
}

Наконец, напишите тестовый код клиента.


    public static void main(String[] args) {
        GPer gper = GPer.getInstance();
        Teacher tom = new Teacher("Tom");
        Teacher jerry = new Teacher("Jerry");

        gper.addObserver(tom);
        gper.addObserver(jerry);

        //用户行为
        Question question = new Question();
        question.setUserName("张三");
        question.setContent("观察者模式适用于哪些场景?");

        gper.publishQuestion(question);
}

Результат работы показан на рисунке ниже.

file

2 Простая реализация режима наблюдателя на основе API Guava

Автор рекомендует очень простой в использовании фреймворк для реализации паттерна наблюдателя, а использование API тоже очень простое, например, впервые представлен пакет зависимостей Maven.


<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>20.0</version>
</dependency>

Затем создайте событие прослушивателя GuavaEvent.


/**
 * Created by Tom
 */
public class GuavaEvent {
    @Subscribe
    public void subscribe(String str){
        //业务逻辑
        System.out.println("执行subscribe方法,传入的参数是:" + str);
    }

}

Наконец, напишите тестовый код клиента.



/**
 * Created by Tom
 */
public class GuavaEventTest {
    public static void main(String[] args) {
        EventBus eventbus = new EventBus();
        GuavaEvent guavaEvent = new GuavaEvent();
        eventbus.register(guavaEvent);
        eventbus.post("Tom");
    }

}

3 Разработка API ответа на событие мыши с использованием шаблона наблюдателя

Давайте разработаем бизнес-сценарий, чтобы помочь друзьям лучше понять шаблон наблюдателя. В исходном коде JDK есть много применений шаблона наблюдателя. Например, java.awt.Event — это своего рода шаблон наблюдателя, но Java редко используется для написания настольных программ. Давайте реализуем его с помощью кода, чтобы помочь нашим друзьям глубже понять принцип реализации шаблона наблюдателя. Сначала создайте интерфейс EventListener.


/**
 * 观察者抽象
 * Created by Tom.
 */
public interface EventListener {

}

Создайте класс событий.



/**
 * 标准事件源格式的定义
 * Created by Tom.
 */
public class Event {
    //事件源,动作是由谁发出的
    private Object source;
    //事件触发,要通知谁(观察者)
    private EventListener target;
    //观察者的回应
    private Method callback;
    //事件的名称
    private String trigger;
    //事件的触发事件
    private long time;

    public Event(EventListener target, Method callback) {
        this.target = target;
        this.callback = callback;
    }

    public Object getSource() {
        return source;
    }

    public Event setSource(Object source) {
        this.source = source;
        return this;
    }

    public String getTrigger() {
        return trigger;
    }

    public Event setTrigger(String trigger) {
        this.trigger = trigger;
        return this;
    }

    public long getTime() {
        return time;
    }

    public Event setTime(long time) {
        this.time = time;
        return this;
    }

    public Method getCallback() {
        return callback;
    }

    public EventListener getTarget() {
        return target;
    }

    @Override
    public String toString() {
        return "Event{" +
                "source=" + source +
                ", target=" + target +
                ", callback=" + callback +
                ", trigger='" + trigger + '\'' +
                ", time=" + time +
                '}';
    }
}

Создайте класс EventContext.


/**
 * 被观察者的抽象
 * Created by Tom.
 */
public abstract class EventContext {
    protected Map<String,Event> events = new HashMap<String,Event>();

    public void addListener(String eventType, EventListener target, Method callback){
        events.put(eventType,new Event(target,callback));
    }

    public void addListener(String eventType, EventListener target){
        try {
            this.addListener(eventType, target, 
target.getClass().getMethod("on"+toUpperFirstCase(eventType), Event.class));
        }catch (NoSuchMethodException e){
            return;
        }
    }

    private String toUpperFirstCase(String eventType) {
        char [] chars = eventType.toCharArray();
        chars[0] -= 32;
        return String.valueOf(chars);
    }

    private void trigger(Event event){
        event.setSource(this);
        event.setTime(System.currentTimeMillis());

        try {
            if (event.getCallback() != null) {
                //用反射调用回调函数
                event.getCallback().invoke(event.getTarget(), event);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    protected void trigger(String trigger){
        if(!this.events.containsKey(trigger)){return;}
        trigger(this.events.get(trigger).setTrigger(trigger));
    }
}

Затем создайте интерфейс MouseEventType.


/**
 * Created by Tom.
 */
public interface MouseEventType {
    //单击
    String ON_CLICK = "click";

    //双击
    String ON_DOUBLE_CLICK = "doubleClick";

    //弹起
    String ON_UP = "up";

    //按下
    String ON_DOWN = "down";

    //移动
    String ON_MOVE = "move";

    //滚动
    String ON_WHEEL = "wheel";

    //悬停
    String ON_OVER = "over";

    //失去焦点
    String ON_BLUR = "blur";

    //获得焦点
    String ON_FOCUS = "focus";
}

Создание класса Мышь.


/**
 * 具体的被观察者
 * Created by Tom.
 */
public class Mouse extends EventContext {

    public void click(){
        System.out.println("调用单击方法");
        this.trigger(MouseEventType.ON_CLICK);
    }

    public void doubleClick(){
        System.out.println("调用双击方法");
        this.trigger(MouseEventType.ON_DOUBLE_CLICK);
    }

    public void up(){
        System.out.println("调用弹起方法");
        this.trigger(MouseEventType.ON_UP);
    }

    public void down(){
        System.out.println("调用按下方法");
        this.trigger(MouseEventType.ON_DOWN);
    }

    public void move(){
        System.out.println("调用移动方法");
        this.trigger(MouseEventType.ON_MOVE);
    }

    public void wheel(){
        System.out.println("调用滚动方法");
        this.trigger(MouseEventType.ON_WHEEL);
    }

    public void over(){
        System.out.println("调用悬停方法");
        this.trigger(MouseEventType.ON_OVER);
    }

    public void blur(){
        System.out.println("调用获得焦点方法");
        this.trigger(MouseEventType.ON_BLUR);
    }

    public void focus(){
        System.out.println("调用失去焦点方法");
        this.trigger(MouseEventType.ON_FOCUS);
    }
}

Создайте метод обратного вызова класса MouseEventListener.


/**
 * 观察者
 * Created by Tom.
 */
public class MouseEventListener implements EventListener {


    public void onClick(Event e){
        System.out.println("===========触发鼠标单击事件==========" + "\n" + e);
    }

    public void onDoubleClick(Event e){
        System.out.println("===========触发鼠标双击事件==========" + "\n" + e);
    }

    public void onUp(Event e){
        System.out.println("===========触发鼠标弹起事件==========" + "\n" + e);
    }

    public void onDown(Event e){
        System.out.println("===========触发鼠标按下事件==========" + "\n" + e);
    }

    public void onMove(Event e){
        System.out.println("===========触发鼠标移动事件==========" + "\n" + e);
    }

    public void onWheel(Event e){
        System.out.println("===========触发鼠标滚动事件==========" + "\n" + e);
    }

    public void onOver(Event e){
        System.out.println("===========触发鼠标悬停事件==========" + "\n" + e);
    }

    public void onBlur(Event e){
        System.out.println("===========触发鼠标失去焦点事件==========" + "\n" + e);
    }

    public void onFocus(Event e){
        System.out.println("===========触发鼠标获得焦点事件==========" + "\n" + e);
    }

}

Наконец, напишите тестовый код клиента.


    public static void main(String[] args) {
        EventListener listener = new MouseEventListener();

        Mouse mouse = new Mouse();
        mouse.addListener(MouseEventType.ON_CLICK,listener);
        mouse.addListener(MouseEventType.ON_MOVE,listener);

        mouse.click();
        mouse.move();
    }
		

Следуйте «Архитектуре Тома» и ответьте на «Шаблон проектирования», чтобы получить полный исходный код.

[Рекомендация] Архитектура бомбы Тома: 30 реальных случаев шаблонов проектирования (с исходным кодом), бросающих вызов годовой зарплате в 60 Вт — это не мечта

Эта статья является оригиналом "Архитектуры бомбы Тома", пожалуйста, указывайте источник при перепечатке. Технология заключается в обмене, я разделяю свое счастье! Если эта статья полезна для вас, пожалуйста, подпишитесь и поставьте лайк, если у вас есть какие-либо предложения, вы также можете оставить комментарий или личное сообщение Ваша поддержка является движущей силой для меня, чтобы продолжать творить. Обратите внимание на «архитектуру бомбы Тома», чтобы получить больше технической галантереи!