2017-12-29 36 views
1

機能を小さくするにはどうすればいいですか?たとえば、ケーキベーキングプログラムがあるとします。機能はどれくらい小さくなければなりませんか?

bakeCake(){ 
    if(cakeType == "chocolate") 
    fetchIngredients("chocolate") 
    else 

    if(cakeType == "plain") 
    fetchIngredients("plain") 
    else 

    if(cakeType == "Red velvet") 
    fetchIngredients("Red Velvet") 

    //Rest of program 

私はそれが雑然となりbakeCake機能に多くのより多くのものを追加するときにこのようなものは、自分自身で十分に単純である一方、私の質問は、です。しかし、このプログラムは毎秒何千ものケーキを焼く必要があると言うことができます。私が聞いたことから、現在の関数で文を実行するだけでは、(コンピュータ時間に比べて)かなり長い時間がかかります。だからこのようなものは読みやすく、効率が重要なのであればその中に入れたいと思うでしょうか?

基本的に、効率性のためにどの時点で可読性を犠牲にしますか?また、どの点で関数が多すぎると読みやすさが低下するのかという簡単なボーナス問題はありますか? Appleの迅速なチュートリアルの例を以下に示します。

func isCandyAmountAcceptable(bandMemberCount: Int, candyCount: Int) -> Bool { 
    return candyCount % bandMemberCount == 0 

彼らは、関数名isCandyAmountAcceptableが、そのための関数を作るために良いだろうとcandyCount % bandMemberCount == 0より読みやすくなりましたので、と言いました。しかし、私の見解からは、2番目のオプションが何を言っているかを理解するのに数秒かかるかもしれませんが、それがどのように動作するかを知ることになると、さらに読みやすくなります。

残念ながら、1つに2つの質問をしています。私の質問を要約するだけです:

  1. 機能を使用すると効率(スピード)が損なわれますか?読みやすさと効率性の間にどのような違いがあるのか​​、どうすればわかりますか?

  2. どのように機能を小さくする必要がありますか?明らかに、もし私が今までにこの機能を繰り返さなければならないのであれば、私はそれらを作っていただろうが、一度使う機能は何か?

みんな、申し訳ありませんおかげでこれらの質問は無知か何かですが、私は答えを本当に感謝している場合。

+0

質問1の答えは、使用している言語と実装(つまり、特定のコンパイラ、インタープリタなど)によって異なります。 – melpomene

+0

関数呼び出しのオーバーヘッドが異なる状況下で重大である場合には、既存の投稿がカバーしていることは確かです。それはiircでもC++のためです。 – Carcigenicate

答えて

2

機能を使用すると効率(速度)が外的に損なわれますか? それが可読性と 効率の間のカットオフをどのように把握することができますか?

パフォーマンスのために、まともなオプティマイザに対する直接関数呼び出しのオーバーヘッドは、一般的に考慮しません。そうでない場合、99.9%のシナリオでは無視できるオーバーヘッドです。パフォーマンス重視の分野でも同様です。レイトレーシング、メッシュ処理、画像処理などの分野で働いていますが、参照の局所性、効率的なデータ構造、並列化、ベクトル化とは対照的に、関数呼び出しのコストは通常​​優先順位リストの一番下にあります。マイクロ最適化を行っている場合でも、ダイレクト関数呼び出しのコストよりもはるかに大きな優先順位があり、マイクロ最適化を行っている場合でも、オプティマイザが実行する最適化をあなたが実際にアセンブリコードを書いている場合を除いて、それと戦い、手でそれをしようとします。

もちろん、いくつかのコンパイラでは、インライン関数呼び出しをしない関数や、すべての関数呼び出しに少しオーバーヘッドがあるものもあります。しかし、そのような場合には、これらの言語やインタプリタ/コンパイラを使用する際に、このようなミクロレベルの最適化について心配するべきではないので、比較的無視できると言います。それでも、参照とスレッド効率の局所性を改善するなど、よりインパクトのあるものとは対照的に、優先順位リストの下位に位置することがよくあります。

非常に単純なレジスタ割り当てを使用するコンパイラを使用していて、使用するすべての変数にスタックが流出するような場合は、できるだけ少ない変数を使用して再利用しようとする必要はありませんその傾向を中心にこれは、無視できないオーバーヘッド(例えば、Cylinderの一部をdylibに書き込んで、最も重要な部分に使用する)や、すべてを実行するようなより高度な最適化に焦点を当てた場合に、新しいコンパイラを手に入れることです並行して。

どのように機能を小さくする必要がありますか?明らかに、私はこれまで関数を繰り返さなければならないとすれば、 を作っていましたが、1回は何回ですか? 関数を使用しますか?

これは、私がやや軽くなっていくところです。実際には、メンテナンス性の理由から十分な機能を避けることをお勧めします。少なくともJohn Carmackは、(コードをインライン化し、副作用を理解しやすくするために副作用が発生した場合の過剰な関数呼び出しを避けるために)少なくともいくらかは同意しているようですが、これは間違いありません。

しかし、多くの状態変更を行う場合は、 をすべてインラインで実行すると利点があります。常にあなたがしていることの完全な恐怖を認識して を作成する必要があります。

私は時々meatier機能の側に誤るために良いことができます信じている理由は、変更を行うか、問題を解決するために必要なすべての情報を理解するために簡単な関数よりも理解する方がしばしばありますされているため、 。

論理が80行のインラインコードで構成されている関数、または数十の関数に分散された関数、おそらくはコードベース全体で異なる場所につながる関数を理解するのは簡単ですか?

答えはあまり明確ではありません。当然ながら、例えば、sqrtabsのように、小さな関数が広く使われている場合、読者は関数呼び出しをスキップして、手の後ろが好きであることを十分に知ることができます。しかし、一度しか使用されていない小さなエキゾチックな機能がたくさんある場合、操作全体を理解する能力は、それらを見て、何が起こっているのかについて適切な理解を得る前に、大きな絵の言葉。

Apple Swiftのチュートリアルでは、実際には算術と比較が何をするかを理解するよりも理解しやすいものですから、何を参照するにはあなたが言うことができないシナリオでは、isCandyAmountAcceptableは私のための十分な情報であり、何が金額を受け入れるかを正確に把握する必要があります。その後、あなたはコード内の異なる場所にジャンプする必要はありませんので、本の類推が原因ブックの他のページへの読者を参照(...

// Determine if candy amount is acceptable. 
if (candyCount % bandMemberCount == 0) 
    ... 

:代わりに、私は実際に簡単なコメントを好むだろう読者は常にページ間を行き来する必要があります)。もちろん、このisCandyAmountAcceptableの機能の背後にあるアイデアは、キャンディーの量を容認できるものにするような詳細については心配する必要はありませんが、実際にはあまりにも頻繁に、最適にコードをデバッグしたり、コードを変更したりする必要があります。コードをデバッグしたり変更したりする必要がない場合は、どのように記述するかは問題になりません。私たちが気にしているすべてのものについて、バイナリコードで記述することさえできます。しかし、将来的にデバッグしたり変更したりするように書かれているのであれば、読者がたくさんのフープを飛ばさなければならないことを避けるのが役に立ちます。これらのシナリオでは、細部はしばしば重要です。

時には大きな絵をパズルピースの中で最も細かいものに分けることで、大きな絵を理解するのに役立つことはありません。バランスのとれた行為ですが、特定のタイプの開発者は、システムを過度に細かく分割し、メンテナンスの問題をそのように見極めることで誤りを犯す可能性があります。これらのタイプは依然として有望なエンジニアであり、バランスをとるだけです。もう一つの極端なのは、500行の関数を書き、リファクタリングを考慮しなくても極端なものです。しかし、私はあなたが以前のカテゴリに適合していると思うし、実際には、ちょっとだけ、パズルピースを健康的なサイズ(あまりにも小さくない、あまりにも大きくない)に保つためにちょっとした機能の面で間違っていることをお勧めします。

コードの重複と依存関係の最小化の間にはバランスの取れた動作があります。交換が数百万行の複雑な数学ライブラリへの依存であれば、複写された数学コードの数十行を削って画像ライブラリを理解することは必ずしも容易ではありません。このような場合、外部の依存関係を避けるために数学関数を複製する代わりに、別の場所に配布するのではなく、その複雑さを分離することを選択すると、画像ライブラリは非常に理解しやすくなり、新しいプロジェクトでの使用と展開も可能になります。

基本的に、効率性のためにどのような時点で可読性を犠牲にしますか?

上記のように、私は小さな画像の可読性と大きな画像の理解度は同義ではないと思います。 2行の関数を読み、それが何をしているのかを知っていて、必要な変更を行うために理解する必要があることを理解することからまだまだ離れていることは本当に簡単です。これらの小さなワンショットの2ライナーの多くは、大きな画像を理解する能力を遅らせることさえできます。

しかし、「理解度と効率」を代わりに使用すると、巨大な入力を処理することが予想される場合には、設計レベルでの前払いと言えます。一例として、カスタムフィルタを備えたビデオ処理アプリケーションは、フレームごとに数百万のピクセルにわたって何度もループすることを知っています。その知識は、何百万ものピクセルにわたって繰り返しループするための効率的な設計を思い付くために利用されるべきである。しかし、それは設計に関して言えば - 大きな中心的な設計変更は遅れて適用するにはコストがかかりすぎるため、他の多くの場所が依存するシステムの中心的な側面に向かっています。

これは、わかりやすいSIMDコードをバットから外して適用しなければならないというわけではありません。これは、設計が、そのような最適化を後見の中で探求するのに十分な呼吸スペースを残すなら、実装の詳細です。このようなデザインは、レベルで抽象化することを意味し、単一のレベルではなく、百万ピクセルのレベルで抽象化することを意味する。IPixel。これは前もって考慮する価値があることです。

その後、ホットスポットを最適化し、操作がより速く進むようにビジネスニーズが強く認識されている真にクリティカルなケースに対して、ここでは理解しにくいアルゴリズムやマイクロ最適化を使用することができます良いツール(プロファイラー、すなわち)を手にして。ユーザーのケースでは、ユーザーが最も頻繁に行うことに基づいて最適化する操作をガイドし、待ち時間を短くしたいという強い希望を見つけます。プロファイラは、その操作に含まれるコードのどの部分を最適化する必要があるかを正確に案内します。

0

読みやすさ、パフォーマンス、および保守性は、3つの異なるものです。読みやすさは、あなたのコードをシンプルで理解しやすくします。必ずしも最良の方法ではありません。最終的な結果が達成された方法よりも重要な非生産環境でこのコードを実行していない限り、パフォーマンスは常に重要になります。エンタープライズアプリケーションの世界に入り、保守性が突然より重要になります。あなたが今日働いていることは、6ヶ月後に他の人に引き渡され、彼らはあなたのコードを修正/変更されます。これが、突然標準デザインパターンが重要になる理由です。ある意味では、可読性は大規模な保守性の一部です。上のケーキベーキングプログラムが、見た目よりももっと複雑なものであれば、最初のことは目立つように目立つものです。それは多形に置き換えられなければならない。スイッチケースの種類も同じです。 あなたはいつ何を犠牲にするのですか?これは純粋にコードが達成しているビジネスに依存します。それは学業ですか?それは、たとえ一見して何が起こっているのかを把握するために90%の開発者の苦闘を意味するとしても、完璧なソリューションになるはずです。それは2つ以上の異なる地理的な場所から働く50人の開発者の分散されたチームによって維持されている小売店に属するウェブサイトですか?従来のデザインパターンに従います。 私はいつもほとんどすべての状況で追跡されてきたと見ています。機能が画面の半分を超えて成長している場合、その機能はリファクタリングの候補です。エディタの長い長さのスクロールバーを持つ機能を持っていますか?リファクタリング!

関連する問題