2011-01-12 12 views
16

私は、C++を使用してプログラムの主な機能に入るもののベストプラクティスのヒントを探しています。現在私は2つのアプローチが可能であると考えています。 (これらのアプローチの「マージン」は互いに任意に近いことができますが)主な機能は何ですか?

1:メイン関数に渡されるパラメータを受け取り、その「マスター」で完全なプログラムを処理する「マスター」クラスを作成します。クラス(もちろん他のクラスも使用します)。したがって、主な機能は最小限の線に縮小されます。

#include "MasterClass.h" 
int main(int args, char* argv[]) 
{ 
MasterClass MC(args, argv); 
} 

2:もちろん、ユーザー定義のオブジェクトを使用してメイン関数で "完全な"プログラムを書く!しかし、グローバル関数も関与しており、メイン関数は多少大きくなる可能性があります。

私は、プログラムの主な機能をC++で書く方法の一般的なガイドラインを探しています。この問題は、最初のアプローチの単体テストを書くことによって克服しました。ほとんどのメソッドがプライベートなので、少し難しいです。

+0

通常、argsの代わりにargcが書き込まれます。 argcは引数の数のカウントです – Will

答えて

23

なぜあなたはマスタークラスを1つ持っていますか? の責任範囲は何ですか?

"マスター"または "アプリケーション"クラスは、あまりにも多くの異なることを行う大きなブロブになる傾向があります。最終的に、何がポイントですか?何を買ったのですか?

メインは、これを提供する機能を使用しないでください。メインの機能はありますか?高レベルのアプリケーションライフサイクルをmainに定義します。いくつかのコマンドライン引数を取り、それらを解析し(できればこれを別の関数またはクラスに委譲することによって)、いくつかのセットアップ機能を呼び出し、クリーンアップを行う前に何らかのメインループに入るかもしれません。全体として、これはおそらく10-15行の主な機能を提供するかもしれませんが、多分それ以上の機能はありません。できるだけ他のクラスや関数に委譲する必要があります。 main自体は短くて甘いですが、まだ目的があります。

mainにこのような超高レベルフローを配置すると、簡単に見つけることができます。mainが出発点です。コードを理解したいかどうかを調べ始めるところです。読者がコードを理解しようとしているときに知りたいことを、その中に入れます。

確かに、あなたはすべてを取って「メインクラス」に入れることができました。そして、あなたはそこにあるすべてのJava-ludditesを満足させること以外は絶対に何も得られませんでした。 "

+1

引数の解析のみの場合+1。 「モジュラ」コードを記述したい場合は、引数解析(実際のコードとのインタフェースの1つ)を切り離し、適切な構造を使用してこれらのすべての「設定」を集める必要があります。引数の構文解析では、環境変数の使用が含まれている可能性があることに注意してください。ほとんどの場合、デフォルトの設定に使用されるためです。その後、完了/クリーンアップの高レベルのビューまで、些細なセットアップ/ループです。 –

+0

"最終的に、何がポイントですか?"ここでは説明していませんが、依存性注入を提供することができます。例えば、 'std :: cout'に書き込むコードを持つ代わりに、' Master'クラス**は 'standardOutput'ストリーム、' std :: ostream'を関連付けます。 'main()'関数はその関連に対して 'std :: cout'を持つ' Master'オブジェクトを生成しますが、ユニットテストでは 'std :: ostringstream'を使うことができます。 「コマンドラインに障害がある場合、プログラムは標準のエラー出力ストリームにエラーメッセージを書き込む必要があります」などのテスト動作要件を容易にすることができます。 – Raedwald

+0

@ Raedwald:なぜマスタークラスが必要ですか?なぜ、 'main'が適切な' std :: ostream'を作成してそれを委譲するすべてのクラスと関数に渡すことができないのですか?それにはマスタークラスは必要ありません。 – jalf

2

あなたの最初のアプローチは非常に一般的ですが、クラスは「アプリケーション」(または少なくとも「アプリケーション」という言葉が含まれています)という傾向があります。

3

メインルーチンのプロセスの引数の分析を行い、argcやargvよりも読みやすいパラメータを渡してクラスインスタンスを作成します。

6

あなたは2つの「極端な」アプローチについて説明していますが、どちらも私には正しいとは言えません。単一のGod Classを持つことも、単一の神関数を持たせることも、実際の使用を意図した重要でないプログラムを実装する正しい方法ではありません。

(私は、コマンドラインパラメータの詳細からMasterClassを分離するために、例えばmain()で任意のコマンドライン固有の処理を行って、より良いパーティショニング機能を好むだろうが)あなたのmain()MasterClassへの単一の呼び出しを持つことがOKすることができます。しかし、そのクラスが単体テストが難しい場合は、設計上の問題の兆候であり、通常、テスト可能な機能の一部または全部を、他のクラスに委譲して、パブリックインターフェイス。

もう一度ユニットテストに問題が発生する可能性がありますので、細かい単位テストを可能にするためにextract methodsから(そして好ましくはmove them into a separate class)に努力する必要があります。

あなたがなりたいスイートスポットは、コードをテスト可能にするという要件によって制約される2つの極端な間のどこかにあります。

