1.概要

Javaはオープンソースの世界の柱の1つです。誰もが車輪を再発明したくないので、ほとんどすべてのJavaプロジェクトは他のオープンソースプロジェクトを使用します。しかし、その機能のためにライブラリが必要になることがよくありますが、それを使用する方法についての手がかりがありません。次のようなことが起こります。

  • これらすべての “** Service”クラスでそれは何ですか?

  • これをどのようにインスタンス化するのですか。は何ですか



ラッチ

“?

  • ああ、私はそれをまとめるが、今では投げ始める


IllegalStateException

。何がおかしいのですか?

問題は、すべての図書館デザイナーが自分のユーザーについて考えるわけではないことです。

ほとんどの人は機能や機能についてだけ考えていますが、APIが実際にどのように使用されるのか、そしてユーザーのコードがどのように見えてテストされるのかを考える人はほとんどいません。

この記事には、これらの闘争の一部をユーザーを救う方法についてのいくつかのアドバイスが付属しています。もちろん、本全体をこの主題について書くことができます(そしていくつかは書かれています)。これらは私自身がいくつかの図書館で仕事をしている間に私が学んだ重要なポイントのいくつかです。

ここでは2つのライブラリを使用してアイデアを例示します。


charles

およびhttps://www.github.com/jcabi/jcabi-github[jcabi-github]

2.境界

これは明白なはずですが、何度もそうではありません。コードを書き始める前に、いくつかの質問に明確に答えておく必要があります。どのような入力が必要ですか。私のユーザーに表示される最初のクラスは何ですか?ユーザーからの実装が必要ですか?出力は何ですか?これらの質問に明確に答えられれば、図書館にはすでに裏地や形があるので、すべてが簡単になります。


2.1. 入力

これはおそらく最も重要なトピックです。ライブラリが機能するためには、ユーザーがライブラリに何を提供する必要があるのか​​を明確にする必要があります。場合によっては、これは非常に些細なことです。それは、APIの認証トークンを表す単なるStringである場合もありますが、インターフェースの実装、または抽象クラスである場合もあります。

いくつかのパラメータを使用して、すべての依存関係をコンストラクタで処理し、それらを短くすることをお勧めします。 3つまたは4つを超えるパラメータを持つコンストラクタが必要な場合は、コードを明確にリファクタリングする必要があります。そして、必須の依存関係を注入するためにメソッドが使用されている場合、ユーザーはおそらく概要で説明されている3番目のフラストレーションになるでしょう。

また、私たちは常に複数のコンストラクタを提供し、ユーザに代替手段を提供するべきです。

String



Integer

の両方で機能するようにしたり、

FileInputStream

で機能しないようにしたり、

InputStream

で機能したりするので、単体テスト時などに

ByteArrayInputStream

を送信できます。

たとえば、jcabi-githubを使用してGithub APIエントリポイントをインスタンス化する方法はいくつかあります。

Github noauth = new RtGithub();
Github basicauth = new RtGithub("username", "password");
Github oauth = new RtGithub("token");

初期化するための単純な、煩わしい、日陰のない設定オブジェクト。これら3つのコンストラクタを使用するのは理にかなっています。ログアウトした状態、ログインした状態、またはアプリがあなたの代わりに認証できる状態でGithub Webサイトを使用できるからです。当然のことながら、認証されていないと一部の機能は機能しませんが、最初からこれを知っています。

2番目の例として、Webクロールライブラリcharlesを使用する方法を次に示します。

WebDriver driver = new FirefoxDriver();
Repository repo = new InMemoryRepository();
String indexPage = "http://www.amihaiemil.com/index.html";
WebCrawl graph = new GraphCrawl(
  indexPage, driver, new IgnoredPatterns(), repo
);
graph.crawl();

それはまた一目瞭然です、と私は思います。しかし、これを書いている間、私は現在のバージョンでは間違いがあることに気づいています。すべてのコンストラクタは

IgnoredPatterns

のインスタンスを提供することをユーザに要求します。デフォルトでは、パターンは無視されませんが、ユーザーはこれを指定する必要はありません。私はこれをこのようにしておくことに決めました、それであなたは反例を見ます。私はあなたがWebCrawlをインスタンス化しようとし、「その

IgnoredPatterns

でそれは何ですか?」と疑問に思うだろうと思います。

変数indexPageはクロールを開始する場所のURL、driverは使用するブラウザです(実行中のコンピュータにインストールされているブラウザがわからないため、デフォルトには設定できません)。リポジトリ変数については、次のセクションで説明します。

ですから、例に見られるように、それを単純で直感的でわかりやすいものにするようにしてください。コンストラクタを見たときにユーザーが頭を悩ませないように、ロジックと依存関係をカプセル化します。

それでも疑問がある場合は、https://github.com/aws/aws-sdk-java[aws-sdk-java]を使用してAWSにHTTPリクエストを送信してみてください。いわゆるAmazonHttpClientを処理する必要があります。どこかでClientConfigurationを使用し、そしてその中間にExecutionContextを取る必要があります。

最後に、あなたはあなたのリクエストを実行してレスポンスを得ることができるかもしれませんが、それでも例えばExecutionContextが何であるかについての手がかりを持っていません。


2.2. 出力

これは主に外界と通信する図書館のためのものです。ここで、「出力はどのように処理されますか」という質問に答えます。繰り返しますが、やや面白い質問ですが、間違って踏み出すのは簡単です。

