MVC 用纯 C 实现 [英] MVC implemented in pure C

查看:27
本文介绍了MVC 用纯 C 实现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否有人知道任何资源提供了在 C 上下文中尝试执行模型视图控制器设计模式的直接示例?尤其是嵌入式系统?

Does anybody know of any resources that provide a straight forward example of trying to do Model View Controller design pattern in a C context? And in particular an embedded system?

澄清一下,我对 C#、C++、Objective-C、Java、PHP 或任何更高级别的语言示例不感兴趣.我想知道人们如何看待如何使用纯 ansi C99 甚至 C89 来处理这种设计模式.也许因为缺乏正式的 OOP 语言结构,这在 C 中甚至没有意义?

To clarify, I am not interested in C#, C++, Objective-C, Java, PHP or any higher level language examples. I want to know what people think about how to approach this design pattern with pure ansi C99 or even C89. Maybe this doesn't even make sense in C because of the lack of formal OOP language constructs?

一些背景:我和我的同事正在研究由基于 Arm 的 PSoC 芯片驱动的嵌入式系统.我们可以控制硬件设计和 PCB,并且必须进行软件开发以增强我们产品的功能集.我们的模型通常包括从产品中的模数转换器采集数据.这些视图可能是由嵌入式 Web 服务器驱动的网页,或者是带有电容式触摸控制的 LCD 屏幕.我们的控制器或多或少是管理这两个代码区域之间关系的胶合逻辑.我们有许多不同的产品和变体需要支持,因此代码重用是可取的.

Some context: my co-workers and I are working on embedded systems powered by Arm based PSoC chips. We have control over the hardware design and PCBs, and have to do software development to enhance our product's feature set. Our model would typically consist of data acquisition from Analog to Digital converters in the product. The views might be a web page powered by an embedded web server, or else an LCD screen with capacitive touch control. Our controllers would more or less be the glue logic that manages the relationship between these two areas of code. We have lots of different products and variations to support so code reuse is desirable.

不寻找高度详细或企业级的框架.但相当简单的例子阐明了分离编程关注点的良好策略,但偏向于在较低级别 C 中发现的习语,例如结构、函数、事件驱动逻辑和一种在 C 中有意义的抽象消息传递.

Not looking for highly detailed or enterprise level frameworks. But rather simple examples that illuminate good strategies for separating the programming concerns, but with a bias toward the idioms found in lower level C, e.g. structs, functions, event driven logic and a kind of abstract message passing that makes sense in C.

由于硬件的性质,我们需要使用 C 并且必须自己引导很多东西.在某些情况下,我们可以访问操作系统,而在其他情况下,只需直接编译到处理器并从主函数开始.所有这些都非常原始,但正在寻找允许代码重用并有望加快软件工程过程的方法.

Because of the nature of the hardware we need to use C and have to bootstrap a lot of things ourselves. And in some cases we have access to an OS and in other cases just compile straight to processor and start with a main function. All very primitive, but looking for approaches that allow for code reuse and hopefully speed up the software engineering process.

推荐答案

Pshew...这可能是一个很长的答案...但这里是...

Pshew... this might be a long answer... but here goes...

首先,让我们从这个声明开始:

First, let's start with this statement:

也许因为缺乏正式的 OOP 语言结构,这在 C 中甚至没有意义?

非常不同意该声明.正如我稍后将展示的那样;仅仅因为 C 没有像class"这样的漂亮关键字并不意味着你不能完成同样的事情.

Couldn't disagree more with that statement. As I'll show later on; just because C doesn't have nifty keywords like "class" doesn't mean you can't accomplish the same things.

我会尽量一步一步地完成这个过程 - 遵循你的问题流程.

I'll try to go through this step-by-step as best I can - following your question's flow.

我怀疑,根据您问题的措辞,您对 OOP 概念有相当不错的掌握(您甚至从模式的角度思考,甚至​​对这些模式将如何在您的特定情况下发挥作用有一个很好的了解)场景) - 所以让我在30 秒或更短的时间内"做一个C 语言中的 OOP"教程.

