web-dev-qa-db-ja.com

Cでのモジュール性と「カプセル化」

これはCに適用されます(おそらく他の同様の非オブジェクト指向言語にも適用されます)。中央データストアと潜在的に同時アクセスがある場合、それを保護するには2つの方法があります。

いくつかのデータ要素を持つデータストアがあるとします...

struct MyStore
{
    int data1, data2, data3, ...., dataN;
} store[M];

この例ではデータ型は同じですが、これが異なる型などで少し変化したものに適用されると想像してみましょう。これは、質問を単純にするためです。

他のユーザーにデータへのアクセスを許可するには、いくつかのことを行うことができます。

  1. 重要な領域の機能を提供し、呼び出し側が正しい保護を取得することに依存する可能性があります。
  2. ストア内のデータ項目タイプごとにセッターおよびゲッター関数を提供し、モジュール内の重要な領域を管理して、ユーザーがロジックを処理する必要がないように保護することができます。

最初の方法の私の問題は、負担が発信者に課されることです。

2番目の問題は、少なくともCでは、最初にボイラープレートアクセサーの全負荷を書き、2番目にテストと設定のような複雑なものを書き込んだり、いくつかの操作を実行しながらデータのロックを保持する必要があることです。乱雑。例えば。方法2に必要

int GetData1(unsigned int index) { 
    int data; 

    ENTER_CR(); 
    data = store[index].data1; 
    LEAVE_CR(); 

    return data;
}

...
...

int GetDataN(unsigned int index) { 
    int data; 

    ENTER_CR(); 
    data = store[index].dataN; 
    LEAVE_CR(); 

    return data;
}

そして、すべてのセッターについて同じです。そして、複数のアイテムをアトミックに設定したい場合はどうなりますか?大変になる!

モジュール性とカプセル化のいくつかの利点を手に入れながら、柔軟なインターフェースをどうやって得るのですか?

6
Jimbo

スレッドセーフなインターフェース(OOPありまたはあり)を提供する場合、操作がアトミックにしたいレベルであることを確認する必要があります。単一のフィールドの設定または取得が、サポートしたいアトミック操作のレベルである場合、オプション1が機能します。 (一方で、intの取得と設定はとにかくアトミックです)

現実には、単純なデータオブジェクトがスレッドセーフを提供する設計の適切な場所ではない可能性があります。これは、データが使用されている「実際の」操作が何であるかを実際に知らないためです。スレッドセーフをカプセル化することもできますが、これはsingingだけではなく、doing何かがある場所である必要があります。

3
Chris Pitman

Cでは、クリーンなインターフェイスを作成するために別の言語で行うすべてのことを実行できます。必要なのは、少ない言語サポートで実行することだけです。

  1. ボイラープレートアクセサーを避けたい場合は、すべての単純型をunionに格納します。列挙型フィールドID(つまり、unionの配列へのインデックス)による単一のアクセスがあり、呼び出し元は各フィールドの型を心配できます。

    struct MyStore;
    union Atom {
        int i;
        double d;
        /* any other types */
    };
    
    /* access any type by index */
    union Atom *field(struct MyStore *, int id);
    
    • あなたは明らかに他の言語が提供するかもしれないタイプセーフの保証を失い、そしてここでのクライアントエラーは、特にあなたのユニオンの値の1つがポインタである場合、厄介である可能性があります
    • 安全性を高めたい場合は、これを差別化されたユニオンにすることができます。また、typeタグを手動で確認してもかまいません。
  2. 複数の操作をアトミックイベントにバンドルする場合は、次のようにすることができます。

    struct MyStore;
    struct BatchOperation;
    
    struct BatchOperation *startBatch(struct MyStore*);
    void endBatch(struct BatchOperation*);
    
    /* force client to call startBatch before accessing */
    union Atom *field(struct BatchOperation *, int id);
    

    ここでは、バッチ操作を開始するとストアがロックされ、終了するとロックが解除されます。

    • 繰り返しますが、BatchOperationライフタイムの自動管理は行われないため、クライアントコードmustはバッチを終了するか、デッドロックを引き起こします

PS。上記で使用されている不透明なタイプに注意してください。unionおよびpublic関数のみがクライアントコードに完全に表示されます。これは実際にはカプセル化であり、恐怖の見積もりは必要ありません。

4
Useless