コードを両方ともできない場合は、乾燥させるか、読み取り可能にしますか?


14

私は単純な暗号化の演習用にRubyコードを書いていますが、このジレンマに頻繁に遭遇しています(この演習は、知っていなければならないソリティア暗号です)。反復を排除したり、エラーの機会を最小限にしたりする簡潔で高密度のステートメントの代わりに、関数を読みやすくする記述変数とシングルステップステートメントでロジックをパディングするかどうかの問題です。

私の最新の例:私のプログラムは入力を受け取り、厳格な形式のガイドラインのために、入力を暗号化または復号化する必要があるかどうかを簡単に判断できます。単純化するには、暗号化キーとメッセージが互換性があるように変換/生成されたら、暗号化されたメッセージからキーを減算するか、暗号化されていないメッセージにキーを追加して、目的の出力を取得します(キーを暗号化として考え、メッセージ+暗号化=コード、コード-暗号化=メッセージ)。DRYの位置は、暗号化されたメッセージを暗号化されていないメッセージとは異なる方法で変換する必要があることを示しています。これは、関数内にネストされたifステートメントが必要であることを意味しますが、ロジックはしっかりしているように見えます。ただし、このコードは簡単には読み込めません。

一方、アプリケーションが暗号化または復号化を決定するときに設定されるフラグに基づいて呼び出される2つの異なる関数を作成できます。これは読みやすくなりますが、暗号化キーをメッセージに適用する(メッセージを暗号化または復号化する)高レベルの機能を複製します。

読みやすいコード、または簡潔なコードに頼るべきですか?または、この機能を取得して両方の原則を満たす別の方法を逃しましたか?プロジェクトの目的を考慮し、その目的を果たすために最善の決定を下さなければならないスケールに沿った位置ですか?

これまでのところ、読みやすいコードよりも簡潔なDRYコードを強調する傾向があります。


2
最初に使用可能、2番目に乾燥、3番目に読み取り可能。高度に使用可能なコードは、DRYを満たし、読み取りやすいコードを非常に読みやすく消費します。これが、あなたが厄介な複雑さを取り、良いAPIでどこかに改善したいのであれば、それを改善したくない場合です。少なくともそれらと相互作用するコードは、このアプローチで同様に悪いことから免れるでしょう。
ジミー・ホッファ

4
いくつかの実際のコード例が役立つかもしれない、私はあなたが乾燥して読み取り可能なコード持っているためにこのような高階関数など、いくつかの第三の方法が欠落している疑いがある
JKを。

2
重複ではありません。簡潔なv。読み込み可能とDRY v。読み込み可能は、2つの非常に異なる比較です。ドライでないことは、簡潔でないことよりもはるかに危険です。
djechlin

予測可能なパターンに陥るので、コードの重複が多いと仮定すると読みやすくなるというthatに陥らないでください。重複が非常に多いシステムを維持して、一方のブランチには微妙なバグがありますが、もう一方のブランチにはほとんどないブランチを見つけるまで、そのように考えるのは簡単だと思います。
KChaloux

「暗号化キーをメッセージに適用する高度な機能」とはどういう意味かわかりません。暗号化と復号化の間に重複があることを意味する場合は、抽出メソッドのリファクタリングを使用して共通部分を除外することを検討してください。
アーロン・カーツハルス

回答:


20

DRYはガイドラインであり、宗教ではありません。それを何よりもDRYのポイントに持っていく人は、あまりにも遠くまで持って行っています。

何よりもまず、使用可能なコードが最も重要です。コードが有用でなく、使用可能でない場合、それは...ではありません。そもそもコードを書く意味はありません。

第二に、いつか誰かがあなたのコードを保守しなければなりません。コードが保守可能でない場合、名前を呪いながら「美しく」、緻密で簡潔なDRYデザインを壊します。これをしないでください。私はその人であり、コード注釈に特定の名前が表示されるたびに、身震いします。

「記述変数」に欠ける密なコードを作成し、ドキュメントなしでラムダを使用してネストされた三項式にすべてを貼り付けることは賢いことです。できることを知ってうれしいです-できません。巧妙なコードはデバッグが非常に困難です。 賢いコードを書くことは避けてください