I suspect, based on the phrasing of your question, that you have a pretty decent grasp of OOP concepts (you're even thinking in terms of patterns and even have a good idea of how those patterns will play out for your particular scenario) - so let me do an "OOP in C" tutorial in "30 seconds or less".

一旦你掌握了窍门,你就会意识到你可以做的比我在这里展示的要多得多——但我只是想让你尝尝.

Once you get the hang of things you'll realize there is a lot more you can do than what I'm going to show here - but I just want to give you a taste.

首先,我们将从一个基本的类"开始(跟我一起学习):

First, we'll start with a basic "class" (go with me on this):

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 中的内容,因为该实现细节在内部"头文件中,而不是公共"头文件中.
  • 我们像 OOP 语言一样实现实例方法"——除了我们必须手动传递this"指针——其他语言只是为你做这件事——但这没什么大不了的.
  • 我们有财产".同样,其他语言将以更好的语法包装属性 getter/settings - 但它们在幕后真正做的就是为您创建一些 getter/setter 方法并将对属性"的调用转换为方法调用.
  • We hid the implementation details of Foo behind an opaque pointer. Other people don't know what is in a Foo because that implementation detail is in the "internal" header file, not the "public" header.
  • We implement "instance methods" just like an OOP language would - except we have to manually pass the "this" pointer - other languages just do this for you - but it's not a big deal.
  • We have "properties". Again, other languages will wrap up property getters/settings in a nicer syntax - but all they are really doing behind the scenes is creating some getter/setter method for you and translating calls to the "properties" into method calls.

那么,如果我们想要一个 Foo 的子类"——它只会增加额外的功能——但可以代替 Foo 怎么办?简单:

So what if we want a "subclass" of Foo - which only adds additional functionality - but can be substituted for a Foo? Simple:

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,而是负责初始化成员变量——我们也确实需要解除分配器——它不调用实际上释放结构体——而是释放/释放任何拥有"的引用等.然而......我实际上将在下面的部分中提到一些可能解释为什么我还没有打扰的东西.

Now, I should mention, just like we made "initializers" which don't actually call malloc, but are responsible for initializing the member variables - we also really need deallocators - which don't actually free the struct - but instead free/release any "owning" references, etc. However... I'm actually going to mention something in the section below which might explain why I didn't bother with that yet.

您现在应该注意到 - 由于我们 FooSubclass 的第一个成员实际上是一个 Foo 结构 - 对 FooSubclass 也是对 Foo 的有效引用 - 这意味着它几乎可以在任何地方使用.

You should notice now - that since our FooSubclass's first member is, in fact, a Foo struct - that any reference to a FooSubclass is also a valid reference to a Foo - meaning it can be used as such pretty much anywhere.

然而,这有一些小问题——就像我在上一段中提到的那样——这种技术实际上并没有让你改变基类的行为.(例如,我们想要为取消分配我们的实例而做的一些事情).

However, there are a few small issues with this - like I mentioned in the paragraph before last - this technique doesn't actually let you change behavior of the base class. (Something we'd like to do for deallocating our instance, for example).

假设我们有一些方法 - 我们将提出一个随机的 BS 示例 - 称为 calculate.

Let's say we have some method - we'll come up with a random BS example - called calculate.

我们希望在 Foo 上调用 calculate 以返回一个值 - 但如果在 FooSubclass 上调用它,则返回一个不同的值.

We want calling calculate on a Foo to return one value - but a different value if it was called on a FooSubclass.

这在 C 中很简单——它实际上只是创建一个包装方法的问题,该方法实际上调用由函数指针引用的函数.OOP 语言在幕后为您执行此操作,它通常通过所谓的 VTable 实现.

This is simple in C - it's really just a matter of creating a wrapper method which actually calls a function referenced by a function pointer. OOP languages do this for you behind the scenes and it's usually implemented via what's referred to as a VTable.

这是一个例子(我将不再给出完整的例子,而是专注于相关部分):

