是否有人知道任何提供在C上下文中尝试执行模型视图控制器设计模式的直接示例的资源?特别是嵌入式系统?
为了澄清,我对C#,C ++,Objective-C,Java,PHP或任何更高级别的语言示例不感兴趣。我想知道人们如何用纯ansi C99甚至C89来解决这个设计模式的问题。也许由于缺乏正式的OOP语言结构,这在C中甚至没有意义?
某些背景信息:我的同事和我正在研究基于Arm的PSoC芯片驱动的嵌入式系统。我们可以控制硬件设计和PCB,并且必须进行软件开发以增强我们产品的功能集。我们的模型通常包括从产品中的模拟到数字转换器的数据采集。视图可以是由嵌入式Web服务器供电的网页,或者是具有电容式触摸控制的LCD屏幕。我们的控制器或多或少是管理这两个代码区域之间关系的胶合逻辑。我们有许多不同的产品和变体来支持,因此需要重用代码。
不寻找高度详细或企业级的框架。但是相当简单的例子说明了分离编程问题的好策略,但偏向于在较低级别C中找到的习语,例如结构,函数,事件驱动逻辑和一种在C中有意义的抽象消息传递。
由于硬件的性质,我们需要使用C并且必须自己引导很多东西。在某些情况下,我们可以访问操作系统,在其他情况下,只需直接编译到处理器并从主函数开始。所有非常原始的,但寻找允许代码重用的方法,并希望加快软件工程过程。
答案 0 :(得分:61)
Pshew ......这可能是一个很长的答案......但是这里......
首先,让我们从这句话开始:
Maybe this doesn't even make sense in C because of the lack of formal OOP language constructs?
该声明无法不同意。正如我稍后会展示的那样;仅仅因为C没有像#34; 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
中的内容,因为实施细节位于&#34;内部&#34;头文件,而不是&#34; public&#34;头。 那么如果我们想要一个&#34;子类&#34;那该怎么办? 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);
}
现在,我应该提一下,就像我们制作&#34;初始化者&#34;它实际上并没有调用malloc
,而是负责初始化成员变量 - 我们也真的需要解除分配器 - 它实际上不会释放结构 - 而是免费/释放任何&#34;拥有&#34;但是......我实际上会在下面的部分提到一些内容,这可能解释了为什么我还没有为此烦恼。
您现在应该注意 - 因为我们FooSubclass
的第一个成员实际上是Foo
结构 - 所以对FooSubclass
的任何引用也是有效的引用Foo
- 意味着它几乎可以在任何地方使用。
然而,这有一些小问题 - 就像我之前在段落中提到的那样 - 这种技术实际上并没有让你改变基类的行为。 (例如,我们要为解除分配我们的实例而做的事情)。
让我们说我们有一些方法 - 我们会提出一个随机的BS示例 - 名为calculate
。
我们希望在calculate
上调用Foo
来返回一个值 - 但如果在FooSubclass
上调用它,则会返回不同的值。
这在C中很简单 - 它实际上只是创建一个实际调用函数指针引用的函数的包装器方法。 OOP语言在幕后为您执行此操作,通常通过所谓的VTable来实现。
以下是一个例子(我将停止提供完整的示例,而是专注于相关部分):
首先我们定义方法的签名。我们在这里说&#34; calculateMethod&#34;是:指向一个方法的指针,该方法接受一个参数(一个指针)并返回一个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;
// ...
}
现在我们为子类创建一些覆盖此方法的方法 - 例如,在名为Foo_Internal.h
的{{1}}文件中声明的方法 - 瞧!可以在子类中重写的方法。
void FooSetCalculateMethod(Foo * this, calculateMethod value);
好的 - 所以,Model可能是最容易实现的东西 - 简单&#34;类&#34;它们被用作数据存储机制。
你必须为你的特定场景找出一些东西(作为一个嵌入式系统,我不确定你的确切限制是什么 - 以及你是否担心RAM /持久性/等等) - 但我想你不要让我潜入那里。
Our model would typically consist of data acquisition from Analog to Digital converters in the product.
对于物质事物,您可以查看&#34;可能是控制面板上的固定按钮 - 或者,就像你说的,它可能是一个LCD或HTML。
这里的底线是你只需要能够向你的系统的其余部分展示简单&#34;用于在视图中显示/更改内容的界面 - 并将IO的详细信息封装到用户。
通常&#34; I&#34; &#34; IO&#34;的一部分在视图中至少需要一些小的代码。
我不认为这是理想的 - 但是,在大多数情况下,没有一个好方法可以让你的观点&#34;代理用户输入回控制器。也许你的系统有一个很好的方法 - 鉴于你有完全控制。
我希望您现在可以看到如何轻松创建一些与您的需求相关的视图类。
The views might be a web page powered by an embedded web server, or else an LCD screen with capacitive touch control.
这通常是应用程序的内容。在给定时间,您可能需要多个控制器 - 一个用于传感器数据的入口/处理,一个或多个用于您已激活的UI,以及可能的其他UI。
无论如何,我希望有帮助...我觉得我现在正在写一本书,所以我会停下来。
如果您想要更多,或者根本不需要,请告诉我。
答案 1 :(得分:6)
我的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);
}
你可能会比这更有趣。如果在一个控制器上有多个视图,则需要像观察者模式这样的视图来管理视图。但有了这个,你就有了可插拔的视图。实际上我可能会更加严格,只允许通过函数更改模型,并且视图'display'函数指针也只能通过函数调用(我直接调用它们)。这允许各种钩子(对于初学者,检查模型或视图/函数指针是否为空)。我遗漏了内存管理,因为它不是很难添加,但让事情看起来很乱。
答案 2 :(得分:4)
我对人们可能提出的建议感兴趣,但我认为你已经敲定了头 - 由于缺乏正式的OOP结构,它可能没有意义。
然而; 可以将OOP概念引入ANSI-C;我已经有一段时间链接到这个PDF了,虽然我从来没有真正吸收它(因为我的日常工作中没有接触过C),它看起来确实很有成效:
http://www.planetpdf.com/codecuts/pdfs/ooc.pdf
这是一项艰巨的任务,但你最终可以提出某种模板/框架工作,这样可以很容易地编写MVC风格的进一步开发;但我认为权衡是 - 你能负担得起时间吗?嵌入式平台的局限性是由于缺乏性能/内存保护/垃圾收集以及必须重新发明轮子的绝对努力而超过了MVC提供的清晰度的好处吗?
祝你好运,我很想知道你的想法!
编辑:
作为一个后来的想法,或许只是拥有OOP的一些技术而不进行完整的MVC实现可能有助于解决您的问题 - 如果您可以使用Interfaces实现适当的多态层次结构,那么您可以使用它。 d对代码重用的目标已经走了很长的路。
这个其他的stackoverflow涉及在Ansi C中实现OOP,它是一个有趣的读物,但链接到相同的pdf:Can you write object-oriented code in C?