我在这里使用谓词是不好的做法还是额外的?
Posted
技术标签:
【中文标题】我在这里使用谓词是不好的做法还是额外的?【英文标题】:Is my usage of predicates here bad practice or extra? 【发布时间】:2021-10-16 04:06:22 【问题描述】:我正在做一个纸牌游戏,我可能有点过度设计。在我有方法pileHasMostSpades()
和pileHasMostSevens()
之前。这些方法具有相同的代码,除了 if 条件和整数值。通过添加谓词和整数参数,我能够将这些方法合并为一个 (pileHasAtLeast()
)。
pileHasAtLeast(count, predicate)
- 检查是否至少有 [count] 张卡片与谓词匹配。
此外,对于这种新型方法 (pileHasAtLeast()
),还有什么替代名称或更好的名称?谢谢。
public int computeScore()
int score = scopas;
final Predicate<Card> spades = c -> c.getSuit() == Card.Suit.SPADES;
final Predicate<Card> sevens = c -> c.getValue() == Card.Value.SEVEN;
score += pile.size() > 20 ? 1 : 0;
score += pileHasAtLeast(6, spades) ? 1 : 0;
score += pileHasAtLeast(3, sevens) ? 1 : 0;
score += hasSevenOfSpades() ? 1 : 0;
return score;
private boolean pileHasAtLeast(int count, Predicate<Card> predicate)
int result = 0;
for(Card card : pile)
if(predicate.test(card))
result++;
return result > count;
【问题讨论】:
你不一定需要专门的方法,你可以写pile.stream().filter(c -> c.getSuit() == Card.Suit.SPADES).count() > 6
你的谓词(在我看来)命名为isSpade
比命名为spades
更好
严格意义上的方法名称:它不是“至少”,而是“超过”(除非这是一个错误,它应该是 >=
而不是 >
)
原始方法可能使代码更具可读性(尽管这只是我的看法)。但是您当然可以将这些方法委托给 Predicate
-accepting 方法以避免代码重复。
@njzk2 是的,这是我在发布后立即修复的错误,谢谢!
【参考方案1】:
我已经对你的代码做了一些重构
public int computeScore()
//here we count all of the conditions that are true (won't work if you need to add different score for each condition)
return scopas + Stream.of(pile.size() > 20,
pileHasAtLeast(6, c -> c.getSuit() == Card.Suit.SPADES),
pileHasAtLeast(3, c -> c.getValue() == Card.Value.SEVEN),
pileHasAtLeast(1, c -> c.getValue() == Card.Value.SEVEN)
&& c.getSuit() == Card.Suit.SPADES)
.filter(condition -> condition)
.count();
private boolean pileHasAtLeast(int count, Predicate<Card> predicate)
return count <= pile.stream()
.filter(predicate)
.count();
几个提示:
不要将“for”等命令式循环与函数式编程混合使用。在没有副作用的情况下使用流。 不要将只使用一次的谓词存储在变量中,否则会失去函数式编程的威力。【讨论】:
回答您的问题:使用谓词是一种很好的做法。在您的情况下,最好内联使用它们。 不错的重构,但我不确定第二个技巧和代码中的样式。我使用变量使我的代码更具可读性。我正在查看您的代码,由于所有选项卡和无法解释的情况,很难阅读。我想命名谓词,以便读者更快地理解它们。可读性差是函数式编程的结果还是有更好的解决方案? @Picarrow 我不觉得它的可读性降低了。也许,你只是不习惯功能,我不知道。不过可以将格式更改为更适合您的格式。 这绝对是真的!我还不习惯功能。但我不确定是否有更好的方法来格式化它,哈哈。我想我得习惯了。【参考方案2】:如果你有一把锤子,一切看起来都像钉子。
这里也是如此。谓词很好,但在这里,尤其是 score += ... ? 1 : 0;
行,完整的低级逻辑被压入特定的形式。
最好把代码写出来,然后再添加抽象。
我仍然不会注销谓词。但不是在卡片上,而是在一堆上。
您是否想要解释您的分数(针对游戏初学者)、单个谓词及其分数和说明文字"* 1 Point: " + "more than 6 spades"
借用 @nzjk2 的 Stream 替换:
record ScoreRule(int score, Predicate<Pile>, String explanation);
ScoreRule[] scoreRules =
new ScoreRule(1,
pile -> pile.size() > 20,
"more than 20 cards"),
new ScoreRule(1,
pile -> pile.stream().filter(c -> c.getSuit() == Card.Suit.SPADES).count() > 6,
"more than 6 spades"),
new ScoreRule(...),
...
;
... score counting and reporting ...
之所以效果更好,是因为堆作为一个整体是要处理的情况,一个Stream有很多求值逻辑,以及count、contains、all match、none match等表达查询能力。
【讨论】:
有趣的方法!虽然我将如何计算分数?遍历 ScoreRule[] 并针对每个规则的谓词进行测试? @Picarrow 是的,数组的 Stream 也是可能的,过滤真正的谓词并对分数求和。但是迭代很好。我喜欢这种声明式方法:逻辑的高级模型。 现在这太强大了,我得开始这样想了,哈哈!感谢您的洞察力!以上是关于我在这里使用谓词是不好的做法还是额外的?的主要内容,如果未能解决你的问题,请参考以下文章
Accounts.onCreateUser 在创建新用户时添加额外的属性,好的做法?