Here's an example (I'm going to stop giving complete examples and instead focus on the relevant parts):

首先我们定义方法的签名.这里我们说的calculateMethod"是:一个指向一个方法的指针,它接受一个参数(一个指针)并返回一个int.

First we define the signature of the method. Here we're saying "calculateMethod" is: a pointer to a method which takes one parameter (a pointer) and returns an int.

typedef int (*calculateMethod)(void *);

接下来,我们在基类中添加一个指向某个函数的成员变量:

Next, we add a member variable in our base class which will point to some function:

struct Foo { 
    // ...
    calculateMethod calc;
    // ...
}

我们在 FooInitialize 方法中使用一些初始值初始化它(对于我们的基本实现):

We initialize this with some initial value in the FooInitialize method (for our base implementation):

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 文件中声明的一个名为 void FooSetCalculateMethod(Foo * this, calculateMethod value) 的方法; - 瞧!可以在子类中覆盖的方法.

Now we make some way for subclasses to override this method - say, for example, a method declared in the Foo_Internal.h file called void FooSetCalculateMethod(Foo * this, calculateMethod value); - and voila! Methods which can be overridden in subclasses.

我们的模型通常包括从产品中的模数转换器采集数据.

好的 - 所以,模型可能是最容易实现的东西 - 用作数据存储机制的简单类".

OK - so, Model is probably the easiest thing to implement - simple "classes" which are used as data storage mechanisms.

你必须为你的特定场景想出一些办法(作为一个嵌入式系统,我不确定你的确切限制是什么 - 如果你担心 RAM/持久性/等) - 但我认为你无论如何都不想让我深入研究.

You'll have to figure something out for your particular scenario (being an embedded system I'm not sure what your exact restrictions will be - and if you're worried about RAM / persistence / etc) - but I think you don't want me to dive into that anyways.

视图可能是由嵌入式 Web 服务器驱动的网页,或者是带有电容式触摸控制的 LCD 屏幕.

对于物理事物,您的视图"可能是控制面板上的固定按钮 - 或者,如您所说,它可能是 LCD 或 HTML.

For physical things your "view" may be fixed buttons on a control panel - or, like you said, it could be an LCD or HTML.

这里的底线是您只需要能够向系统的其余部分呈现简单"界面的类,用于在视图中显示/更改内容 - 并将 IO 的详细信息封装给用户.

The bottom line here is you simply need classes which are capable of presenting the rest of your system with a "simple" interface for displaying/changing things in the view - and encapsulate the details of IO to the user.

通常,IO"的I"部分至少需要视图中的一小部分代码.

Typically the "I" part of "IO" needs at least some small wedge of code in the view.

我不认为这是理想的 - 但是,在大多数情况下,将视图"代理用户输入返回到控制器并没有什么好方法.也许对于您的系统,有一个很好的方法来解决这个问题——因为您拥有完全的控制权.

I don't think this is ideal - but, most of the time, there isn't a good way around having your "view" proxy user input back to your controllers. Maybe with your system there is a good way around this - given you have total control.

我希望您现在可以看到如何轻松地创建一些与您的需求相关的视图类.

I hope you can see now how you could easily go about creating some view classes which are relevant to your needs.

我们的控制器或多或少是管理这两个代码区域之间关系的胶合逻辑.

这通常是应用程序的核心.在给定时间,您可能需要多个控制器 - 一个用于传感器数据的输入/处理,一个或多个用于您激活的任何 UI,可能还有其他控制器.

This is usually the guts of the application. You'll likely need more than one controller around at a given time - one for ingress/processing of sensor data, one or more for whatever UI you've got active, and possibly others.

无论如何,我希望这会有所帮助...我觉得我现在正在写一本书,所以我会停下来.

Anyways, I hope that helps... I feel like I'm writing a book now, so I'll stop.

如果您想要更多,或者这是否有帮助,请告诉我.

Let me know if you want more, or if that helps at all.

这篇关于MVC 用纯 C 实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