web-dev-qa-db-ja.com

高度な言語機能を使用すると保守性が低下しますか?

コードレビューで、私は一人で適切に判断できない興味深いアイデアに出会いました。

広く知られていない言語構文を使用してコードの可読性を向上させても大丈夫ですか?元のライターが特定のコーディング問題を解決するために指定された特別な構文を使用せず、回避策でシミュレートすることによってそれを解決した場合—明快さと簡潔さのためにリファクタリングする価値はありますか?コードレビューアでさえ言語機能について知らなかった場合はどうなりますか?

具体的には、コードレビューで出会った正確な問題を取り上げますが、これは単なる例であることを覚えておいてください。

したがって、元のコードはPythonの小さなWebサービスでした。ある時点で、いくつかの要求ハンドラを認証メカニズムで保護する必要が生じました。誰かが継承してそれを解決しました:

class AuthOnlyAccess:
    def GET(self, *args):
        if not hasattr(self, 'PROTECTED_GET'):
            raise web.Nomethod()

        if check_auth():
            return self.PROTECTED_GET(*args)
        else:
            raise web.Unauthorised()

    def POST(self, *args):
        if not hasattr(self, 'PROTECTED_POST'):
            raise web.Nomethod()

        if check_auth():
            return self.PROTECTED_POST(*args)
        else:
            raise web.Unauthorised()

...

class FooHandler(AuthOnlyAccess):
    def PROTECTED_GET(self, a, b, c):
        handle_foo_get(a, b, c)

    def PROTECTED_POST(self, x, y, z):
        handle_spam_eggs()

...
<more handlers deriving AuthOnlyAccess>

リファクタラーはこのコードを醜く見つけ、デコレーターを導入しました:

from functools import wraps
...

def requires_auth(wrapped_func):
    """ (a little doc telling that this is a decorator and how to use it) """
    @wraps(wrapped_func)
    def decorator(*args):
        if check_auth():
            return wrapped_func(*args)
        else 
            raise web.Unauthorized()

    return decorator

...

class FooHandler:
    @requires_auth
    def GET(self, a, b, c):
        handle_foo_get(a, b, c)

    @requires_auth
    def POST(self, x, y, z):
        handle_spam_eggs()

...
<more refactored handlers>

レビュー担当者は、この変更は実際には減少保守性であると主張しています。これは、ほとんどのチームがデコレータ構文を知らないためです。したがって、リファクタリングされたコードにアクセスする次の人は、構造に戸惑うかもしれませんが、以前のバージョンでは困惑する可能性があります。

ほとんどのコードベースはPythonにない(その言語はその小さなWebサービスにのみ存在する)という説得力のある議論もあります。そのため、チームメンバーは浅い知識さえも必要ありません。 Python。しかし、コードを操作するには、その言語を知る必要がありますよね?

そして、もしあなたがpythonデコレータはそうではないそれ高度な構文ではない)と言う誘惑があるなら、これはほんの一例です。すべての言語には暗いコーナーがあります(しかし、それらのコーナーを学ぶことはより良くなることを意味します)。

だから問題は...未知の構文を読むことができますか?エレガンスは驚きの価値がありますか?少なくとも、あなたが挙げることができるトレードオフやガイドラインは何ですか?プログラミングの専門家のコミュニティからの意見が本当に必要です。

15
ulidtko

リファクタリングされた例は客観的に優れています繰り返しコードが少ないためです。したがって、その実装は実際にはsimplerであり、おそらく短くなります。また、例の元のコードが traits (または戦略パターンの扱いにくい表現)のアドホック実装であり、結果が不必要に複雑なコードであるという議論もあります。

この場合、ほとんどの開発者がデコレーターを知らない場合でも、デコレーターを使用する必要があります。これにより、コードの一部に1つの余分な概念のみが追加されます。これは、プログラミングが一般に豪華な新しい概念に富んでいることを考えると、非常に安価です。必要な情報は、この機能を説明する簡潔なコメントに追加できます。また、関連するドキュメントへのリンクも含める必要があります。

他の言語機能については、同じ考慮事項を行う必要があります。

  • どちらのバージョンがよりシンプルで、問題のドメインの言語により近いソリューションを表現していますか?
  • 将来のメンテナを教育するコストはどのくらいですか?すべてを短いコメントで説明できますか、それともカテゴリー理論についての講義を開催する必要がありますか?
  • 将来のメンテナを教育しないコストはではありませんか?ここに2行追加するか、人生の終わりまで赤ちゃんのコードを書く必要がありますか?

    男に魚を与え、あなたは彼に一日餌を与えます。
    釣りをし、彼の人生のために彼を養うように男を教える。
    プログラマーのためにコードを調整すれば、プログラマーはすぐにそれを理解できます。
    プログラマに高度なテクニックを教えると、プログラマは将来のコードをすべて自分で理解できるようになります。

19
amon

これは、ケースバイケースで行う必要があるものです。言語の見慣れない機能を使用すると、明快さが向上し、将来のメンテナンスが容易になると私は主張します(どちらの例も私の意見では実現します)。

never機能を使用するのは、賢くなりたい、または機能を使用したいという理由だけです。一方で、チームの他の開発者がその使用方法をまだ知らないからといって、コードを改善する機能の使用は避けてはいけないと思います。彼らが彼らの塩の価値があるなら、彼らはすぐにそれを学び、アイデアにメリットがあれば自分でそれを使うでしょう。これまでに見たことも使用したこともないという理由だけで優れた手法を採用することを拒否した開発者は、大きな問題を提起する必要があります。

8
Evicatos

まず、コーディング規約は、言語の使用に関する制限が保存される場所だと思います。そうしないと、チームにとってなじみのあるものとそうでないものについて、人々はいつも異なる考えを持つことになります。

サブセットを選択すると、新しい言語が作成されます。それは確かに「ホスト」言語と多くのことを共有しますが、異なります。簡単になるものもあれば、難しいものもあります。

サブセットを定義する理由:

  1. ホスト言語には危険と見なされる機能があります(C++のプリプロセッサを制限するなど)
  2. ニーズが言語能力よりもはるかに小さい(JavaScriptのサブセットとしてのJSONなど)
  3. 特定のニーズがある(たとえば、最新の言語機能をサポートしていない古いバージョンのコンパイラをサポートしている)

言語全体にこだわる理由:

  1. 新機能は理由があります(例:あなたのケース)
  2. より良いサポート(「LISPでそれを解決する方法」は「ラムダ関数なしのLISPでこれを解決する方法」よりも速く回答されます)
  3. 新しい開発者をより簡単に採用する(サブセットについては、候補者はand自分の制限の下で快適に仕事ができるはずです)
  4. あなたは言語全体があなたよりも優れた言語デザイナーによって設計されたと信じています:)
2
maxim1000