CコンテキストでModel View Controllerのデザインパターンを実行しようとする簡単な例を提供するリソースを知っている人はいますか?特に組み込みシステムはどうでしょうか?
明確にするために、私はC#、C++、Objective-C、Java、PHPまたはそれ以上のレベルの言語の例には興味がありません。このデザインパターンにどのように取り組むかについて人々がどう考えているか知りたいです純粋なANSI C99またはC89。正式なOOP言語構造がないため、これはCでは意味がありませんか?
いくつかのコンテキスト:私の同僚と私は、ArmベースのPSoCチップを搭載した組み込みシステムに取り組んでいます。ハードウェア設計とPCBを制御し、ソフトウェア開発を行って製品の機能セットを強化する必要があります。私たちのモデルは通常、製品のアナログデジタルコンバーターからのデータ取得で構成されます。ビューは、組み込みのWebサーバーを搭載したWebページ、またはLCD静電容量式タッチコントロールを備えた画面です。私たちのコントローラーは、これら2つの間の関係を管理するグルーロジックです。コードの領域。サポートする製品やバリエーションはたくさんあるので、コードの再利用が望まれます。
非常に詳細な、またはエンタープライズレベルのフレームワークを探していません。しかし、プログラミングの問題を分離するための優れた戦略を明らかにするかなり単純な例ですが、低レベルのCで見られるイディオムに偏りがあります。構造体、関数、イベントドリブンロジック、およびCで意味のある一種の抽象メッセージパッシング.
ハードウェアの性質上、Cを使用する必要があり、bootstrap自分自身で多くのことを行う必要があります。また、OSにアクセスできる場合や、プロセッサに直接コンパイルする場合もあります。すべてが非常に原始的ですが、コードの再利用を可能にし、ソフトウェアエンジニアリングプロセスを高速化できるアプローチを探しています。
Pshew ...これは長い答えかもしれません...しかし、ここに行きます...
まず、このステートメントから始めましょう。
_Maybe this doesn't even make sense in C because of the lack of formal OOP language constructs?
_
そのステートメントではこれ以上同意できません。後で説明するように、 Cに "class"のような気の利いたキーワードがないからといって、同じことを達成できないという意味ではありません。
質問の流れに沿って、できる限りこの手順を順を追って説明します。
あなたの質問の言い回しに基づいて、あなたはOOPの概念をかなりきちんと理解していると思います(パターンの観点から考えていて、それらのパターンが特定のシナリオで再生されます)-「CでのOOP」チュートリアルを「30秒以内」で実行します。
コツをつかんだら、ここで紹介することよりも多くのことができることに気づくでしょう。しかし、ただ味わいたいだけです。
まず、基本的な「クラス」から始めます(これについては一緒に行きます)。
Foo.h:
_typedef struct Foo Foo;
Foo * FooCreate(int age, int something);
void FooSetAge(Foo * this, int age);
void FooFree(Foo * this);
_
Foo_Internal.h:(私がこれをすぐに打ち出した理由がわかります)
_#include "Foo.h"
struct Foo {
int age;
int something;
};
void FooInitialize(Foo * this, int age, int something);
_
Foo.c:
_#include "Foo_Internal.h"
// Constructor:
Foo * FooCreate(int age, int something) {
Foo * newFoo = malloc(sizeof(Foo));
FooInitialize(newFoo);
return newFoo;
}
void FooInitialize(Foo * this, int age, int something)
{
this->age = age;
this->something = something;
}
// "Property" setter:
void FooSetAge(Foo * this, int age) {
this->age = age;
}
void FooFree(Foo * this) {
// Do any other freeing required here.
free(this);
}
_
いくつかの注意点:
Foo
の実装の詳細は、不透明なポインターの背後に隠しました。 Foo
の内容が他の人にはわかりません。その実装の詳細は、「パブリック」ヘッダーではなく「内部」ヘッダーファイルにあるためです。では、Foo
の「サブクラス」が必要な場合-追加機能のみを追加しますが、Foo
の代わりに使用できますか?シンプル:
FooSubclass.h:
_typedef struct FooSubclass FooSubclass;
FooSubclass * FooSubclassCreate(int age, int something, int somethingElse);
void FooSubclassSetSomethingElse(FooSubclass * this, int somethingElse);
void FooSubclassFree(FooSubclass * this);
_
FooSubclass_Internal.h:
_#include "FooSubclass.h"
#include "Foo_Internal.h"
struct FooSubclass {
Foo base;
int something;
};
void FooSubclassInitialize(FooSubclass * this, int age, int something, int somethingElse);
_
FooSubclass.c
_#include "FooSubclass_Internal.h"
// Constructor:
Foo * FooSubclassCreate(int age, int something, int somethingElse) {
FooSubclass * newFooSubclass = malloc(sizeof(FooSubclass));
FooSubclassInitialize(newFooSubclass, age, something, somethingElse);
return newFooSubclass;
}
void FooSubclassInitialize(FooSubclass * this, int age, int something, int somethingElse) {
FooInitialize(this, age, something);
this->somethingElse = somethingElse;
}
void FooSubclassSetSomethingElse(Foo * this, int somethingElse)
{
this->somethingElse = somethingElse;
}
void FooSubclassFree(FooSubclass * this) {
// Do any other freeing required here.
free(this);
}
_
ここで、実際にmalloc
を呼び出さないが、メンバー変数の初期化を担当する「イニシャライザ」を作成したのと同じように、実際に構造体を解放しないデアロケータも必要ですが、代わりにfree/release "所有している"参照など。ただし、実際には以下のセクションで、まだ気にしていない理由を説明する可能性があることを説明します。
ここで、FooSubclass
の最初のメンバーが実際にはFoo
構造体であるため、FooSubclass
への参照はFoo
への有効な参照でもあることに注意してください。つまり、ほとんどどこでも使用できます。
ただし、これにはいくつかの小さな問題があります-前の段落で述べたように-この手法では、基本クラスの動作を変更できません。 (たとえば、インスタンスの割り当てを解除するために実行したいこと)。
なんらかの方法があるとしましょう-ランダムなBSの例-calculate
を考えます。
calculate
でFoo
を呼び出すと、1つの値が返されますが、FooSubclass
で呼び出された場合は別の値になります。
これはCでは単純です。実際には、関数ポインターによって参照される関数を実際に呼び出すラッパーメソッドを作成するだけです。 OOP言語は舞台裏でこれを行い、通常は VTable と呼ばれるものを介して実装されます。
ここに例があります(完全な例を示すのをやめ、代わりに関連する部分に焦点を当てます):
まず、メソッドのシグネチャを定義します。ここで、「calculateMethod」とは、1つのパラメーター(ポインター)を受け取り、intを返すメソッドへのポインターです。
_typedef int (*calculateMethod)(void *);
_
次に、いくつかの関数を指すメンバー変数を基本クラスに追加します。
_struct Foo {
// ...
calculateMethod calc;
// ...
}
_
これをFooInitialize
メソッドの初期値で初期化します(基本実装の場合)。
_int FooCalculate(Foo * this)
{
this->calc(this);
}
int FooCalculateImplementation(void * this)
{
Foo * thisFoo = (Foo *)this;
return thisFoo->age + thisFoo->something;
}
void FooInitialize(Foo * this, ...)
{
// ...
this->calc = &FooCalculateImplementation;
// ...
}
_
ここで、サブクラスがこのメソッドをオーバーライドする方法をいくつか作成します。たとえば、void FooSetCalculateMethod(Foo * this, calculateMethod value);
と呼ばれる_Foo_Internal.h
_ファイルで宣言されたメソッドなどです。サブクラスでオーバーライドできるメソッド。
_Our model would typically consist of data acquisition from Analog to Digital converters in the product.
_
OK-そう、モデルはおそらく実装するのが最も簡単なものです-データストレージメカニズムとして使用される単純な「クラス」。
あなたはあなたの特定のシナリオのために何かを理解する必要があります(組み込みシステムであるため、あなたの正確な制限がどうなるかわかりません-あなたが心配している場合RAM/persistence/etc )-しかし、私はとにかくそれについて飛び込んでほしくないと思います。
_The views might be a web page powered by an embedded web server, or else an LCD screen with capacitive touch control.
_
物理的なものについては、「ビュー」はコントロールパネルの固定ボタンである可能性があります-または、あなたが言ったように、LCDまたはHTMLである可能性があります。
ここでの一番下の行は、ビュー内で物事を表示/変更するための「シンプルな」インターフェースを使用してシステムの残りの部分を提示し、IO toユーザー。
通常、「IO」の「I」部分には、ビュー内に少なくともいくらかの小さなコードのくさびが必要です。
これは理想的だとは思いませんが、ほとんどの場合、「ビュー」プロキシユーザーがコントローラーに入力を戻すのに良い方法はありません。多分あなたのシステムでこれを回避する良い方法があります-あなたが完全に制御できるとすれば。
あなたがあなたのニーズに関連するいくつかのビュークラスを作成することにどのように簡単に取り掛かることができるか、今あなたが見ることができることを望みます。
_Our controllers would more or less be the glue logic that manages the relationship between these two areas of code.
_
これは通常、アプリケーションの本質です。センサーデータの入力/処理用に1つ以上、アクティブになっているUIに1つ以上、場合によっては他にも、複数のコントローラーが必要になる可能性があります。
とにかく、それがお役に立てば幸いです...今は本を書いているような気分になるので、やめます。
あなたがもっと欲しいかどうか、それがまったく役立つかどうか教えてください。
私のMVCフレームワーク!
typedef struct
{
int x;
} x_model;
typedef void (*f_void_x)(x_model*);
void console_display_x(x_model* x)
{
printf("%d\r\n",x->x);
}
typedef struct
{
f_void_x display;
} x_view;
typedef struct
{
x_model* model;
x_view* view;
} x_controller;
void create_console_view(x_view* this)
{
this->display = console_display_x;
}
void controller_update_data(x_controller* this, int x)
{
this->model->x = x;
this->view->display(this->model);
}
void x_controler_init(x_controller* this, x_model* model, x_view* view)
{
this->model = model;
this->view = view;
}
int main(int argc, char* argv[])
{
x_model model;
x_view view;
x_controller controller;
create_console_view(&view);
x_controler_init(&controller, &model, &view);
controller_update_data(&controller, 24);
}
あなたはおそらくこれより少し奇妙になるでしょう。 1つのコントローラーに複数のビューがある場合、ビューを管理するためのオブザーバーパターンのようなものが必要になります。しかし、これにより、プラグ可能なビューがあります。私はおそらく実際にはもう少し厳しく、モデルは関数を介してのみ変更でき、ビューの「表示」関数ポインタも関数を介してのみ呼び出すことができます(直接呼び出します)。これにより、さまざまなフックが可能になります(最初に、モデルまたはビュー/関数ポインターがnullかどうかを確認します)。追加するのはそれほど難しくないので、メモリ管理は省略しましたが、見た目が乱雑に見えます。
私は人々がどんな提案をしているかもしれないのか興味がありますが、あなたは頭に釘を打ったと思います-正式なOOP構成が不足しているため、おそらく意味がありません。
しかしながら; itisANSI-Cへの導入OOP概念;これへのリンクがありましたPDFしばらくの間、私は本当にそれを吸収したことがありませんが(私の日常の仕事でCにさらされていないため))確かに実りあるように見えます:
http://www.planetpdf.com/codecuts/pdfs/ooc.pdf
これは大きな仕事ですが、最終的には、MVCスタイルの開発をさらに簡単に書くことができるような、ある種のテンプレート/フレーム作業を思い付く可能性があります。しかし、私はトレードオフがそうだと思います-あなたは時間を割く余裕がありますか?埋め込まれたプラットフォームの制限は、MVCによって提供される明確さの利点よりも、パフォーマンス/メモリ保護/ガベージコレクションの欠如、そしてもちろん、ホイールを再発明しなければならないという多大な労力を上回っていますか?
幸運をお祈りします。あなたが何を思いついたのか、とても興味があります。
編集:
後の考えとして、おそらく完全なMVC実装を実行することなくOOPのテクニックのいくつかを使用することは、問題の解決に役立つ可能性があります-適切なポリモーフィック階層をインターフェースについては、コードの再利用という目標を達成するのに長い道のりがありました。
この他のスタックオーバーフローは、OOPの実装を扱います。興味深い読み物ですが、同じPDFにリンクしています: Cでオブジェクト指向コードを記述できますか?