Canvas LMS REST API をラップする Java API ライブラリを作成しようとしています。API が扱うオブジェクトの種類ごとに、リーダーとライターのインターフェイスがあります。たとえば、「コース内のユーザーをリストする」エンドポイントと「コンソーシアム内のユーザーを検索するUserReader
」エンドポイントからユーザー オブジェクトを返すインターフェイスがあります。
私たちが苦労している問題は、すべてのオプション パラメータを API 呼び出しに実装する最善の方法です。リンクされた 2 つのエンドポイントの仕様を見ると、どちらも User オブジェクトを返しますが、パラメータが大きく異なることがわかります。最初の実装では、listUsersInCourse メソッドに一連のリスト (空にすることもできます) とオプションの引数 (空にすることもできます) を追加しただけです。したがって、オプションのパラメーターを指定しない場合のこのメソッドの呼び出しは、次のようになります。はい、オプションを指定しないためのメソッドの引数なしの便利なバージョンを使用できますが、オプションのいずれかを使用したい場合は、とにかくそれらすべてを処理する必要があります。
List<User> userList = userReader.listUsersInCourse("courseId",
Collections.emptyList(), Optional.empty(), Collections.emptyList());
1) 一部のエンドポイントには多くのオプションがあり、ばかげたメソッド シグネチャが作成されます。2) Canvas がオプションを追加または変更すると (これは既に発生しています)、メソッド シグネチャが壊れて、ライブラリに依存しているすべての人にコードを更新します。
オプションの名前付きパラメーターは便利ですが、Java はこれを行いません。私は他のいくつかの API ライブラリを調べましたが、REST API で使用するためにこれほど多くのパラメーターを使用することはやや珍しいようです。これに対処するためのいくつかの方法を考えましたが、最善の選択肢が何であるかはわかりません。これが私がこれまでに思いついたものです:
1
これを行うための比較的構造化された方法は、特定のオプションを追加するメソッドを使用してビルダー パターンに従うことです。たとえば、次のようになります。
List<User> userList = userReader.withSearchTerm("Myname")
.withEnrollmentType(EnrollmentType.Student)
.withEnrollmentType(EnrollmentTye.Teacher)
.listUsersInCourse("courseId");
このように、新しいオプションが追加された場合、追加するのは単なる新しい方法であり、既存のコードを壊しません。欠点は、特定のリーダー クラスのすべてのオプション メソッドがその中のすべての API メソッドに適用されるとは限らず、混乱を招く可能性があることです。たとえば、上にリンクされているユーザー リストの方法はどちらも検索語を使用しますが、コンソーシアムの方法は登録の種類を受け入れません。そのため、特定の呼び出しに対してどのオプションが有効かを確認するには、Canvas API のドキュメントを常に再確認する必要があります。また、オプションが追加された場合、人々がアクセスできるようになる前に、ライブラリの新しいバージョンをリリースする必要があります。Canvas API は活発に開発されているため、状況は確実に変更される可能性があります。
2
潜在的にさらに構造化された方法は、呼び出し固有のオプション クラスを作成して、物事をさらに制約することです。これは次のようになります。
CourseUserListOptions opts = new CourseUserListOptions();
opts.addSearchTerm("Myname");
opts.addEnrollmentType(EnrollmentType.Student);
opts.addEnrollmentType(EnrollmentType.Teacher);
List<User> userList = userReader.listUsersInCourse("CourseId", opts);
ConsortiumUserSearchOptions
メソッドを持たない別のクラスがありaddEnrollmentType
ます。これはおそらく最も型安全で明確に定義された方法ですが、これを作成して維持するのは悪夢のようです。
3
別のアプローチは、より一般的な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");
これにより、コーディングと保守の手間が大幅に軽減されますが、ライブラリのユーザーは、キャンバス API で定義されたマジック ストリングを渡して、もう少し詳しく知る必要があります。上記の #1 で述べたように、構造化されていないことの最大の利点は、Canvas API に新しいオプションが追加されたときに、新しいメソッドが含まれるライブラリの新しいリリースを待たずに、ユーザーがすぐにそれを利用できることです。 .
4
もう 1 つのオプションは、すべての 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 ライブラリを使用しているとしたら、どれが最も便利だと思いますか? どちらを作成/維持しますか? 私はそれを行うためのより良い方法を完全に見逃していますか?