ソフトウェアに費やされる時間のほとんどは、ソフトウェアのメンテナンスに費やされます-最初にそれを書くのではありません。あなた(または他の誰か)が迅速かつ簡単にバグを修正し、必要に応じて機能を追加できるように、理想的な設計を壊すような変更をほとんど加えずにコードを記述してください。


直観的であるかどうかにかかわらず、あなたは私が見落としていた問題にぶつかった。私はこの演習でできる限り「賢い」コードを書いてきました。できることを知っているだけです。今、私はやるべきリファクタリングがたくさんあります。
ラッツェ

1
@lutze私はperlで、時にはrubyのコーダーであり、そこに巧妙なコードを書く誘惑を確かに理解できます。この回答のポストポストバージョンには、hubrisの Larry Wallからの引用が含まれています。これは、いつか維持されるコードを扱う際に非常に重要な美徳だと思います。言語が簡潔であるため、巧妙な高密度コードを維持しようとするよりも、より多くのコードを書く利点を得るのが難しい場合があります。

17

あなたがDRYを理解しているという質問に基づいて私は確信していません。DRYコードは簡潔とは異なります。かなり頻繁に反対です。

ここで、この例で何が大事なのかわかりません。暗号化する関数、復号化する関数、一般的な機能のヘルパー(ミックスバイト)、および入力を取得して暗号化/復号化を決定する単純なフロントエンドを作成します。

ただし、一般に、コードの維持と拡張を支援するために、DRYと可読性の両方が存在します。すべてのシナリオが同じというわけではありません。少しの繰り返しを削除するための大きな読みやすさは良くありません。また、少し読​​みやすくするための重複もありません。

押された場合、読みやすさを好みます。重複したコードは引き続きテストできます-読み取り不能なコードは、間違ったことを実行(およびテスト)することにつながります。


すばらしい点は、DRYの原則と簡潔なコードの目標を実際に少し組み合わせたことです。
ラッツェ

12

私はあなたの特定のケースに精通していませんが、いくつかの一般的なガイドラインを与えることができます。

読みやすさとDRYの両方の目的は、保守性です。

保守性は、ほとんどの場合、コードを記述するよりもコードの保守に多くの時間を費やすことが重要です。これは、コードの作成を特別な種類のメンテナンスと見なす場合に特に当てはまります。

残念ながら、DRYはしばしば誤解されます。これは部分的には非常に単純に見えるからですが、多くの単純なことのように、よく...複雑です。

DRYの意図は、すべての機能ユニットが1か所に存在することです。原則に従えば、その機能の変更または検証を担当するコード管理者は、一度でコードを処理できます。DRYに従わない場合、機能のコピーが適切に維持されないという非常に現実的な危険があります。

DRYの最も露骨な違反は、コピーアンドペーストコーディングです。この場合、コードのブロック全体がコードベース内で繰り返されます。これは私の経験では驚くほど一般的であり、読みやすさを増す方法はありません。したがって、コードをリファクタリングして共通メソッドを導入すると、常にDRYの適合性と可読性が向上します。

2番目に明白な違反は、「コピーして貼り付けて少し変更する」です。繰り返しますが、パラメータを使用して共通メソッドを導入するリファクタリング、または関数をステップに分割し、共通性を抽象化することにより、ほとんどの場合読みやすさが向上します。

次に、機能が重複しているがコードが異なる、より微妙な違反があります。これは常に簡単に見つけることはできませんが、それを見て、リファクタリングして共通のコードを単一のメソッド/クラス/関数にプルすると、通常よりもコードが読みやすくなります。

最後に、設計の繰り返しの場合があります。たとえば、コードベースで状態パターンを数回使用した可能性があり、この繰り返しを排除するためにリファクタリングを検討しています。この場合、注意して進めてください。より多くのレベルの抽象化を導入することにより、読みやすさを低下させる可能性があります。同時に、あなたは本当に機能性の複製ではなく、抽象化の複製を扱っています。時々それは価値があります...しかし、しばしばそうではありません。あなたの指導原則は、「これらのどれがより保守可能であるか」を尋ねることです。

