9

Ruby では、プログラマーは定義済みのクラスを変更できます。したがって、本当に下手なプログラマーは次のようなことをする可能性があります。

class String
  def ==(other)
    return true
  end
end

明らかに、これほど愚かな人はほとんどいないでしょうが、事前定義されたクラスへのより微妙な変更が、既に機能しているコードに問題を引き起こす可能性があるという考えは、カプセル化の原則に違反しているように思えます。

4 つの質問:

  1. まず、これは実際、カプセル化のオブジェクト指向の原則に違反していますか?
  2. 第二に、プログラマーとして、変更されていないバージョンのクラスで作業していることをコードで保証できる方法はありますか?
  3. 第三に、何らかの理由で、コード内でクラスを「開く」必要がありますか?
  4. 最後に、このようなことは、大規模な本番コーディング環境でどのように処理されるのでしょうか? 言い換えれば、プログラミング業界の人々は、他の人が使用するコードで実際にこれを行っているのでしょうか? または、そうでないとしても、どこかのプラグイン作成者が、プログラムの重要な部分を台無しにするようなことをしていないことをどのように保証しますか?

これはやや主観的な質問であることは承知していますが、このいわゆる「モンキー パッチ」について、より広いプログラミング コミュニティがどのように感じているかを知りたいと思っています。

4

6 に答える 6

10

まず、これは実際、カプセル化のオブジェクト指向の原則に違反していますか?

はい。

第二に、プログラマーとして、変更されていないバージョンのクラスで作業していることをコードで保証できる方法はありますか?

まだ。Ruby 2.0 のクラスボックスが (うまくいけば) 解決策になるでしょう。

第三に、何らかの理由で、コード内でクラスを「開く」必要がありますか?

最後の手段としてのみ。

独自のクラスにモンキー パッチを適用しないでください。まったく意味がありません。あなたはそれらを制御し、そもそもあなたがやりたいことをさせることができます。

ライブラリ内のクラスにモンキー パッチを適用しないでください。(このルールの例外は、何かにモンキー パッチを適用することが唯一の目的であるライブラリです。たとえば、Marc-André Lafortune のbackportsライブラリでは、Ruby 1.8.6、1.8.7、1.9.0、および 1.9.1 にできるだけ多くのモンキー パッチを適用しています。 Ruby 1.9.2 の機能を使用できるようになります。) ライブラリを使いやすくするモンキー パッチを提供するアドオン ライブラリを提供することができます (たとえば、メソッドを提供する暗号化ライブラリがありKryptonite.encrypt(str)アドオンを提供する場合)。メソッド)、ただし、そのアドオンは、ユーザーが明示的にString#encrypt必要とする別のライブラリにある必要があります。それは完全にオプションであるべきです。 require

コア クラスにモンキー パッチを適用しないでください。Arrayこれは RubyやRubyなどのクラスを指しますが、Rails ライブラリの場合は、「コア」ラベルの下Symbolにクラスも含めます。ActiveRecord::Base(上記と同じ警告です。たとえば、Rails 3.0 より前のバージョンでは、明確に定義されたプラグイン API がなく、 Rails を拡張する唯一の方法はモンキー パッチでした。このルールを破った人がいなければ、プラグインは存在しなかったでしょう。 Rails が現在のようになることはありません。)

最初に継承を試してください。最初に構成 (ラッパー、プロキシ、ファサード、アダプターなど) を試してください。最初にリファクタリングを試してください。最初にヘルパー オブジェクトを試してください。それがうまくいかない場合にのみ、モンキーパッチを適用してください。

モンキー パッチを適用するときは注意してください。新しいメソッドを追加する場合は、それがまだ存在していないことを確認し、存在する場合は対処します (たとえば、メソッドから呼び出します)。既存のメソッドをラップしている場合は、他の誰かがすでにラップしている場合はそのラッパーが呼び出され、後で誰かがそれをラップしたい場合は、ラッパーがそれを許可することを確認してください。(特に、これはメソッドの既存のコントラクトを保持する必要があることを意味します。)

可能であれば、モンキーパッチを mixin に入れます。そうすれば、それらは継承チェーンに表示され、コードをデバッグしようとする人は誰でも、何が起こっているのかを理解するための戦いの機会を得ることができます. モンキーパッチは、明らかに名前が付けられた別のファイルに入れます。

最後に、大規模な本番コーディング環境では、この種のことはどのように処理されるのでしょうか? 言い換えれば、プログラミング業界の人々は、他の人が使用するコードで実際にこれを行っているのでしょうか? または、そうでないとしても、どこかのプラグイン作成者が、プログラムの重要な部分を台無しにするようなことをしていないことをどのように保証しますか?

あなたが言うように、「本当に悪いプログラマー」と一緒に仕事をしないでください。

これは単純に聞こえるかもしれませんが、基本的にはこのようになります。はい、もちろん、テストを書いたり、コード レビューを行ったり、ペア プログラミングを練習したり、静的解析ツールを使用したり、警告を有効にしてコードを実行したりできます (たとえば、質問に投稿したコードは を生成しますwarning: method redefined; discarding old ==)。しかし、私にとって、それはすべて、それほど悪いプログラマーがとにかく行うことのすべてです。

