【问题标题】:Java listener implementationJava 监听器实现
【发布时间】:2020-09-04 03:20:29
【问题描述】:

为以下用例寻找最佳设计。

用例: 在我的应用程序中,我有一个调度程序每隔一小时运行一次并公开一项服务。

调度程序每隔一小时运行一次,从特定表中检索记录,并在任何记录处理失败时发送通知。通知可以是电子邮件或短信。

此外,此服务可以被其他服务调用以通知记录处理失败(电子邮件或短信)。

失败类型为 batchFailure 或 serviceFailure。

此外,还有一些道具设置类可以启用或禁用短信和电子邮件通知。

public class Settings {
  private boolean emailEnabled;
  private boolean smsEnabled;
}

根据类型,电子邮件主题应包含“批量失败”或“服务失败”的主题。批处理作业和服务失败的其他内容保持不变。

我创建了以下类:-

  1. 主题 - 我为两者创建了一个共同的主题。

    public class Notification {
    
          private int recordId;
    
          private String recordName;
    
          private String email;  // Related to email
    
          private String smsNumber; // Related to sms
    
    }
    
  2. 监听类

    public interface RecordFailureListener {
    
      void sendNotification(Notification notification);
    
    }
    
  3. 电子邮件监听器

    public class EmailListener implements RecordFailureListener {
    
       void sendNotification(Notification notification) {
        // Code to send email here
       }
    }
    
  4. 短信监听器

    public class SMSListener implements RecordFailureListener {
    
       void sendNotification(Notification notification) {
        // Code to send SMS here
       }
    }
    
  5. 服务类

    public class NotificationService {
       List<RecordFailureListener> listeners = new ArrayList(); // This list has emailListener and smsListener
    
       void sendNotification(Notification notification) {
           listeners.forEach(listener -> listener.sendNotification(notification));
       }
    }
    

正在从调度程序调用此通知服务以应对任何故障。此外,从暴露的失败服务。

问题:

1) 在这里,主题似乎具有电子邮件和短信所需的属性。有没有其他更好的方法让电子邮件通知具有电子邮件所需的属性,而短信通知将具有短信所需的属性?还有一些共同的属性。

2) 在调用发送电子邮件之前检查电子邮件侦听器中是否启用了电子邮件,在短信侦听器中也是如此。这是正确的地方吗?

还有其他更好的设计吗?

谢谢

【问题讨论】:

    标签: java oop design-patterns


    【解决方案1】:

    你的问题是典型的通知者/监听者问题。 我建议也使用事件总线,作为解耦lisetner 和通知程序的一种方式。这个想法是通知器将通知事件总线,事件总线将遍历侦听器并在通知时更新它们。设计如下: (这是一个简单的骨架,有一些方便的捷径,例如为 EventBus 使用枚举单例。细节取决于您)。

    public interface Event {
        // general event information - date, message, meta data, etc....
    }
    public interface Listener { 
       public void listen(Event event);
    }
    
    public enum EventBus {
       INSTANCE;
    
       private List<Listener> listeners = new ArrayList<>;
    
       public void addListener(Listener newListener) {
           listeners.add(newListener);
       }
    
       public void notify(Event event) {
          for (Listener listener : listeners) {
             listener.listen(event);
          }
       }
    }
    
    
    public class Notifier {
    
        public void handlePointOfInterest(.....) {
            // do something interesting with the parameters, etc...
            Event event = // create a new event based on your needs
            EventBus.INSTANCE.notify(event);
            ...
        }
    }
    

    您的事件接口也可以使用泛型来支持特定的元数据类型 - 例如您的设置。 您的 Listener 实现可以使用泛型来处理特定类型的事件。

    希望这会有所帮助,请随时要求澄清。

    【讨论】:

    • 这是什么已经实现了?当在 for 循环中调用侦听器时,我如何拥有不同的主题,但电子邮件侦听器需要电子邮件相关属性,而工单侦听器需要与工单相关的属性。
    • @user1578872:属性应该是您的事件的属性。此外,如果您在事件接口中使用泛型,您还可以将特定事件绑定到特定侦听器。就像我写的那样,这里发布的设计是一个骨架,可以添加很多内容以微调和处理特定需求。
    • 我对使用泛型为不同的侦听器发送事件感到困惑。你能说清楚这部分吗?
    • @user1578872:当然。这个想法是通过 YourSpecificEvent 类实现特定事件。然后,您可以创建一个专用的侦听器系列,该系列旨在仅处理此类事件 - 例如,SpecificEventListener 和 SubhandlingEventListener 侦听器。使用泛型背后的想法是将侦听器绑定到特定的事件类型。事件侦听器的实现对于每个侦听器都是唯一的。这只是处理事件到监听器绑定问题的一种方法,它不是必需品。
    【解决方案2】:

    只是分享我的设计模式,不过是个老问题。

    可以在 NotificationService 本身中验证设置,因为这是我们开始通知流程的地方。假设正在从调度程序调用 NotificationService 以进行失败通知

    public class NotificationService {
    
        Notification notification;
    
       static List<RecordFailureListener> listenerList = new ArrayList<>(); // holds email & sms listeners
    
        public NotificationService (Notification notification){
            this.notification=notification;
        }
    
        public void sendNotification(){
    
            if(Settings.emailEnabled){
                listenerList.stream().filter(i-> i instanceof EmailListener).forEach(i->i.sendNotification(notification));
            }
            if(Settings.smsEnabled){
                listenerList.stream().filter(i-> i instanceof SmsListener).forEach(i->i.sendNotification(notification));
            }
    
        }
    }
    

    RecordeFailureListener

    public interface RecordFailureListener {
    
        void sendNotification(Notification notification);
    }
    

    EmailListener(FailureType 可以在此处验证以分配相应的电子邮件主题行)

    public class EmailListener implements RecordFailureListener {
    
        private String emailSubject;//emailistener specific property
        @Override
        public void sendNotification(Notification notification) {
    
            if(Stream.of("BatchFailure","ServiceFailure").anyMatch(notification.getFailureType()::equalsIgnoreCase)){
                emailSubject= notification.getFailureType();
            }
            else{
                emailSubject="customSubject";
            }
    
            //code to send email notification
    
        }
    }
    

    短信监听器

    public class SmsListener implements  RecordFailureListener {
        @Override
        public void sendNotification(Notification notification) {
            //code to sms notification
        }
    }
    

    设置

    public class Settings {
    
         public static boolean emailEnabled;
         public static boolean smsEnabled;
    }
    

    通知模型(已添加 failureType 作为 Notification 属性)

     public class Notification {
    
        private int recordId;
        private String recordName;
        private String email;
        private String smsnumber;
        private String failureType;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-03-24
      • 2011-08-21
      • 2015-10-07
      • 2011-02-02
      • 1970-01-01
      • 2011-06-22
      • 2023-03-12
      相关资源
      最近更新 更多