在纯C中实现MVC [英] MVC implemented in pure C

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

问题描述

有没有人知道任何资源,提供一个直接的例子,试图在C上下文中执行模型视图控制器设计模式?特别是嵌入式系统?



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



某些上下文:我的同事和我正在使用嵌入式系统基于Arm的PSoC芯片。我们可以控制硬件设计和PCBs,并且必须进行软件开发以增强我们产品的功能集。我们的模型通常由产品中的模数转换器的数据采集组成。视图可能是由嵌入式Web服务器供电的网页,也可以是具有电容触摸控制的LCD屏幕。我们的控制器或多或少是管理这两个代码领域之间关系的胶合逻辑。我们有许多不同的产品和变体来支持,因此代码重用是可取的。



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



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

解决方案

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



首先,我们从这个语句开始:


$ b $由于缺乏正式的OOP语言结构,这可能在C中甚至没有意义?



不能不同意更多与该声明。我稍后会显示只是因为C没有像类这样的漂亮的关键字并不意味着你不能完成同样的事情。



我将尽可能地完成这一步一步一步的跟随您的问题的流程。



C中的OOP



我怀疑,根据您的问题的措辞,你有一个相当体面掌握OOP概念(您甚至在模式方面思考,甚至​​有一个很好的想法,这些模式如何为您的特定场景播放) - 所以让我做一个OOP在C教程30秒或更少。



一旦你得到的东西,你会意识到,还有更多的你可以做的比我要显示在这里 - 但我只是想给你的口味。



101



首先,基本的课(与我一起去):



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(你会看到为什么我在一秒钟内打破这个)

  #includeFoo.h

struct Foo {
年龄
int something;
};

void FooInitialize(Foo * this,int age,int something);

Foo.c:

  #includeFoo_Internal.h

//构造函数:
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;
}

//Propertysetter:
void FooSetAge(Foo * this,int age){
this-> age = age;
}

void FooFree(Foo * this){
//在此处执行任何其他释放。
免费(这);
}

结合事情注意:




  • 我们将不透明指针后面的 Foo 的实现细节隐藏起来。其他人不知道什么是 Foo ,因为该实现细节在内部头文件中,而不是public头。

  • 我们实施实例方法就像一个OOP语言一样 - 除了我们必须手动传递this指针 - 其他语言只为你做这个 - 但这不是一件大事。

  • 我们有属性。再次,其他语言将以更好的语法包装属性getter / settings - 但是它们在幕后真正做的都是为您创建一些getter / setter方法,并将调用转换为属性到方法调用中。



继承



那么如果我们想要一个子类 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:

  #includeFooSubclass.h
#includeFoo_Internal.h

struct FooSubclass {
Foo base;
int something;
};

void FooSubclassInitialize(FooSubclass * this,int age,int something,int somethingElse);

FooSubclass.c

  #includeFooSubclass_Internal.h

//构造函数:
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){
//在此处执行任何其他释放。
免费(这);
}

现在,我应该提到,就像我们制作的初始化器 t实际调用 malloc ,但是负责初始化成员变量 - 我们也真的需要释放器 - 实际上并不释放这个结构 - 而是释放/释放任何拥有引用等等,但是...我实际上会在下面的部分提到一些东西,这可能解释了为什么我没有打扰那个。



你应该注意到,因为我们的 FooSubclass 的第一个成员其实是一个 Foo struct - 任何引用到 FooSubclass 也是对 Foo 的有效引用 - 这意味着它可以在任何地方使用。



但是,有一些小问题,就像我之前提到的那样 - 这种技术实际上并没有让你更改基类的行为。 (例如,我们想要解除我们的实例的分配)。



多态性



我们有一些方法 - 我们会来随机BS示例 - 称为计算



我们想要调用计算 Foo 返回一个值,但如果在$ code> FooSubclass 上调用不同的值。



这在C中很简单 - 它只是一个创建一个实际调用函数指针引用的函数的包装方法的问题。 OOP语言在幕后为您做这件事,通常通过 VTable 实现。



这是一个例子(我将停止提供完整的例子,而不是专注于相关部分):



首先我们定义方法的签名。这里我们说calculateMethod是:一个指向一个方法的指针,它使用一个参数(一个指针)并返回一个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;
// ...
}

现在我们为子类做一些方法要重写这个方法 - 例如,在$ code> Foo_Internal.h 文件中声明的方法称为 void FooSetCalculateMethod(Foo * this,calculateMethod value); - 瞧瞧!可以在子类中覆盖的方法。



模型



我们的模型通常包括从模数转换器在这个产品中。



OK - 所以,模型可能是最简单的实现方式 - 简单的类,用作数据存储机制。



您将不得不为您的特定场景找出一些东西(作为嵌入式系统,我不知道您的具体限制将如何 - 如果您担心关于RAM /持久性/等等) - 但是我认为你不希望我潜入这个。



查看



视图可能是由嵌入式网络服务器,或者是具有电容触摸控制的LCD屏幕。



对于物理的东西,您的视图可能是控制面板上的固定按钮 - 或像你说的那样,它可以是一个LCD或者HTML。



这里的底线是您只需要能够使用简单界面显示系统其余部分的类来显示/更改视图中的内容 - 将IO的细节封装给用户。



通常,IO的I部分在视图中至少需要一些小的代码。



我不认为这是理想的 - 但是,绝大多数情况下,让您的查看代理用户输入回到您的控制器。也许与你的系统有一个很好的方法,因为你有完全控制。



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



控制器



我们的控制器或多或少是管理这两个代码区之间的关系。



这通常是应用程序的胆量。在特定时间,您可能需要一个以上的控制器 - 一个用于传感器数据的进入/处理,一个或多个控制器可用于任何您已经活动的UI,以及可能的其他控制器。



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



让我知道如果你想要更多,或者这有帮助。


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?

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?

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.

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.

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... this might be a long answer... but here goes...

First, let's start with this statement:

Maybe this doesn't even make sense in C because of the lack of formal OOP language constructs?

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 in C

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.

101

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: (you'll see why I broke this out in a second)

#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);
}

Couple things to notice:

  • 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.

Inheritance

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);
}

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.

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).

Polymorphism

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

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

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):

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;
    // ...
}

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;
    // ...
}

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.

Model

Our model would typically consist of data acquisition from Analog to Digital converters in the product.

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

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.

View

The views might be a web page powered by an embedded web server, or else an LCD screen with capacitive touch control.

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

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.

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.

Controller

Our controllers would more or less be the glue logic that manages the relationship between these two areas of code.

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.

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

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