首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >当使用多个默认值从缓存中拉取数据时,代码是否有异味?

问当使用多个默认值从缓存中拉取数据时,代码是否有异味?
EN

Stack Overflow用户
提问于 2018-07-08 20:27:20
回答 2查看 69关注 0票数 2

我们有一个包含4列的表:

代码语言:javascript
复制
A B C VALUE  
1 2 0 100  
0 3 3 200  
0 0 7 400  
0 0 0 700  

key由A、B、C三列组成。

我们将这个表保存在一个HashMap<ImmutableTriple<Integer, Integer, Integer>, Integer>对象上。

0表示默认值,所以如果找不到确切的key,我们应该按照下面的逻辑拉取相应的key:

代码语言:javascript
复制
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的默认值会产生代码气味的问题。有没有更好\更干净的方法呢?

EN

回答 2

Stack Overflow用户

发布于 2018-07-08 23:45:46

您可以使用默认值为三元组的所有可能状态创建一个“掩码映射”,例如:

代码语言:javascript
复制
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,表示必须填充键值。然后,您可以使用此掩码创建三元组密钥的列表:

代码语言:javascript
复制
List<Triple<Integer, Integer, Integer>> triples = mask.stream()
                                                          .map(row -> genrateTriple(row, a, b, c))
                                                          .collect(Collectors.toList());

generateTriplets的代码如下:

代码语言:javascript
复制
 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]);
}

并在搜索中使用它:

代码语言:javascript
复制
return triples.stream()
           .map(triple -> cache.get(triple))
           .findFirst()
           .orElse(null);

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

EDITED:当然,如果您避免创建中间集合(findFirst是一个简短的回路运算符),这将更加有效:

代码语言:javascript
复制
Integer result = mask.stream()
        .map(row -> genrateTriple(row, a, b, c))
        .map(cache::get)
        .findFirst()
        .orElse(null);
票数 0
EN

Stack Overflow用户

发布于 2018-07-08 23:57:19

有时候,有一个松散的循环不一定是一件坏事,如果它使代码更具可读性的话。

我建议你保持代码的原样,因为你很快就会看到,如果你试图压缩它,你很可能会得到更糟糕的结果。如果你坚持要继续读下去。

我的想法很简单:您实际上是在尝试遍历几个掩码,以用于匹配模式。这种情况下的掩码是“使用id还是default”。

  1. 掩码无,如果不匹配,则返回
  2. 掩码c,如果不匹配,则返回
  3. 掩码b,如果不匹配,则返回
  4. 掩码b c,如果不匹配,则返回

掩码a b c,如果不匹配,则返回H210H111返回null。G213

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

代码语言:javascript
复制
    // 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或任何合理的方法来存储此掩码。

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

代码语言:javascript
复制
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;
}

将所有内容放在一个类中:

代码语言:javascript
复制
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;
    }
}

备注:对于此特定情况,由于0=默认值,因此您可以使用1和0代替true和false作为掩码,并执行此int aId = mask.getLeft() * a;,但这是微不足道的。

正如您所看到的,尽管我们确实将您的代码“压缩”到一个循环中,但它的效果是非常有争议的。事实上,我认为这段代码实际上可读性较差。因此,有时坚持使用爆炸循环并不一定是坏事。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51231990

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档