于 2010-11-15T16:58:55.247 に答える
4
  1. 場合によっては、はい。1 つのクラスが 1 つのジョブを担当し、1 つのジョブのみを担当するというパラダイムに従う場合、再開クラスを使用すると、多くの場合 (必ずしもそうとは限りませんが) カプセル化が解除されます。しかし、これはルビーの伝統ではないようです。たとえば、Array クラスはリスト、配列、およびスタックとして機能するため、stdlib も厳密なカプセル化に準拠していないようです。好みの問題だと思います。
  2. 方法がわかりません。たぶん、他の誰かが何かを思い付くでしょう。
  3. 私の意見では、他の人が使用するライブラリを作成している場合は、これを行うことは避けたいと思います。アプリケーションを書いていて必要になった場合 (些細な例: 数値の配列に対して平均的な方法が必要です。これは、読みやすさを向上させるか、モンキーパッチを適用しないかの選択です)。
  4. 最も (悪名高い) 有名な実世界のモンキーパッチャーはレールです。そのため、多くの場合、コア クラスへの特に適切な変更を文書化することをお勧めします。はい、テストは役に立ちます。
于 2010-11-15T13:47:10.933 に答える
3

まず、これは実際、カプセル化のオブジェクト指向の原則に違反していますか?

カプセル化は、クラスの使用方法を指示するのではなく、実装の詳細を隠すためにあります。ルビーでは、通常、プライベート変数を尊重します。それを回避したい場合は、ライブラリをアップグレードするときに壊れる可能性のあるハックが何をしているのかを知っています。カプセル化を解除する時間の約90%はテスト状況であり、他の言語でそれを実行できない場合は非常に苛立たしいと思います。

第二に、プログラマーとして、変更されていないバージョンのクラスで作業していることをコードで保証できる方法はありますか?

それは「オープンクラス」全体に違反するようなものですよね;-)

第三に、何らかの理由で、コード内のクラスを「開く」必要がありますか?

それを「最後の手段」タイプのものと考えてください。通常、答えは「いいえ」です。クラス定義を制御する必要がないためです。特定のインスタンスのシングルトンクラスに何かを追加することは、まったく別の話ですが;-)

最後に、この種のことは、大規模な実稼働コーディング環境でどのように処理されますか?言い換えれば、プログラミング業界の人々は実際に他の人が使用するコードでこれを行うのでしょうか?または、そうでない場合でも、プラグインの作成者がプログラムの重要な部分を台無しにするようなことをしていないことをどのように確認しますか?

原則として、ライブラリが別のライブラリを開いている場合は、最後の手段として実行する必要があります(つまり、通常のOO機能では同じことを実行できません)。実行するときは、ライブラリが開いていないことを確認する必要があります。すでに他の誰かによって開かれています。古いalias_method_chainや、ミックスインの使用やスーパーの呼び出しに関する新しいものなど、プロセスをより安全にするために実行できるトリックがあります。

そうは言っても、常に発生するRubyでは、Railsではプラグインを取得する方法です。

私は250klocコードベースの製品に取り組んでおり、モンキーパッチを適用したものがたくさんあります。また、TDDを実践し(locとtest locの比率は1:1.5)、メインラインリポジトリにコミットする前にすべてのテストを実行します。すべてのモンキーパッチは、「config / initializers」で明確にラベル付けされた目的を持つファイルにあり、すべてが完全にテストされています。ここで1年間働いていますが、少なくともその間、モンキーパッチ関連の問題に遭遇したことはありません。

そうは言っても、これは私が今まで参加した中で最高のチームであり、私たちは皆、エクストリームプログラミングに非常に熱心に取り組んでいます。どちらかが当てはまらない場合、レールは良い考えではないと思います。あなたはあなたのチームがルビーと同じくらいの力を持った言語で正しいことをすることを信頼し、そしてあなたができる限り多くのチェックとバランスをとる必要があります。

于 2010-11-15T14:29:53.613 に答える
2
  1. 簡単な答え: はい。より長い答え:ちょっと。カプセル化のポイントは、実際にこの種のことが起こらないようにすることですが、カプセル化は他の言語ではより困難な手段を介して侵害される可能性があります。

  2. テストケースかもしれませんが、繰り返しになりますが、Ruby はアプリケーションを作成するときに癖があることで有名です。特に、Rails のような重いフレームワークを使用する場合は、バージョン 3 が登場するまでグローバル名前空間を汚染し、予期しない状況で奇妙な結果を引き起こすという罪がありました。

  3. この質問の意味がわかりません。

  4. 現実の世界では、開発者はどのパッケージを使用するかを決定します。

追加の注意として、他の開発者は、自分が使用するプログラムを中断することができ、頻繁に実行します。カプセル化は、アプリケーションの一部へのアクセスをロックアウトするソフトウェア機能ではなく、コーダーがコードを直接台無しにするのを防ぐのに役立つ言語機能です。

于 2010-11-15T13:40:32.293 に答える
1

Ruby の現在の経験から、次のことがわかります。

  • はい、外部クラスのプライベート属性を返すメソッドを追加できるため、プログラムはカプセル化を自由に破ることができます。
  • いいえ、それを防ぐためにできることは何もありません。これは言語の機能です。
  • はい、場合によっては、既存のクラスにメソッドを追加することが有用であるか、少なくとも見栄えの良いコードを生成することがあります。たとえば、アプリケーション フィルタリング メソッドを String または Array に追加する場合です。いずれにせよ、これらのメソッドをモジュールで作成し、それらを含めます。私は特にActiveRecordで行われている方法が好きです。ソースを読んでください。すべてが素晴らしくきれいです。
  • 大規模なコードでは、優れた単体テストと訓練された開発者がいない限り、脆弱性の低い言語への切り替えを検討してください (同意しない人もいると思います)。
于 2010-11-15T13:46:41.913 に答える
0

パート 4. では、「選択は壊れない」という原則があります。あなたが使用しているプラ​​グインを多くの人が使用している場合、プラグインが何か悪いことをしている場合、誰かがそれを発見した可能性があります。

繰り返しになりますが、他の誰も使用していないプラグインの組み合わせを使用している可能性があります。

于 2010-11-16T12:02:07.810 に答える