これらの判断を下すときはいつでも、人々がコードの維持に費やす時間を考慮しようとします。コードがビジネスのコア機能である場合、おそらくさらにメンテナンスが必要になります。その場合、コードベースと関連する抽象化に精通している人々がコードを保守できるようにすることを目指しています。繰り返しを減らすために、もう少し抽象化を導入した方が幸せです。対照的に、めったに使用されず、めったにメンテナンスされないスクリプトは、抽象化が多すぎる場合、メンテナーにとって把握しにくい可能性があります。その場合、私は繰り返しの側で誤ります。

また、チームの他のメンバーの経験レベルも考慮します。経験の浅い開発者との「派手な」抽象化は避けますが、より成熟したグループで、業界で認められたデザインパターンを活用します。

結論として、あなたの質問に対する答えは、コードを最も保守しやすいものにすることです。シナリオでそれが何を意味するかは、あなた次第です。


あなたは私の問題の1つを正確に解決したと思います。複製しないようにしようとしていた機能の例は、おそらく抽象化の複製です。
ラッツェ

+1。私は現在、とんでもない程度にコピーアンドペーストを悪用したシステムを扱っています。コピー/貼り付けられたコードから単一のメソッドを抽出して、今日の私のクラスの1つから400行以上を削除しました。残念ながら、900行の方法で見つけました
...-KChaloux

1
2つのコード構造が現在同じことをしているが、一方への変更が一般に他方への変更を意味するものではない場合、コピー/貼り付けは、因数分解された共通機能よりも良い場合があります。コードが文字ごとに2つの同一のメソッドを使用し、異なる目的で同じことを行い、必要な目的に基づいて一貫してメソッドの選択を行う場合、それらの目的のいずれかで後で行う必要がある場合は、適切なものだけを変更しますメソッドは、影響を受けるはずの動作のみに影響します。単一の一般的な方法を使用すると、変更がさらに難しくなります。
supercat 14年

7

DRYにしようとしたときに関数がより複雑で長くなる(読みにくくなる)場合、間違っていることになります。1つの関数に多くの機能を入れようとしているのは、これが2番目の関数で同じコードを繰り返すことを避ける唯一の方法だと思うからです。

コードをDRYにすることは、ほとんどの場合、共通の機能を小さな関数にリファクタリングすることを意味します。その関数のそれぞれは、元の関数よりも単純でなければなりません(したがって、読みやすくなります)。元の関数のすべてを同じレベルの抽象化に保つために、これは他の場所で使用されていない追加の部分をリファクタリングすることも意味します。その後、元の関数は「高レベル関数」になり、小さな関数を呼び出します。また、より小さくシンプルになります。

その結果、ほとんどの場合、コード内のさまざまなレベルの抽象化が行われ、一部の人々はそのような種類のコードは読みにくいと考えています。私の経験では、これは誤りです。ここでの重要なポイントは、小さな関数に適切な名前を付け、適切な名前のデータ型と抽象化を導入することです。そうすれば、「DRY」と「読みやすさ」が競合することはほとんどありません。


2

ここで問題の原因を正確に特定することはできませんが、私の経験では、DRYと可読性の矛盾が見つかった場合、それは何かをリファクタリングする時です。


0

曜日を問わず、DRYではなく読み取り可能(=メンテナンス可能)を選択します。現実には両方ともしばしば整合しており、DRYの概念を取得した人は一般に(ほとんど)読み取り可能なコードを生成します。


2
説明がなければ、他の誰かが反対意見を投稿した場合、この答えは役に立たない可能性があります。たとえば、「曜日の代わりにDRY(=メンテナンス可能)を選択する」などの主張を誰かが投稿した場合、この回答は読者が2つの反対意見を選ぶのにどのように役立ちますか?それをより良い形に編集することを検討してください
-gnat

0

私のキャリアの中で非常に非常に非常に非常にまれに、DRYに対して読みやすさを落としている合法的なケースを見つけました。まず読みやすくします。物事によく名前を付けます。必要に応じてコピーして貼り付けます。

さあ、一歩下がって作成した全体を見てください。まるでアーティストが絵を見て戻っているように。これで、繰り返しを適切に識別できます。

繰り返されるコードは、独自のメソッドにリファクタリングするか、独自のクラスにプルアウトする必要があります。

繰り返しコードは最後の手段でなければなりません。読み取り可能なAND DRYを最初に使用します。繰り返しコードの範囲を制限すると、読み取りが失敗します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.