2012-01-10 13 views
2

インターフェイスの可能性とメッセージ構造が異なるさまざまなデバイスからメッセージを読み取る関数を実装する必要があります。私は戦略パターンを使用するオブジェクト指向言語では、私のメインでCのデザインパターン - 複数のデバイスとインターフェイスからの読み取り

例:

Device_A { 
    message type: A 
    iface 1: tcp 
} 

Device_B { 
    message type: B 
    iface 1: serial 
    iface 2: tcp 
} 
... and so on 

...

struct msg_data; 
while(user_wants_to_read) { 
    read_msg(); // reads and sets data in msg_data 
    do_work(msg_data); 
} 

(ただし、メッセージはかなり同じデータを持っています)。私はvoid* read_func;でこれを行うことができると思いますか?

私はCでの経験がありません。良いCプログラマーのように、これをプログラムする方法を学びたいと思います。どのようなデザインパターン/機能を実装すればいいですか?

+0

私はread_funcだけではなく、* read_func、* open_func、* close_func、* seek_func、おそらく* write_funcで構造体を作ります。 – rsaxvc

答えて

3

解決するために2つ以上の異なる抽象があるようです。

  1. 異なるストリームソース(TCPとシリアル)。デバイスAとデバイスBのTCPプロトコルは同じですか?

  2. 構造的に異なるが、意味的に同じ異なるメッセージタイプ。

  3. 異なるデバイスクラス(デバイスB VS装置A)

Iは、ストリームから読み取るための工場と戦略パターンに焦点を当てます。メッセージオブジェクトにデータを追加するためのアダプタパターンや戦略パターンなどがあります。しかし、私は "どんなデザインパターン"につかまえていません。おそらく、インターフェイスの面で考えてみてください。

したがって、おそらく、シリアルとTCPストリーミングを同じインターフェイスでさまざまな実装に抽象化することができます。メッセージの内容に関係なく、TCPソケットからバイトをどのように接続して読み込むかを知っている実装。シリアルポートから読み取る方法を知っている別の人。彼らは同じ "インターフェース"を持つべきです。軽量化された "バイトストリームインターフェース"の例です。いくつかのハッキングされたソケットコードがスローされています。これがコンパイルされないなら、私を許してください。私は誤ってCでC++で誤字があるかもしれません。いずれの場合でも、それは関数テーブルポインタを介してインターフェイスを示す単なる例です。

これを提案する私の考えは、「これをC++でどのように実装するのですか?そして私は純粋な "C"に私の答えを移しています。 (注:私はおそらく、以下のいくつかの宣言ミスを作ってるんだ。)

struct ByteStreamer; 

typedef int (*ReadFunc)(ByteStreamer*, char* buffer, int count); 
typedef int (*OpenFunc)(ByteStreamer*, char* url); // maybe 'open' isn't needed if it's handled by the factory 
typedef int (*CloseFunc)(ByteStreamer*); 
typedef void (*DisposeFunc)(ByteStreamer*); 

typedef struct _ByteStreamer 
{ 
    ReadFunc readfunc; 
    OpenFunc openfunc; 
    CloseFunc closefunc; 
    DisposeFunc dispose; 

    // private data meant for the "class" 
    void* instancedata; 
} ByteStreamer; 

struct _tcpconnection 
{ 
    int socket; 
    sockaddr_in addrRemote; 
} TCPConnection; 

struct _serialconnection 
{ 
    int filehandle; 
    int baud; 
} SerialConnection; 

// --------------------------------------- 

ByteStream* CreateStreamForTCP(const sockaddr_in *pAddr) // pass additional parameter as needed 
{ 
    ByteStreamer* pStream = (ByteStreamre*)malloc(sizeof(ByteStreamer)); 
    TCPConnection* pTCPConnection = (TCPConnection*)malloc(sizeof(TCPConnection*)); 
    pTCPConnection->socket = -1; 
    pTCPConnection->addrRemote = *pAddr; 
    pStream->instancedata = pTCPConnection; 
    pStream->ReadFunc = TCPRead; 
    pStream->OpenFunc = TCPOpen; 
    pStream->CloseFunc = TCPClose; 
    pStream->DisposeFunc = TCPDispose; 
    pStream->type = STREAM_TYPE_TCP; 
    return pStream; 
} 

int TCPRead(ByteStream* pStream, char* buffer, int count) 
{ 
    return recv(((TCPConnection*)pStream->instancedata)->socket, buffer, count, 0); 
} 

