解決するために2つ以上の異なる抽象があるようです。
異なるストリームソース(TCPとシリアル)。デバイスAとデバイスBのTCPプロトコルは同じですか?
構造的に異なるが、意味的に同じ異なるメッセージタイプ。
異なるデバイスクラス(デバイス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この点を打つのはうんざりです。お役に立てれば。
私はread_funcだけではなく、* read_func、* open_func、* close_func、* seek_func、おそらく* write_funcで構造体を作ります。 – rsaxvc