純粋な魂を伴う汚いプログラミング:発見的システムの開発(パート2)

この記事の最初の部分では 、私はダーティと呼ばれる複雑なヒューリスティックソフトウェアシステムについて説明しました。 このパートでは、このようなシステムでの作業の実際的な側面について説明します。



ヒューリスティックシステムの恐ろしい複雑さについて話しました。 それは生と死に関するものです。システムの品質を改善するために支払う複雑さが増大するか、急速に増大します。 2番目のケースでは、小さな改善でさえ毎回ますます難しくなり、アキレスは決して亀に到達しません。 最初のケースでは、スープを食べる時間があります。





ゴム手袋



泥の中を突くのは保護するのに便利です。したがって、システムを正常に開発するには、衛生基準に準拠する必要があります。 ここでは多くのことが同僚の推奨事項を反映しているので、必要な対策について簡単に説明します。



テストシステム。 プログラムの振る舞いを総合的に研究し、隠されたパターンを明らかにするために必要です。 理想的には、指先で何が起きているのかを感じることができるはずです。 システムに、調査対象プログラムの動作のさまざまな側面を識別するための一連のメトリックが装備されていると便利です。 テストシステムの作成と開発、および参照データベースの準備と拡張に真剣にリソースを費やす準備をします。 プログラムを変更するたびにテストシステムを使用します。



ロギングシステム。 この特定のケースでは、プログラムの複雑さとヒューリスティックの相互作用を迅速かつ便利に理解する必要があります。 解決する問題に応じて、ロギングシステムの外観は異なる場合がありますが、テキストファイルへのダンプ以上のものになることを期待する必要があります。 デブリーフィングに頻繁に対処する必要があるため、利便性に注意してください。



ドキュメントシステム。 ここではすべての手段が優れていますが、最も効果的で毎日のように思えます-コードに関するコメント。 反映することを恐れないでください。 ヒューリスティックに関するコメントでは、それらを書くときに頭の中にあった状況を説明する必要があります。 たとえば、「長いセパレータは装飾的な要素であることが多いため、あまり多くの罰金を科すことはできません」または「L字型の誤挿入に対する大まかな保護」。 これは、非常に高いU字型の真のタイインの場合に機能します。 これらのケースを分離することを検討してください。」



同僚とのコミュニケーション。 プログラムへの変更およびそれらがどのような影響を与える可能性があるかを通知します。



フォーカス。 これは、非体系的な進化と根本的な利点との違いです。 将来を見据えて、意味のある変更を加えなければなりません。 これを行う方法については、以下で説明します。



コード品質。 偉大な者の誰かは、 「超越性について明確に語らなければならない」と言った。 それが何を意味するにしても、ダーティコードは維持するのが難しいです。 ヒューリスティックのための優れた単体テストを書くことはできません。 多くの場合、エラーを修正するには遅すぎる場合にエラーが検出されます。 作成者がどのような状況を意図しているかを完全に理解するのは難しいため、コードは読みにくいです。 これは、汚れてはならないコードのこれらの側面の品質の向上によって補う必要があります。



責任の分担



ヒューリスティックプログラミングでは、一部のコンポーネントを他のコンポーネントの作業の望ましくない機能に適応させ、コンポーネントを互いの下にラッピングする状況は避けられません。 間接的に相互に影響する 2つのコンポーネントABがあるとします。 コンポーネントAのわずかな変更でコンポーネントBがその動作の欠陥がより顕著になる新しい条件下で動作し始めたとします。 その結果、 Aが改善されても、変更は受け入れられないことがあります(記事の最初の部分の分類子を使用した例を思い出してください)。 もちろん、この場合、 Bに変更を加えて、このコンポーネントが新しい条件下でより良く動作するようにする必要があります-しかし、私たちは現実の世界に住んでいます。 その結果、そのような変更のみがAに導入され、 Bの欠陥は現れません。 そして、 Bの粗さと粗さに暗黙的に適応し始めます その後、欠陥Bを修正するのは簡単ではありません。AはすでにBの不快な機能に適応しており、現状を変更すると作業が中断されます。 Bを直接使用するコンポーネントCを作成する必要がある場合、頑固なBを編集するよりも、 Bのエラーを明示的に考慮して自分で修正するCを記述する方が簡単な場合があります 新しい嗜癖が現れ、今ではBを改善する可能性は非常に幻想的になっています。 寛大な手で、コンポーネントDEなどを絵に描き、それらの相互作用も思い出します。 システムの変更が有害な状況になるのは簡単です。



