Kotlin:ベストプラクティスがなければ、人生は同じではありません。 パート1

こんにちは、Habr! この記事は、Kotlinでプログラミングする際の痛みを伴う問題について説明しています。 特に、最もあいまいさを引き起こすいくつかのトピックに触れます-ラムダ式でのそれの使用、Standard.ktファイルからの関数の乱用、および記述vs.の簡潔さ。 コードの可読性。



背景



私は約1年前にマイルストーン12からKotlinを調べ始め、それを積極的に使用してAndroidアプリケーションを作成しました。 JavaでAndroidアプリケーションを2年間書いた後、Kotlinで書くのは新鮮な息吹でした-コードははるかにコンパクトで(匿名クラスはなく、機能が登場しました)、言語自体ははるかに表現力があり(拡張関数、ラムダ関数)、より安全でした( null安全性)。



言語がリリースされたとき、私は職場で新しいプロジェクトを間違いなく書くことを始め、同僚に対してそれを賞賛しました(私の小さな会社では私が唯一のAndroid開発者であり、残りはJavaでクライアントサーバーアプリケーションを開発しています)。 私の後、チームの新しいメンバーはこの言語を習得しなければならないことを理解しましたが、私の意見では問題ではありませんでした-この言語はJavaに非常に似ており、公式文書を読んでから3-5日後に自信を持って書き始めることができます。



しばらくすると、場合によっては短くて理解しにくいコードではなく、長くて理解しやすいコードを書く必要があることに気付き始めました。 例:



//   ,         Safe-call ("?."),        val user = response?.user ?: return val name = user.name.toLowerCase() //  ,          val name = response?.user?.name?.toLowerCase() ?: return
      
      





私が唯一のプログラマーだったので、私はこのパターンをすぐに理解し、コードの読みやすさを簡潔にするために暗黙のうちにルールを作成しました。 意欲的なAndroidプログラマーをインターンシップに連れて行くまでは、すべてが順調だったでしょう。 予想通り、言語に関する公式ドキュメントを読んだ後、彼はすぐにKotlinを習得し、彼の背後でJavaプログラミングの経験がありましたが、その後、奇妙なことが起こり始めました:各コードレビューは、言語構造は特定の状況で最もよく使用されます。 言い換えれば、当社ではKotlinプログラミングスタイルの開発を開始しました。 これらの議論は、Kotlinの世界への入り口であるドキュメントにベストプラクティスが含まれていないため、つまり、これらの機能を使用せず、代わりに使用する方がよい場合に発生したと考えています。 それが、私がこの記事を書くことにした理由です。



私は自分の声明の真実を証明しようとはせず、コトリンであらゆる種類のものを正しく書く方法を議論しようとしていることをすぐに規定したいと思います。



言語の問題



「それ」コールバック地獄



この問題は、Kotlinではコールバック関数の単一のパラメーターに名前を付けないことが許可されていることです。 デフォルトの名前は「it」です。 例:



  /**    callback,         */ fun execute(callback: (Any?) -> Unit) { ... callback(parameter) ... } /**  . Kotlin    execute { ... },   execute({ ... }),     */ execute { if (it is String) { //   parameter   it,      String .... } .... }
      
      





ただし、複数のネストされた関数がある場合、混乱が生じる可能性があります。



 execute { execute { execute { if (it is String) { // it       execute .... } .... } } } execute { execute { execute { parameter -> if (it is String) { //  it        execute,        .... } .... } } }
      
      





小さなフラグメントでは、これがそのような問題のように見えないかもしれませんが、数人がコードで作業し、接続された呼び出しを伴うそのような関数が10-15行を持っている場合、このネストレベルで実際にそれを所有している人を失うのは簡単です。 名前がネストの各レベルで何らかの操作に使用されると、状況は悪化します。 この場合、そのようなコードの理解は大きく損なわれます。



 executeRequest { //  it -    Response if (it.body() == null) return executeDB { //  it -    DatabaseHelper it.update(user) executeInBackgroud { //  it -    Thread if (it.wait()) ... .... } } }
      
      





これを使用したコードの読みやすさについて説明します。 私の意見では、それはコードを大幅に削減し、単純な関数の理解を容易にしますが、ネストされたコールバック関数を扱うとすぐに、両方の関数のパラメーターに名前を付ける方が良いと考えています:



  //   executeInBackgroud { if (it.wait()) ... .... } //   executeRequest { response -> if (response.body() == null) return executeDB { dbHelper -> dbHelper.update(user) ... } }
      
      





Standard.ktファイルの機能の不正使用



知らない人のために、 Standard.ktファイルには多くの便利な機能が含まれています。 以下に、それぞれが必要な理由を詳しく説明します。



これらの関数の問題は、プログラマが頻繁に使用し始めたときに始まります。



最初の例はlet関数です。これは基本的に2つのタスクを実行します。値がnullでない場合にコードを呼び出して、この値をit変数に転送できます。



 response?.user?.let { val name = it.name //  it    user }
      
      





この関数の最初の欠点は、前のセクションのテーマと交差しています-it変数が表示され、エラーが発生する可能性があります。 2番目の欠点は、この関数を使用すると、コードが英語のテキストとして読み取れないことです。 次のように書く方がはるかに良いです:



 val user = response?.user ?: return val name = user.name
      
      





第三に、コードの可読性を低下させるインデントの追加レベルを追加しましょう。 この機能については、 こちらこちらこちらご覧ください 。 私の意見では、この機能は言語ではまったく必要ではなく、唯一のプラスはヌルの安全性の助けです。 ただし、このプラスでさえ、他のよりエレガントで理解しやすい方法で解決できます(null with?の予備チェック:またはif if)。



他の機能については、非常にまれに、慎重に使用する必要があります。 例えば、と。 関数を呼び出すオブジェクトが毎回示されないようにします:



 with(dbHelper) { update(user) delete(comment) } //    : dbHelper.update(user) dbHelper.delete(comment)
      
      





問題は、これらの呼び出しがdbHelperオブジェクトに関連しない他のコードと混合されるところから始まります。



 with(dbHelper) { val user = query(user.id) user.name = name user.address = getAddress() // getAddress()     dbHelper .... update(user) val comment = getLatestComment() // getLatestComment()      dbHelper .... delete(comment) }
      
      





この場合、実際に誰がこの機能またはその機能を所有しているかを常に監視する必要があり、可読性が大幅に低下します。 withを使用したネストの例を示しません。そのため、どのスパゲッティコードが結果になるかは明らかです。



次の記事では、すでに成長しているため、他の痛みについて書きます。



更新:



記事の第2部の資料を準備中に、Anton Keksプレゼンテーションのビデオが公開されました 。これは、第2記事のすべてのポイントを完全にカバーするだけでなく、いくつかの重要なポイントも含んでいます。 しかし、最も重要なことには、このビデオには開発者からのコメントもあります。 2番目の記事は1番目の記事の形式ではなくなることを決定しました(そして、そのような問題があると言うでしょう、ビデオではそのようなことを言っています)。少なくとも、言語に新しい問題が見つかるまで。 継続を待っていた人は、ビデオプレゼンテーションをご覧になることをお勧めします。



All Articles