功能解决方案而不是迭代

时间:2021-01-05 23:19:12

标签: java java-8 functional-programming

我必须执行以下任务。为简单起见,我创建了下表:

enter image description here

我们有输入和一个定义了规则的表格。如果有任何规则与此输入匹配,则对其应用操作。

例如输入HBC1234

  1. starts_with H => true。
  2. 因此,将matching_value (H) 替换为M => 结果为MBC1234

以同样的方式迭代此输入的所有其他规则。

这是替换规则的实体:

@Data
@Entity
@NoArgsConstructor
public class ReplacementRule implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @NotNull
    @Enumerated(EnumType.STRING)
    private MatchingOperation matchingOperation;
    @NotNull
    private String matchingValue;

    @NotNull
    @Enumerated(EnumType.STRING)
    private ReplacementOperation replacementOperation;
    private String replacementValue;
}

以及此任务的实现:

private static String apply(ReplacementRule rule, String plateNumber) {
    switch (rule.getMatchingOperation()) {
        case STARTS_WITH:
            if (plateNumber.startsWith(rule.getMatchingValue())) {
                return switch (rule.getReplacementOperation()) {
                    case REPLACE -> plateNumber.replaceFirst(rule.getMatchingValue(), rule.getReplacementValue());
                    case REMOVE -> plateNumber.substring(rule.getMatchingValue().length());
                };
            }
        case CONTAINS:
            if (plateNumber.contains(rule.getMatchingValue())) {
                return switch (rule.getReplacementOperation()) {
                    case REPLACE -> plateNumber.replaceAll(rule.getMatchingValue(), rule.getReplacementValue());
                    case REMOVE -> plateNumber.replaceAll(rule.getMatchingValue(), "");
                };
            }
        case ENDS_WITH:
            if (plateNumber.endsWith(rule.getMatchingValue())) {
                return switch (rule.getReplacementOperation()) {
                    case REPLACE -> plateNumber.substring(0, getEndIndex(rule, plateNumber)).concat(rule.getReplacementValue());
                    case REMOVE -> plateNumber.substring(0, getEndIndex(rule, plateNumber));
                };
            }
        case EQUALS:
            if (plateNumber.equals(rule.getMatchingValue())) {
                return switch (rule.getReplacementOperation()) {
                    case REPLACE -> rule.getReplacementValue();
                    case REMOVE -> "";
                };
            }
    }
    return "";
}

private static int getEndIndex(ReplacementRule rule, String plateNumber) {
    return plateNumber.length() - rule.getMatchingValue().length();
}

最终用法如下:

public Optional<WhiteList> checkReplacementRules(String plateNumber) {
    List<ReplacementRule> allRules = ruleRepository.findAll();

    Optional<WhiteList> result = Optional.empty();
    for (ReplacementRule rule : allRules) {
        String newPlate = transform(rule, plateNumber);
        if (StringUtils.isNotBlank(newPlate)) {
            result = whiteListRepository.findByNumberPlate(newPlate);
        }
    }

    return result;
}

它结合了匹配和逻辑以在一种方法 apply() 中进行替换。然而,它违反了Open-Closed Principle。但它有效。尽管解决方案并不是最好的。

似乎这种任务符合函数式编程概念。

如何将其重新设计为函数式风格?

Java 版本为 15

2 个答案:

答案 0 :(得分:2)

enum 类实现动作,即操作。

Regex 似乎非常适合您正在做的事情。

private static String apply(ReplacementRule rule, String plateNumber) {
    String regex = rule.getMatchingOperation().regexFor(rule.getMatchingValue());
    String replacement = rule.getReplacementOperation().regexFor(rule.getReplacementValue());
    return Pattern.compile(regex)
                  .matcher(plateNumber)
                  .replaceFirst(replacement);
}
public enum MatchingOperation {
    STARTS_WITH(v -> "^" + Pattern.quote(v)),
    CONTAINS   (v -> Pattern.quote(v)),
    ENDS_WITH  (v -> Pattern.quote(v) + "$"),
    EQUALS     (v -> "^" + Pattern.quote(v) + "$");

    private final UnaryOperator<String> asRegex;

    private MatchingOperation(UnaryOperator<String> asRegex) {
        this.asRegex = asRegex;
    }

    public String regexFor(String matchingValue) {
        return this.asRegex.apply(matchingValue);
    }
}
public enum ReplacementOperation {
    REPLACE(Matcher::quoteReplacement),
    REMOVE (v -> "");

    private final UnaryOperator<String> asRegex;

    private ReplacementOperation(UnaryOperator<String> asRegex) {
        this.asRegex = asRegex;
    }

    public String regexFor(String replacementValue) {
        return this.asRegex.apply(replacementValue);
    }
}

答案 1 :(得分:0)

我不认为您的设计违反了开闭原则,但是,这是我的实现。由于它可以将未来的需求添加为功能,因此可以称为功能性。

@FunctionalInterface
interface Rule {
    public String apply(String string);
}

Rule 用于每个需求的函数。

class Replacement {
    ArrayList<Rule> rules = new ArrayList<Rule>();

    public Replacement addRule(Rule rule) {
        rules.add(rule);
        return this;
    }
    
    public String applyAll(String string) {
        System.out.printf("%s --> ", string);
        for (Rule rule : rules) {
            string = rule.apply(string);
        }   
        System.out.println(string);
        return string;
    }
}

Replacement 可以对输入进行 addRuleapplyAll 规则。

以下是如何将 Rule 添加为函数并应用:

public class Main {
    public static void main(String[] args) {
        Replacement r = new Replacement()
        
        .addRule((String string) -> {
            if (string.startsWith("H"))
                return "M" + string.substring(1);
            return string;
        })
 
        .addRule((String string) -> {
            return string.replaceAll("A", "");
        })
        
        .addRule((String string) -> {
            if (string.endsWith("8"))
                return string.substring(0, string.length() - 1);
            return string;
        })
        
        .addRule((String string) -> {
            if (string.equals("ABC12B467"))
                return "ABC123467";
            return string;
        });

        r.applyAll("HBC1234");
        r.applyAll("ABC1234");
        r.applyAll("ABC9988");
        r.applyAll("ABC12B467");
    }
}