プログラマの仕事は、このような望ましくない研削の状況を最小限に抑えることです。 これを行うには、コンポーネント間の暗黙的な依存関係を常に監視します。 変更によってもたらされる副作用を慎重に特定して分析し、長期的にシステムが動作するためにそれらがどれほど重要であるかを判断します。 プラスの効果をもたらす変更を加えないでください。ただし、ロジックを「ぼかし」、相互作用を複雑にします。 汚れたコンポーネントには、定義上、曖昧な責任領域があります。 あなたの仕事は、それらをフレームワーク内に保持することであり、可能であれば、他の人の仕事を許可したり、単に彼らの精神の特徴ではありません。



深い浸透



場合によっては、システムが完全に機能しなかったとします。 チェーン内のコンポーネントの動作を最後から最初まで追跡し、不適切に使用されているコンポーネントを特定して修正できます。 しかし、おそらく本当の問題はまったく別の場所に隠されていたのでしょう。 コンポーネントは、システムの他の部分が最高のパフォーマンスを得るための条件を作成すると暗黙的に想定していましたが、そのような条件は作成されませんでした。 システムのそのような部分が存在し、そのように動作するはずだったと誰でもどうして推測できますか?



残念ながら、「知っていなければならなかった」より良い答えはありません。 したがって、システムを適切に構成するには、プロセスに影響を与えるコンポーネントの操作に関するすべての暗黙の合意を知る必要があります。 何かを知らなかった場合、即座の災害は発生しません-相互作用をできるだけ複雑にします。これは、私たちが知っているように、ヒューリスティックなシステムの早すぎる死に満ちています。



したがって、汚いプロジェクトで作業を開始する開発者のトレーニングのための誇張された要件。 システムを十分に研究していないプログラマーは、たとえヒューリスティックの品質を改善する優れたコードを書いたとしても、それを傷つける傾向があります。 それはばかげたことです。文書分析システムを使い始めたばかりのとき、私は...いや、それについて話すのは恥ずかしいです。



接続の解明



分割統治は、複雑なシステムを管理するための強力な原則の1つです。 コンパクトでありながら強固に接続されたシステムを持つことは、システムがより大きくてかさばるシステムよりもはるかに悪いですが、相互作用は想像力によってカバーされます。 結びつきを解くときに必要な悪は、システムを膨張させ、コードの量を増やすことです。



複雑さを処理する方法は一般によく知られていますが、ヒューリスティックプログラミングの世界では、通常の世界では汚いと考えられる多くの効果的な手法があります。 これらのトリックのいくつかを以下で説明します。



コピーアンドペーストしたら



従来のプログラミングでは、コードの重複は当然非難されます。 ヒューリスティックプログラミングでは、この手法は、相互作用のもつれを解くときに、最も害が少ない場合があります。



ダーティプログラミングのコンポーネントには曖昧な責任領域があり、依存関係があるとこの領域が変形する傾向があることを思い出してください。 コンポーネントBCが使用するコンポーネントAがあるとしますBCAを異なる方向に「引っ張る」傾向があることがわかります(2つのコンポーネントのそれぞれがAとは異なる何かを必要とします)。 Aが負荷耐えるためには、負荷を複雑にする必要があります(たとえば、追加のパラメーターを入力します)。 その結果、単純にコードAをコピーしてコンポーネントA_for_BおよびA_for_Cに変換するよりも、複雑さは受け入れ難いソリューションになることがあります 。 (ちなみに、コピーは進化の主な創造的ツールの1つです。最初は、遺伝子が偶然に単純に複製され、そのコピーの1つが新しいタスクに適応することがよくあります。)



コピーする場合の主なリスクは、新しい変更をコピー間で同期することが難しいことです。 ファジーコンポーネントの場合、これは多くの場合必要ありません。コピーを自由に遊ばせることができます。 もちろん、すべてのコピーに有用なアイデアをもたらすことは素晴らしいことであり(本質的にこれは収束進化と呼ばれます )、これを忘れることは不快です。 このマイナスは、重要な利点よりも重要です。各コピーは、ユーザーにより密接に「擦り付けられる」ことができます。 コンポーネントのラッピングの危険性について説明しましたが、クライアントとユーザーの比率が1対1の場合、ほとんどの被害は平準化されますが、ヒューリスティックはより表現力豊かになります。



