为了账号安全,请及时绑定邮箱和手机立即绑定

从具有多个默认值的缓存中提取代码时会闻到什么味道?

从具有多个默认值的缓存中提取代码时会闻到什么味道?

holdtom 2021-05-07 14:12:42
我们有一个包含4列的表格:A B C VALUE  1 2 0 100  0 3 3 200  0 0 7 400  0 0 0 700  该键由3列组成:A,B和C。我们将此表保存在一个HashMap<ImmutableTriple<Integer, Integer, Integer>, Integer>对象上。零代表默认值,因此在找不到确切密钥的情况下,我们应根据以下逻辑拉出相关密钥:value = cache.get(new ImmutableTriple<>(a, b, c));if (value != null) {    return value;}value = cache.get(new ImmutableTriple<>(a, b, 0));if (value != null) {    return value;}value = cache.get(new ImmutableTriple<>(a, 0, c));if (value != null) {    return value;}value = cache.get(new ImmutableTriple<>(a, 0, 0));if (value != null) {    return value;}value = cache.get(new ImmutableTriple<>(0, 0, 0));if (value != null) {    return value;}通过具有默认值的3个ID的键获取会产生代码异味的问题。有没有更好,更清洁的方法呢?
查看完整描述

2 回答

?
狐的传说

TA贡献1804条经验 获得超3个赞

有时,如果爆炸式循环使代码更具可读性,则不一定是一件坏事。


我建议您将代码保持原样,因为稍后您会看到,如果您尝试对其进行压缩,则很可能会遇到更糟糕的情况。如果您坚持要继续阅读。


我的想法很简单:您实际上是在尝试遍历多个掩码以用于您的匹配模式。在这种情况下,掩码为“是否使用id或默认值”。


不掩饰任何东西,如果没有匹配,

遮罩c,如果没有匹配项,

遮罩b,如果不匹配,

屏蔽bc,如果没有匹配项,

屏蔽abc,如果没有匹配项,

返回空值。

因此,我们可以使用某种数据结构来存储掩码,如下所示:


    // true = use id (don't mask); false = use default (mask).

    List<ImmutableTriple<Boolean, Boolean, Boolean>> matchMasks = new ArrayList<>();

    matchMasks.add(new ImmutableTriple<>(true, true, true));

    matchMasks.add(new ImmutableTriple<>(true, true, false));

    matchMasks.add(new ImmutableTriple<>(true, false, true));

    matchMasks.add(new ImmutableTriple<>(true, false, false));

    matchMasks.add(new ImmutableTriple<>(false, false, false));

当然,你可以使用List的List,Array的Array,或任何明智的存储这个面具。


现在,我们可以使用循环来压缩您的代码:


private Integer match(int a, int b, int c)

{

    Integer value = null;

    // Loop through match rules.

    for (ImmutableTriple<Boolean, Boolean, Boolean> mask : matchMasks)

    {

        // Check whether to apply mask.

        int aId = mask.getLeft() ? a : 0;

        int bId = mask.getMiddle() ? b : 0;

        int cId = mask.getRight() ? c : 0;

        // Try to match cache.

        value = cache.get(new ImmutableTriple<>(aId, bId, cId));

        // Stop if match found.

        if (value != null) break;

    }

    return value;

}

全部归为一类:


public class Test

{

    private HashMap<ImmutableTriple<Integer, Integer, Integer>, Integer> cache = <your table here>;

    private List<ImmutableTriple<Boolean, Boolean, Boolean>> matchMasks = new ArrayList<>();


    public Test()

    {

        // true = use id (don't mask); false = use default (mask).

        this.matchMasks.add(new ImmutableTriple<>(true, true, true));

        this.matchMasks.add(new ImmutableTriple<>(true, true, false));

        this.matchMasks.add(new ImmutableTriple<>(true, false, true));

        this.matchMasks.add(new ImmutableTriple<>(true, false, false));

        this.matchMasks.add(new ImmutableTriple<>(false, false, false));

    }


    private Integer match(int a, int b, int c)

    {

        Integer value = null;

        // Loop through match rules.

        for (ImmutableTriple<Boolean, Boolean, Boolean> mask : this.matchMasks)

        {

            // Check whether to apply mask.

            int aId = mask.getLeft() ? a : 0;

            int bId = mask.getMiddle() ? b : 0;

            int cId = mask.getRight() ? c : 0;

            // Try to match cache.

            value = this.cache.get(new ImmutableTriple<>(aId, bId, cId));

            // Stop if match found.

            if (value != null) break;

        }

        return value;

    }

}

PS对于此特定情况,由于0 =默认值,因此可以为掩码使用1和0而不是true和false int aId = mask.getLeft() * a;,但这很简单。


如您所见,尽管我们确实将代码“压缩”成一个循环,但其效果还是值得商de的。实际上,我认为该代码实际上不那么可读。因此,有时候,坚持使用爆炸循环并不一定很糟糕。


查看完整回答
反对 回复 2021-05-19
?
牛魔王的故事

TA贡献1830条经验 获得超3个赞

您可以使用默认值为三胞胎的所有可能状态创建“掩码映射”:


private static List<Integer[]> mask = new ArrayList<>();

static {

    mask.add(new Integer[] {null, null, null});

    mask.add(new Integer[] {0, null, null});

    mask.add(new Integer[] {null, 0, null});

    mask.add(new Integer[] {null, null, 0});

    mask.add(new Integer[] {null, 0, 0});

    mask.add(new Integer[] {0, null, 0});

    mask.add(new Integer[] {0, 0, null});

    mask.add(new Integer[] {0, 0, 0});

}

假设为null,表示必须填充键值。然后,您可以从此掩码创建三元组键的列表:


List<Triple<Integer, Integer, Integer>> triples = mask.stream()

                                                          .map(row -> genrateTriple(row, a, b, c))

                                                          .collect(Collectors.toList());

generateTriplets的代码:


 private Triple<Integer, Integer, Integer> genrateTriple(Integer[] row, int... values) {

    int[] tripleElems = new int[3];

    for (int i = 0; i < row.length; i++) {

        tripleElems[i] = row[i] == null ? values[i] : row[i];

    }

    return new ImmutableTriple<>(tripleElems[0], tripleElems[1], tripleElems[2]);

}

并在搜索中使用它:


return triples.stream()

           .map(triple -> cache.get(triple))

           .findFirst()

           .orElse(null);

但是我不确定这是否比您的“代码气味”;)好。


编辑:当然,如果您避免创建中间集合,这会更有效(findFirst是短循环运算符):


Integer result = mask.stream()

        .map(row -> genrateTriple(row, a, b, c))

        .map(cache::get)

        .findFirst()

        .orElse(null);


查看完整回答
反对 回复 2021-05-19
  • 2 回答
  • 0 关注
  • 124 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信