我们正在尝试编写Java API库来包装Canvas LMS REST API。我们为API处理的每种类型的对象都有一个阅读器和一个编写器接口。例如,我们有一个UserReader
接口,它将从" List users in course"返回用户对象。端点以及" Search users in consortium"端点。
我们正在努力解决的问题是如何最好地为API调用实现所有可选参数。如果查看两个链接端点的规范,您将看到虽然它们都返回User对象,但它们采用非常不同的参数。初始实现只是向listUsersInCourse方法添加了一堆列表(可以为空)和可选参数(可以为空)。因此,如果您不想指定任何可选参数,则调用此方法将如下所示。是的,您可以使用方法的无争论便利版本,但不指定任何选项,但只要您想使用任何选项,您就必须全部处理它们。
List<User> userList = userReader.listUsersInCourse("courseId",
Collections.emptyList(), Optional.empty(), Collections.emptyList());
这是一个可怕的想法,因为1)一些端点有一个很多的选项,这使得荒谬的方法签名和2)如果Canvas添加或更改选项(已经发生)它将打破我们的方法签名并强制每个人根据我们的库来更新他们的代码。
可选,命名参数会很好但Java不会这样做。我已经查看了一些其他API库,并且在REST API中使用这么多参数似乎很少见。我已经考虑过几种解决方法,但我不确定最佳选择是什么。这是我到目前为止所提出的:
执行此操作的相对结构化方法是遵循构建器模式以及添加特定选项的方法。例如:
List<User> userList = userReader.withSearchTerm("Myname")
.withEnrollmentType(EnrollmentType.Student)
.withEnrollmentType(EnrollmentTye.Teacher)
.listUsersInCourse("courseId");
这样,如果添加了新选项,它只是一种新方法,可以添加并且不会破坏现有代码。缺点是并非给定读者类中的所有选项方法都适用于其中的所有API方法,这可能会造成混淆。例如,上面链接的两个用户列表方法都采用搜索词,但联盟一个人不接受注册类型。因此,您必须仔细检查Canvas API文档,以查看哪些选项对给定调用有效。此外,如果添加了一个选项,我们将不得不在人们可以访问它之前发布新版本的库。 Canvas API正在积极开发中,因此事情肯定会发生变化。
可能更有条理的方法是创建特定于呼叫的选项类来进一步约束事物。这看起来像这样:
CourseUserListOptions opts = new CourseUserListOptions();
opts.addSearchTerm("Myname");
opts.addEnrollmentType(EnrollmentType.Student);
opts.addEnrollmentType(EnrollmentType.Teacher);
List<User> userList = userReader.listUsersInCourse("CourseId", opts);
还有另一个ConsortiumUserSearchOptions
课程没有addEnrollmentType
方法。虽然这可能是最安全,最明确的方式,但这似乎是编写和维护的噩梦。
另一种方法是使用更通用的addOption(String key, String value)
方法。上述调用将变为:
userReader.addOption("search_term", "Myname");
userReader.addOption("enrollment_type[]", EnrollmentType.Student.name());
userReader.addOption("enrollment_type[]", EnrollmentType.Teacher.name());
List<User> userList = userReader.listUsersInCourse("CourseId");
这样可以大大降低我们编写和维护的工作量,但会迫使库的用户更多地了解并传递Canvas API中定义的魔术字符串。如上面的#1所述,结构化程度较低的最大好处是,当Canvas API添加新选项时,我们的用户可以立即使用它而不必等待包含新方法的库的新版本
另外一个选项也可以在每个API方法中接受完全通用的选项列表,并强制库的用户更多地参与构建请求。这看起来像这样:
List<Pair> optsList = new ArrayList<>();
optsList.add(new ImmutablePair("search_term", "Myname"));
optsList.add(new ImmutablePair("enrollment_type[]", EnrollmentType.Student.name());
optsList.add(new ImmutablePair("enrollment_type[]", "EnrollmentType.Teacher.name());
List<User> userList = userReader.listUsersInCourse("courseId", optsList);
这些是我提出的选择。如果您使用此API库,您认为哪一个最有用?你更喜欢写/维护哪一个?我完全错过了一种更好的方法吗?
答案 0 :(得分:0)
如果我理解正确,你的问题就是“我应该考虑做多少工作来帮助我的用户”
听起来Canvas API很复杂且仍在不断变化,这会让我倾向于将负担推到用户身上,例如你的第四个选项。如果某些部分太复杂而无法供用户使用,这仍然可以实现您的第二个选项。