上記のコードをもう一度見てください。リポジトリ実装を提供する必要があるのはなぜですか? WebCrawl.crawl()メソッドがWebPage要素のリストを返さないのはなぜですか?クロールされたページを処理するのは図書館の仕事ではないことは明らかです。私たちが彼らと何をしたいのかをどのようにして知るべきなのでしょうか。このようなもの:

WebCrawl graph = new GraphCrawl(...);
List<WebPage> pages = graph.crawl();

さらに悪いことはありません。クロールされたサイトに1000ページがあるとすると、OutOfMemory例外はどこにも発生しない可能性があります – ライブラリはそれらをすべてメモリに読み込みます。これには2つの解決策があります。

  • ページを返し続けますが、ページングメカニズムを実装します。

ユーザーは開始番号と終了番号を入力する必要があります。または
** と呼ばれるメソッドでインターフェースを実装するようにユーザに依頼

最大ページ数に達するたびにアルゴリズムが呼び出す(export <WebPage>)

2番目の選択肢は、はるかに優れています。それは物事を両側でよりシンプルに保ち、よりテストしやすくなります。最初の方法を使用した場合、ユーザー側でどのくらいのロジックを実装する必要があるのか​​を考えます。このように、ページのリポジトリが指定され(DBに送信するかディスクに書き込むため)、crawl()メソッドを呼び出した後に他に何もする必要はありません。

ちなみに、上のInputセクションのコードは、Webサイトのコンテンツを取得するために書く必要があるものすべてです(repoの実装ではまだメモリ上にありますが、私たちが選んだのはその実装です)。私達は危険を冒します)。

このセクションを要約すると:私たちは自分の仕事をクライアントの仕事から完全に切り離すべきではありません。私たちは常に私たちが作成した出力で何が起こるのかを考えるべきです。トラックの運転手は、目的地に到着したときに商品を捨てるのではなく、商品を開梱するのを手伝うべきです。

3.インターフェース

常にインターフェースを使用してください。ユーザーは厳格な契約を通してのみ私たちのコードと対話するべきです。

たとえば、

jcabi-github

ライブラリでは、RtGithubクラスがユーザに実際に見られる唯一のクラスです。

Repo repo = new RtGithub("oauth__token").repos().get(
  new Coordinates.Simple("eugenp/tutorials"));
Issue issue = repo.issues()
  .create("Example issue", "Created with jcabi-github");

上記のスニペットはhttps://github.com/eugenp/tutorials[eugenp/tutorialsリポジトリ]にチケットを作成します。 RepoとIssueのインスタンスが使用されますが、実際のタイプは明らかにされません。こんなことはできません。

Repo repo = new RtRepo(...)

上記は論理的な理由で不可能です:Githubレポジトリで直接問題を作成することはできません、できますか?まず、ログインしてレポを検索する必要があります。それから問題を作成できます。もちろん、上記のシナリオは許可されますが、その場合ユーザーのコードは多くの定型コードで汚染されることになります。等

インタフェースはまた、拡張性と下位互換性を容易にします。一方では開発者としてすでにリリースされている契約を尊重し、他方ではユーザーは私たちが提供するインターフェースを拡張することができます。

言い換えれば、できるだけ抽象化してカプセル化してください。インターフェイスを使用することで、これをエレガントで制限のない方法で実行できます。アーキテクチャルールを適用しながら、プログラマに公開する動作を拡張または変更する自由を与えます。

このセクションを終わらせるために、私たちのライブラリ、私たちのルールだけを覚えておいてください。クライアントのコードがどのように表示されるのか、そしてユニットコードをどのようにテストするのかを正確に知っておく必要があります。私たちがそれを知らないならば、誰もそして私たちのライブラリは理解し維持するのが難しいコードを作成することに単に貢献するでしょう。

4.第三者

良いライブラリは軽量のライブラリです。あなたのコードは問題を解決して機能するかもしれませんが、jarファイルが私のビルドに10 MBを追加するならば、あなたはずっと前にあなたのプロジェクトの青写真を失ったことは明らかです。あなたがたくさんの依存関係を必要とするなら、あなたはおそらくあまりにも多くの機能性をカバーしようとしていて、そしてプロジェクトを複数のより小さなプロジェクトに分けるべきです。

可能な限り透明にし、可能な限り実際の実装にバインドしないでください。頭に浮かぶ最良の例は、SLF4Jを使用することです。これはロギング用のAPIにすぎません。log4jを直接使用しないでください。ユーザーが他のロガーを使用したい場合があります。

あなたのプロジェクトを他動詞的に見てきて、

xalan



xml-apis

のような危険な依存関係を含まないようにしてください(なぜこのような危険なのかは、この記事では説明しません)

ここで重要なのは、ビルドの明るさを透明に保ち、常に作業内容を把握しておくことです。想像以上に煩わしさが軽減されます。

5.まとめ

この記事では、ユーザビリティに関して、プロジェクトを成功させるための簡単なアイデアをいくつか紹介します。ライブラリは、より大きな文脈でその場所を見つける必要があるコンポーネントであるため、機能的に強力でありながら、滑らかで洗練されたインターフェースを提供するはずです。

それは線を越えた簡単な一歩であり、設計を崩します。貢献者はそれを使う方法を常に知っているでしょう、しかし最初にそれを目にした誰かが新しいかもしれません。最も重要なのは生産性であり、この原則に従って、ユーザーはほんの数分でライブラリを使い始めることができるはずです。