Ответ 1
Pshew... это может быть длинный ответ... но здесь идет...
Сначала начнем с этого утверждения:
Maybe this doesn't even make sense in C because of the lack of formal OOP language constructs?
Не удалось не согласиться с этим утверждением. Как я покажу позже; просто потому, что у C нет отличных ключевых слов, таких как "класс", это не значит, что вы не можете выполнить одни и те же вещи.
Я постараюсь сделать это шаг за шагом, насколько это возможно, после вашего вопроса.
ООП в C
Я подозреваю, основываясь на формулировке вашего вопроса, что у вас есть довольно приличное понимание концепций ООП (вы даже думаете с точки зрения шаблонов и даже имеете хорошее представление о том, как эти шаблоны будут разыгрываться для вашего конкретного сценарий) - так позвольте мне сделать "ООП в 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: (вы поймете, почему я сломал это через секунду)
#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
, потому что эта деталь реализации находится во внутреннем заголовочном файле, а не в "общедоступном" заголовке. - Мы реализуем "методы экземпляра", как и язык ООП, за исключением того, что нам нужно вручную передать указатель "this" - другие языки просто сделают это для вас - но это не имеет большого значения.
- У нас есть "свойства". Опять же, другие языки завершают свойства getters/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:
#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
, но отвечают за инициализацию переменных-членов - нам также действительно нужны дезактиваторы, которые фактически не освобождают структуру - но вместо этого освобождайте/освобождаете любые "владеющие" ссылки и т.д. Однако... Я на самом деле собираюсь упомянуть что-то в разделе ниже, что может объяснить, почему я еще не потрудился с этим.
Теперь вы должны заметить, что поскольку наш FooSubclass
первый член является, по сути, структурой Foo
, что любая ссылка на a FooSubclass
также является действительной ссылкой на Foo
- это означает, что это может быть используется как таковое почти везде.
Однако есть несколько небольших проблем с этим - как я уже упоминал в предыдущем параграфе, этот метод фактически не позволяет вам изменять поведение базового класса. (Например, что-то, что мы хотели бы сделать для освобождения нашего экземпляра).
Полиморфизм
Скажем, у нас есть какой-то метод - мы придумаем случайный пример BS - под названием calculate
.
Мы хотим вызвать calculate
на Foo
, чтобы вернуть одно значение, но другое значение, если оно было вызвано на FooSubclass
.
Это просто в C - это действительно вопрос создания метода-оболочки, который фактически вызывает функцию, на которую ссылается указатель на функцию. Языки ООП делают это за кулисами и обычно реализуются с помощью 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;
// ...
}
Теперь мы сделаем некоторый способ для подклассов переопределить этот метод - скажем, например, метод, объявленный в файле Foo_Internal.h
с именем void FooSetCalculateMethod(Foo * this, calculateMethod value);
- и voila! Методы, которые могут быть переопределены в подклассах.
Model
Our model would typically consist of data acquisition from Analog to Digital converters in the product.
ОК - так, модель, вероятно, самая простая вещь для реализации - простые "классы", которые используются в качестве механизмов хранения данных.
Вам нужно будет что-то выяснить для своего конкретного сценария (будучи встроенной системой, я не уверен, что ваши точные ограничения будут - и если вы беспокоитесь о 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.
Для физических вещей ваш "вид" может быть фиксированными кнопками на панели управления - или, как вы сказали, это может быть ЖК-дисплей или HTML.
В нижней строке здесь вам просто нужны классы, которые способны представить остальную часть вашей системы "простым" интерфейсом для отображения/изменения вещей в представлении - и инкапсулировать детали ввода-вывода пользователю.
Обычно "I" часть "IO" требует по крайней мере небольшого клина кода в представлении.
Я не думаю, что это идеальный вариант, но, в большинстве случаев, нет хорошего способа вернуть ваш пользовательский прокси-сервер к вашим контроллерам. Возможно, с вашей системой есть хороший способ обойти это - если у вас есть полный контроль.
Надеюсь, теперь вы можете увидеть, как легко можно было создать некоторые классы классов, соответствующие вашим потребностям.
контроллер
Our controllers would more or less be the glue logic that manages the relationship between these two areas of code.
Это, как правило, кишки приложения. Вероятно, вам понадобится более одного контроллера в определенный момент времени - один для входа/обработки данных датчика, один или несколько для любого пользовательского интерфейса, который вы активировали, и, возможно, другие.
В любом случае, я надеюсь, что это поможет... Мне кажется, что я пишу книгу сейчас, поэтому я остановлюсь.
Сообщите мне, хотите ли вы большего, или если это вообще поможет.