【问题标题】:Creating different POJO child objects depending on criteria but share common fields根据条件创建不同的 POJO 子对象,但共享公共字段
【发布时间】:2018-02-14 06:56:56
【问题描述】:

我正在实施一个日志管理系统,并希望日志类型可以扩展。我们得到一个从 JSON(来自 Filebeat)解析的基础对象,例如:

class LogLine {
   String message
   Date timestamp
   String type
   String source
}

给定这个 LogLine 对象,我希望能够创建不同的对象,这些对象也将扩展这个 LogLine。

class SomeLog extends LogLine {
    int myfield
    String username
}

class SomeOtherLog extends LogLine {
   Date startTime
   Date endTime
   String username
   String transactionID
}

所以,在我目前的非理想实现中:

void parse(String s){
   LogLine logLine = .....parseFromString(s)
   if ( logline.type.equals('def') ){
      SomeLog someLog = new SomeLog.Builder.build(logLine)
   } else if ( logline.message.containts('abc') ){
      SomeOtherLog someotherLog = new SomeOtherLog.Builder.build(logline)
   }
}

但是,正如您可以想象的那样,子类中的构建器复制超类 LogLine 对象,无论如何我可以在不复制值的情况下做到这一点,因为它们已经是子类?有没有设计模式来实现这一点?我不想依赖像BeanUtils.copyProperperties这样的反射

【问题讨论】:

    标签: java design-patterns polymorphism


    【解决方案1】:

    当您基于另一个对象创建新对象时,最好复制所有字段。这是一种称为defensive copying 的最佳实践。

    由于您解析字符串,因此不需要防御性副本。另外我想你会想从输入字符串中解析一些特定的字段,比如myfield 用于SomeLogstartDate 用于SomeOtherLog。您可以像

    那样重构对象创建
    LogLine result = null;
    if (s.contains('type=def') { 
      result = SomeLog.parse(s);
    } else if (trickyRegexp.mathces(s)) {
       result = SomeOtherLog.parse(s);
    } else {
       result = LogLine.parse(s);
    }
    

    如果您有许多 LogLine 的子类,那么您可能希望将创建逻辑移至 LogFactory,该 LogFactory 管理有关将字符串解析为特定对象的所有内容。

    【讨论】:

      【解决方案2】:

      引入用于创建LogLine 对象的工厂接口。

      public interface LogLineFactory {
          public LogLine createLog(LogLine logLine);
      }
      

      并使用Map 进行查找。

      private Map<String, LogLineFactory > logLineFactories = new HashMap<>();
      
      {
          logLineFactories .put("def", new SomeLogFactory());
          logLineFactories .put("abc", new SomeOtherLogFactory());
      }
      

      然后您可以使用映射循环省略 if else 分支。

      LogLine logLine = parseFromString(s);
      LogFactory logFactory = logLineFactories.get(logLine.type);
      
      if(logFactory != null) {
          LogLine wrappedLogLine = logFactory.createLog(logLine);
      }
      

      也许您需要更多信息来创建LogLines,并且您必须更改界面。

      public interface LogLineFactory {
          public LogLine createLog(LogLine logLine, String s);
      }
      

      PS:对于 Java 8,您可能希望使用方法引用。

      logLineFactories.put("def", SomeLog::new);
      logLineFactories.put("abc", SomeOtherLog::new);    
      

      【讨论】:

      • 使用Map 代替if-then-else if 模式的想法很棒。但是 'abc' 子字符串来自不同的字段,因此您必须改进解决方案。
      猜你喜欢
      • 1970-01-01
      • 2021-09-05
      • 2017-04-08
      • 2020-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-29
      相关资源
      最近更新 更多