【发布时间】:2021-12-30 15:08:31
【问题描述】:
问题是关于this code(在Android应用程序中):
private Task<QuerySnapshot> getVotesFromDB() {
res = new int[]{0, 0, 0}; // a private class-member
return FirebaseFirestore.getInstance().collection("Votes")
.whereEqualTo("proposition_key", curr_proposition.getKey())
.get().addOnCompleteListener(task -> {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
String userChoice = (String) document.get("user_choice");
int choice;
switch (userChoice) {
case "against":
choice = 0;
break;
case "impossible":
choice = 1;
break;
case "agreement":
choice = 2;
break;
default:
throw new IllegalStateException("Unexpected value: " + userChoice);
}
res[choice]++;
}
}
});
}
一般来说,代码会从 Firestore 集合中读取一些行,并对它们应用一些“业务逻辑”(计算每种类型的字符串数)。未来,业务逻辑可能会变得比单纯的计数要复杂得多。所以我正在寻找一种方法来重构代码,以便可以将业务逻辑与数据库分开编写和测试。我想要的是具有某种形式的功能:
int[] countVotes(Generator<String> strings) {
res = new int[3];
(for String userChoice: strings) {
// Update res as above
}
return res;
}
可以在不需要数据库连接的情况下进行单元测试。那么,上面的函数可以重构如下:
private Generator<String> getVotesFromDB() {
return FirebaseFirestore.getInstance().collection("Votes")
.whereEqualTo("proposition_key", curr_proposition.getKey())
.get().addOnCompleteListener(task -> {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
userChoice = (String) document.get("user_choice");
yield userChoice;
}
}
});
}
然后运行类似的东西:
countVotes(getVotesFromDB())
问题是,我不知道如何使用异步函数调用来做到这一点。有没有办法以类似或更好的方式重构代码?
【问题讨论】:
-
Firebase 不为该套件中的任何产品提供同步 API。一切都是异步的,您需要应用适当的异步编程技术才能有效地使用它。您当然可以尝试阻止执行查询的线程,但这在 Android 上是一个非常糟糕的主意,因为这会导致应用程序冻结并可能因 ANR 而崩溃。
-
@DougStevenson 我的目标不一定是让代码同步——我的目标是将逻辑与数据库连接分离,这样逻辑可以单独进行单元测试,与不同的数据库一起使用,等等。还有其他方法吗?
-
你不能直接从
onCompleteListener内部调用countVotes吗?然后你仍然可以单独对countVotes进行单元测试。 -
@TylerV 如何在使
countVotes接受字符串集合(或流)的同时做到这一点? -
为什么需要?难道它不能只取你从数据库中得到的字符串参数并更新 res 类成员(返回 void)吗?要对其进行测试,您只需多次调用它来模拟实际用途。
标签: java android firebase asynchronous google-cloud-firestore