数十行のコードを超えない、個々のヒューリスティックのレベルでのコピー&ペーストがよく使用されます。 大量のコードをコピーすることは合理的ではありませんが、いくつかのクラスのサブシステムレベルで正当なコピーアンドペーストを処理する必要がありました。



特別な機会への愛



プログラミングでよくある間違いは、ソリューションが特別なケースを考慮していないことです。 これに対する主な防御策は、決定が最も一般的になるようにし、特別なケースが自動的に発生するようにすることです。 ヒューリスティックプログラミングでは、これは多くの場合、問題を解決する最良の方法ではありません。



特定の種類の画像の画像で検索モジュールを開発する必要があるとしましょう。 幼少期からの最も洗練された非人間的な連続マニアの多くは、残虐な動物虐待の病理学的傾向によって特徴付けられてきました。 この事実は、記事のトピックとは関係ありません-私はあなたが眠りに落ちないことを確認したかっただけです。 そのため、特定の種類の写真の画像で検索モジュールを開発する必要があるとしましょう。 最初は、写真は任意の形状であると想定されています。 したがって、写真の形式に要件を課さないソリューションを作成する必要があります。 それにもかかわらず、このモジュールとともに、長方形の画像のみを検索するモジュールを実装するというアイデアは、非常に生産的です(一般的な解決策は長方形の画像も見つけることです)。 その理由は次のとおりです。 まず、実際の画像では、長方形の画像の割合が非常に代表的です。 第二に、長方形の画像検索モジュールは書くのがはるかに簡単であり、一般的なソリューションに比べて作業の質が向上することを期待できます。 3番目の理由はそれほど明白ではありません。 最も重要なケースだけでなく単純なケースも個別に処理すると、メインモジュールの負荷が大幅に減少します。 ある程度の進化の後、このモジュールは、独自に開発した場合よりも、平均して長方形の写真を探すほうが悪くなる可能性があります。 ただし、自明ではない場合には、より良い品質の作業を期待する必要があります。



動物園作り



Python哲学の原則の1つは、「これを行うための明白な方法は1つ、できれば1つだけでなければなりません」です。 ダーティプログラミングでは、通常の状況では、同じ問題がいくつかの方法で同時に解決されます。



実際、この原則は、上記で説明した一般的な解決策とともに、プライベートソリューションを作成する原則を一般化したものです。 引数も同じです。コンポーネントの負荷を減らすことで、コンポーネントが最も強力なものに集中できるようになります。



問題がいくつかの方法で解決されると、異なる解決策が表示される場合があります。 一般的な手法は、仲裁人として機能し、競合他社の中から最適なソリューションを選択する独立したコンポーネントを作成することです。 そのような裁判官が効率的に機能する場合、少なくとも1つのコンポーネントが「撃つ」ことが期待されます。



決定の多様性の原則の重要性は過大評価されることはほとんどなく、実際に広く使用されています。 たとえば、開発の行き詰まりにあるコンポーネントを徐々に取り除くために使用できます。 それらの制御が失われたと考えることができるほど複雑になった。 道徳的に廃止されたサブシステムをすぐに放棄する代わりに、部分的に置き換えるコンポーネントを導入および開発することで、その責任範囲を徐々に減らすことができます。 並行して、冗長になった部品を削除することにより、問題のあるサブシステムを簡素化できます。 遅かれ早かれ、サブシステムは管理可能になるか、システム全体から簡単に撤退します。



もちろん、議論中の原則には副作用があります。 まず、その使用は深刻なコードの膨張につながります。 第二に、競合するソリューションは、それぞれのコンポーネント間に重要な依存関係を作成しますが、そのような依存関係の害についてはすでに議論されています。



おわりに



もちろん、「ダーティプログラミング」という用語は不正確です(ただし、記事のタイトルが注目を集めています)。 ここでダーティと呼ばれるコードは、通常のコードよりも注意を払う必要がありますが、むしろ注意を払う必要があります。 それにもかかわらず、それを書くことの喜びにもかかわらず、それは良い人生から来ていません。 これはプログラミングの一種のブルートフォースであるという感覚があります。 したがって、ダーティプログラミングは、他のソリューションがまだ発明されていないタスクにのみ必要です。



All Articles