main()のコンテキストだけでなく、プログラムの一般的な構成方法について考える価値があります。基本的な考え方は、理解試験し、容易

  • 論理的凝集を維持するのに十分に小さい

    • ある「チャンク」(クラス及びメソッド)にそれを分割することです。
  • +1

    私は同意しません。入り口がなければなりません。それは、神の機能であろうと神の階級であろうと、少なくとも一つでなければなりません。もちろん、@DeadMGは – Puppy

    +2

    です。しかし、OPの引用:* "ほとんどのメソッドがプライベートなので、最初のアプローチ[...]の単位テストを書くことは少し難しいです。これは私が解釈するので、彼の「マスター」クラスは神クラスであり、どこにも何も委託しない。 –

    +0

    ああ、あなたが何を意味しているのか分かります。はい、私は完全に同意します。 – Puppy

    0

    通常、必要なプラットフォーム固有のセットアップアクションを実行し、オブジェクトに委任します。関数に比べてオブジェクトの利点はほとんどありませんが、オブジェクトはインターフェイスから継承することができます。これは、コールバックのインターフェイス実装を使用するプラットフォームに依存しないライブラリを使用している場合に適しています。

    2

    まずは:私はまれにC++を使用しますが、これは実際には言語特有の問題ではないと思います。
    まあ、これはいくつかの実用性の問題を除いて味わい深いと思います。 私は個人的にレイアウト#1を使用する傾向がありますが、MasterClassにコマンドライン解析ルーチンを入れないでください。私のために、それは明らかにメインに属しています。 MasterClassは、の構文を持つ引数(整数、FileStreamsなど)を取得する必要があります。

    2

    通常、私は自分のアプリケーションの名前空間で(同じ署名whith)メイン関数を呼び出す:

    namespace current_application { 
        int main(int argc, char **argv) { 
         // ... 
        } 
    } 
    int main(int argc, char **argv) { 
        return current_application::main(argc, argv); 
    } 
    

    そして、私は通常、 アプリケーション賢明物事を初期化するために(名前空間内の1つの)私の実際のメインを使用:標準入力/出力/エラーに

    • セットロケール)

    • 解析applicaンパラメータ

    • 私のメインクラスのオブジェクトにインスタンス化、存在する場合(QApplicationのようなものに相当)

    • コール主な機能、存在する場合(QApplication::runのようなものに相当)

    通常、クラッシュの場合にさらにデバッグ情報を印刷するには、trycatchブロックを追加することをお勧めします。


    このすべては非常に主観的ですが、それはあなたのコーディングスタイルの一部です。

    +0

    これはあなたに何が得られるかはわかりません。混乱した保守プログラマーを除いて。 :) –

    +0

    @ジョンディブリング:なぜですか? – peoro

    +1

    C++で何かJava-ishを実行しようとしているからです。このようなアーキテクチャは、 'main()'をネームスペース内のフリー関数に単に置き換えても、何も得られず、予期しないものです。名前空間「main」は、名前空間以外のものは何をしていないのですか? –

    0

    あなたが与えた例は、名前空間の中でmain関数を動かすだけです。私はここで優位を見ません。マスタークラスモデルへのやや傾向のある道路アプローチの真ん中が私のために働いています。マスタクラスオブジェクトは通常、巨大であり、ヒープ上で最もよく作成されます。私はオブジェクトを作成し、ここで発生する可能性のあるエラーを処理するmain関数を持っています。

    class MasterClass { 
    public: 
    static MasterClass* CreateInstance(int argc, char **argv); 
        // ... 
    } 
    
    int main(int argc, char** argv) 
    { 
        try 
        { 
         MasterClass mc = MC::CreateInstance(argc, argv); 
        } 
        catch(...) 
        { 
         // ... 
        } 
    } 
    

    また、これは、そのような環境を読んだ、として、実際のプログラム・ロジックとは何の関係もありません任意の処理などはマスタークラスに入れられる必要がないという利点があります。彼らはmain()に入ることができます。良い例は、Lotus Dominoシステムのサーバーアドインタスクです。ここでタスクは、Dominoスケジューラタスクによって制御がハンドラに渡されたときにのみ実行する必要があります。ここでの主は、おそらく以下のようになります。

    STATUS LNPUBLIC AddInMain(HMODULE hModule, int argc, char far *argv[]) 
    { 
        MasterClass mc = MC::CreateInstance(argc, argv); 
        while(/* wait for scheduler to give control */) 
        { 
          STATUS s = mc->RunForTimeSlice(); 
          if (s != SUCCESS) 
           break; 
        } 
        // clean up 
    } 
    

    スケジューラと対話するためのすべてのロジックは、メインにこれ​​で、プログラムの残りの部分は、それのいずれかを処理する必要はありません。

    0

    例外にハンドラがない場合、ローカルオブジェクトのデストラクタがstd::terminateの前に呼び出されているかどうかは不明です。

    この動作を予測可能にしたい場合は、mainが一番上の例外ハンドラを持っていて、なんらかの報告をするのに適しています。

    通常それは、そうでない場合は、単にcppMain ... ;-)

    乾杯& HTHを呼び出す私はmainに入れているすべての、です。、

    +1

    もう一つのトリックは、グローバルスコープでオブジェクトを持つ代わりに、グローバルスコープでポインタを持つことができます。メインの内部にオブジェクトを構築し(そして、そのために建設順序を制御する)、それらの地方の地点を指すようにポインタを設定してから、 "本物の"メイン関数を呼び出します。 –

    0

    私は私にIOC(制御の反転)アプローチを好みますプログラム。

    したがって、 "main"はコマンド引数を使用してプログラムの "options"と "configuration"を決定します。オプションでは、コマンドラインで渡される特定のフラグを解釈することを意味し、構成によって、構成ファイルのロードを意味します。

    設定ファイルを読み込むために使用するオブジェクトは、「実行する」コマンドを持っていますが、実行されるものは、コマンドライン引数にも依存します。

    関連する問題