java - 用于基于 Java 模板的字符串构造的工具

标签 java string templates construction

我正在尝试重构一些遗留代码。这里的任务是根据一些预定义的模板构造冗长的消息/字符串,如下所示:

field1,8,String
filed2,5,Integer
field3,12,String
......

然后我收到一个包含所有这些字段的 java 对象。这里需要做的只是从对象字段中获取数据,并使用它们根据模板构造一个长消息/字符串。其中一些字段也可以基于一些简单的规则进行转换。例如:

abc => a
def => d
ghi => g

因此,我们需要不时检查这些字段的值。还有关于填充的规则(主要是在右侧添加空白区域)。因此构造的消息/字符串可能如下所示:

uater   4751 enterprise  ......

目前我们只是使用残酷的武力来完成这项工作。首先,我们将模板输入到 ArrayList 中,每个元素都是一行,例如“field1,8,String”。在实际的消息构造过程中,我们循环遍历这个ArrayList,然后将数据填充到StringBuffer中。这是一些示例片段

StringBuffer message = new StringBuffer(1000);
for (String field : templateFields) {
    String[] fieldArray = field.split(Constants.SEPARATOR);
    if (fieldArray[0].equalsIgnoreCase(Constants.WORKFLOW)) {
        message.append(rightPad(object.getFieldOne(), Integer.parseInt(fieldArray[1])));
    } else if (fieldArray[0].equalsIgnoreCase(Constants.WORKVOLUME)) {
        message.append(rightPad(object.getFieldTwo(), Integer.parseInt(fieldArray[1]));
    } else if (fieldArray[0].equalsIgnoreCase(Constants.WORKTYPE)) {
        if (object.getFieldThree().equalsIgnoreCase("abc")) {
             message.append(rightPad("a", Integer.parseInt(fieldArray[1]));
        } else if (object.getFieldThree().equalsIgnoreCase("def")) {
             message.append(rightPad("d", Integer.parseInt(fieldArray[1]));
        } else {
            message.append(rightPad("g", Integer.parseInt(fieldArray[1]));
        }
    } else if ......
}

正如你所看到的,尽管它很可怕,但它完成了工作。但这样的代码容易出错,并且难以维护。我想知道你们是否有任何工具或库或一些优雅的解决方案可以推荐。
非常感谢!华

最佳答案

如果我正确理解你的问题,你有一种方法可以循环遍历可能的 templateFields 。那没有必要。

自从每 fieldArray[0]与一些Constants相比值,如果进一步处理匹配,我​​们可以将 for 循环替换为 Map 。它的键是可能的Constants值及其值是映射器。映射器是 BiFunction这需要 objectfieldArray[1] 的值并为这些返回类型为 String 的消息.

让我们从映射器开始:

public class FieldToMessageMapper {

    private static final Map<String, Function<String, String>> WORKINGTYPE_MESSAGE_MAPPER = new HashMap<>();
    static {
      WORKINGTYPE_MESSAGE_MAPPER.put("abc", fieldArray1 -> rightPad("a", Integer.parseInt(fieldArray1)));
      WORKINGTYPE_MESSAGE_MAPPER.put("def", fieldArray1 -> rightPad("d", Integer.parseInt(fieldArray1)));
      WORKINGTYPE_MESSAGE_MAPPER.put("DEFAULT", fieldArray1 -> rightPad("g", Integer.parseInt(fieldArray1)));
    }

    private static Map<String, BiFunction<MyObject, String, String>> MESSAGE_MAPPER = new HashMap<>();
    static {
      MESSAGE_MAPPER.put(Constants.WORKFLOW, (o, fieldArray1) -> rightPad(o.getFieldOne(), Integer.parseInt(fieldArray1)));
      MESSAGE_MAPPER.put(Constants.WORKVOLUME, (o, fieldArray1) -> rightPad(o.getFieldTwo(), Integer.parseInt(fieldArray1)));
      MESSAGE_MAPPER.put(Constants.WORKTYPE,
        (o, fieldArray1) -> WORKINGTYPE_MESSAGE_MAPPER.getOrDefault(o.getFieldThree().toLowerCase(), WORKINGTYPE_MESSAGE_MAPPER.get("DEFAULT")).apply(fieldArray1));
    }

    public static Optional<String> map(MyObject o, String fieldArray0, String fieldArray1) {
      return Optional.ofNullable(MESSAGE_MAPPER.get(fieldArray0.toLowerCase()))
        .map(mapper -> mapper.apply(o, fieldArray1));
    }

    private static String rightPad(String string, int pad) {
        // TODO right pad
        return string;
    }
  }

我们不返回映射器本身。 FieldToMessageMapper提供方法map它进行映射。它返回 Optional<String>这表明如果没有输入映射,结果可能为空。
为了确保获得独立于字符大小写的映射器,所有键都是 String..toLowerCase() .

让我们继续整体处理:

  protected StringBuffer process(Collection<String> templateFields, MyObject object) {
    StringBuffer message = new StringBuffer(1000);
    for (String field : templateFields) {
      String[] fieldArray = field.split(Constants.SEPARATOR);
      String msg = FieldToMessageMapper.map(object, fieldArray[0], fieldArray[1])
        .orElseThrow(() -> new IllegalArgumentException(String.format("Unsupported field %s", field)));
      message.append(msg);
    }
    return message;
  }

我不知道您需要如何处理丢失的映射。我选择通过抛出异常来快速失败。

请注意:StringBuffer

A thread-safe, mutable sequence of characters. A string buffer is like a String, but can be modified.

如果您的处理不是多线程的,您可以使用 StringBuilder 。如果结果不进一步修改,您可以使用 String .

让我展示使用 Stream 的进一步替代方案它返回 String :

  protected String process(Collection<String> templateFields, MyObject object) {
    return templateFields.stream()
      .map(field -> field.split(Constants.SEPARATOR))
      .map(fieldArray -> FieldToMessageMapper.map(object, fieldArray[0], fieldArray[1])
        .orElseThrow(() -> new IllegalArgumentException(String.format("Unsupported field %s", Arrays.toString(fieldArray)))))
      .collect(Collectors.joining());
  }
<小时/>

如果我从问题中得到了正确的代码,应该有以下 Constants 的实现:

  public class Constants {
    public static final String SEPARATOR = ",";
    public static final String WORKFLOW = "field1";
    public static final String WORKVOLUME = "filed2";
    public static final String WORKTYPE = "field3";
  }
<小时/>

编辑:

如果您想要一种配置方法,您可以进一步详细说明此代码以使用 Spring 配置:

  1. 定义接口(interface) MessageMapper它有两个方法:String getKey()String map(MyObject o, String fieldArray1)getKey()返回 Constants映射器为其提供映射的值。
  2. 实现上述每项 MESSAGE_MAPPER使用此接口(interface)。
  3. 添加CommonMessageMapper它有一个构造函数 CommonMessageMapper(MessageMapper... messageMappers)messageMappers必须放在 Map<String, BiFunction<MyObject, String, String>> mappers 中例如:mappers.put(messageMapper.getKey(), messageMapper) 。定义一个方法String map(MyObject o, String fieldArray0, String fieldArray1)这将查找适当的 MessageMapper mm使用fieldArray0 :MessageMapper mm = mappers.get(fieldArray0) 。然后调用mm.map(o, feldArray1) 。 (您也可以在此处使用 Optional 来处理不存在适当映射器的情况。)
  4. 使用 Spring 配置全部 MessageMapperCommonMessageMapper必须注释为 BeanComponentCommonMessageMapper的构造函数必须用 @Autowired 进行注释.
  5. 定义一个 Spring 配置(使用 XML 或 @Configuration ),它将注入(inject)所需的 MessageMapper进入CommonMessageMapper并且有一个这样的工厂方法 CommonMessageMapper .
  6. 使用CommonMessageMapper而不是FieldToMessageMapper如上所述。

关于java - 用于基于 Java 模板的字符串构造的工具,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53213422/

相关文章:

python: 'str' 对象没有属性 'iteritems'

python - 创建列表的叙述字符串的优雅方式

C++模板函数问题

c++ - 使用模板和可变参数模板进行线程初始化

c++ - 动态转换、对象列表转换、模板

java - 更新两个 JProgressBars

java - 如何检查字符串是否仅由字母和数字组成

java - 无法将字符串转换为长字符串

java - GWT 缩放时居中对话框

string - 将包含天、小时和分钟的字符串转换为 hh :mm:ss