int TCPOpen(ByteStream* pStream, char* url) 
{ 
    // it's up to you if you want to encapsulate the socket address in url or in the instance data 
    TCPConnection* pConn = (TCPConnection*)(pStream->instancedata); 
    int sock = socket(AF_INET, SOCK_STREAM, 0); 
    connect(&pConn->addrRemote, sizeof(pConn->addrRemote)); 
    return (pConn->sock >= 0); // true/false return; 
} 

void TCPClose(ByteStream* pStream) 
{ 
    TCPConnection* pConn = (TCPConnection*)(pStream->instancedata); 
    close(pConn->sock); 
} 
void TCPDispose(ByteStream* pStream) 
{ 
    free(pStream->instancedata); 
    free(pStream); 
} 

を今すぐ同等のシリアルポートの実装と上記のすべてのTCPコードを交換してください。また、ByteStream構造体の "ファイルストリーム"(または "メモリストリーム")バージョンを実装することもお勧めします。これは、より高いレベルのコードの単体テストで非常に役立つためです。

バイトストリームの実装がすべて完成したら、デバイス固有のメッセージを解析してください。

typedef struct _Message_A 
{ 
    // A specific data fields  
} Message_A; 

struct _Message_B 
{ 
    // B specific data fields 
} Message_B; 

struct Message 
{ 
    // commonality between Message_A and Message_B 
}; 

typedef (*ReadMessageFromStream)(MessageReader* pReader, Message* pMsg); // pStream is an in-param, pMSg is an out-param. 

typedef (*MessageReaderDispose)(); 

struct MessageReader 
{ 
    ReadMessageFromStream reader; 
    MessageReaderDispose dispose; 

    // ----------------------------- 
    ByteStream* pStream; 
    void *instancedata; 
}; 


// function to read a "Message_A" from a stream - and then transpose it to the generic Message type 
int ReadMessage_A(ByteStream* pStream, Message* pMsg); 
// function to read a "Message_B" from a stream - and then transpose it to the generic Message type 
int ReadMessage_B(ByteStream* pStream, Message* pMsg); 

だからReadMessage_AとReadMessage_Bの実装については本当にクールだ、あなたがバイトストリームの「ファイルストリーム」の実装を渡すと、いくつかの本当に良いユニットテストを作ることができるということです。したがって、TCPまたはシリアルバージョンを接続すると、TCPとシリアルコードが別々にテストされていると仮定すると、動作する可能性が高くなります。

そしてユーバーReadMessageFromStream作成するための各クラスオフおそらくファクトリメソッド:

MessageReader* CreateTCPReaderForDeviceA(DeviceA* pA, sockaddr_in* pAddr) 
{ 
    MessageReader *pMR = (vMessageReader*)malloc(sizeof(MessageReader)); 
    pMR->pStream = CreateStreamForTCP(pAddr); 
    pMR->pStream->Open(); 
    pMR->reader = ReadMessage_A; 
    return pMR; 
} 

MessageReader* CreateSerialReaderForDeviceB(DeviceB* pB, int comport) 
{ 
    MessageReader *pMR = (vMessageReader*)malloc(sizeof(MessageReader)); 
    pMR->pStream = CreateStreamForSerial(comport); 
    pMR->pStream->Open(); 
    pMR->reader = ReadMessage_B; 
    return pMR; 
} 

そして、あなたのメインループは、次のようになります。

if ((type == DEVICE_A) && (source == TCP)) 
    pReader = CreateTCPReaderForDeviceA(pDevice, &addr) 
else if ((type == DEVICE_B) && (source == SERIAL)) 
    pReader = CreateSerialReaderForDeviceB(pDeviceB, 1); 

// read the message 
Message msg; 
pReader->reader(pReader, &msg); 
pReader->Dispose(); // free all the data allocated and close connections/files 

Woohを.... Iこの点を打つのはうんざりです。お役に立てれば。

+0

うわー、非常に徹底的な答えをありがとう! – Nate

0

@rsaxvcと同意します。関数ポインタはおそらくこれについて行くための最善の方法です。 Google検索では、このめくれ:Strategy pattern in C

0

をそして、あなたのメッセージ構造体のために、あなたはオブジェクト指向のクラス継承

struct base { 
    // common members 
} 

struct child1 { 
    struct base; 
    // other data members 
} 

またはsimplelyをエミュレートするために、ネストされた構造体を使用することができます。

struct child2 { 
    // same data members as base 
    // other data members 
} 

は、ベース*パラメータを使用します

関連する問題