programing

순수 C로 구현된 MVC

linuxpc 2023. 6. 25. 18:31
반응형

순수 C로 구현된 MVC

C 컨텍스트에서 모델 뷰 컨트롤러 설계 패턴을 수행하려는 간단한 예를 제공하는 리소스를 아는 사람이 있습니까?특히 임베디드 시스템은?

명확하게 말하자면, 저는 C#, C++, Objective-C, Java, PHP 또는 더 높은 수준의 언어 예제에는 관심이 없습니다.저는 순수한 ansi C99나 심지어 C89로 이 디자인 패턴에 접근하는 방법에 대해 사람들이 어떻게 생각하는지 알고 싶습니다.공식 OOP 언어 구조가 부족하기 때문에 C에서 이것이 말이 안 되는 것은 아닐까요?

어떤 맥락에서: 제 동료들과 저는 Arm 기반 PSoC 칩으로 구동되는 임베디드 시스템을 연구하고 있습니다.우리는 하드웨어 설계와 PCB를 관리하고 있으며, 제품의 기능을 향상시키기 위해 소프트웨어 개발을 해야 합니다.당사의 모델은 일반적으로 제품의 아날로그에서 디지털 변환기로의 데이터 수집으로 구성됩니다.보기는 내장된 웹 서버로 구동되는 웹 페이지이거나 정전식 터치 컨트롤이 있는 LCD 화면일 수 있습니다.우리의 컨트롤러는 이 두 코드 영역 사이의 관계를 관리하는 접착 논리일 것입니다.우리는 지원할 수 있는 다양한 제품과 변형이 있으므로 코드 재사용이 바람직합니다.

매우 세부적이거나 엔터프라이즈 수준의 프레임워크를 찾고 있지 않습니다.그러나 프로그래밍 문제를 분리하기 위한 좋은 전략을 설명하는 간단한 예는 하위 수준 C에서 발견되는 관용구에 편향되어 있습니다. 예를 들어, 구조, 함수, 이벤트 기반 논리 및 C에서 의미가 있는 일종의 추상적인 메시지 전달입니다.

하드웨어 특성상 우리는 C를 사용해야 하고 우리 스스로 많은 것들을 부트스트랩해야 합니다.OS에 액세스할 수 있는 경우도 있고, 프로세서로 바로 컴파일하여 주요 기능으로 시작하는 경우도 있습니다.모두 매우 원시적이지만 코드 재사용을 허용하고 소프트웨어 엔지니어링 프로세스를 가속화할 수 있는 접근 방식을 찾고 있습니다.

먼저, 다음 문장으로 시작하겠습니다.

공식 OOP 언어 구조가 부족하기 때문에 C에서 이것이 말이 안 되는 것은 아닐까요?

저는 그 진술에 전적으로 반대합니다.나중에 보여드리겠지만, C가 "클래스"와 같은 멋진 키워드를 가지고 있지 않다고 해서 당신이 같은 일을 할 수 없다는 것을 의미하지는 않습니다.

질문의 흐름에 따라 가능한 한 단계적으로 진행하도록 노력하겠습니다.

OOP in C

질문의 표현을 바탕으로 OOP 개념을 꽤 잘 이해하고 있다고 생각합니다(패턴 측면에서 생각하고 있으며 특정 시나리오에서 해당 패턴이 어떻게 작동할지에 대해서도 잘 알고 있습니다). 그래서 "30초 이하"에서 "OOP in C" 튜토리얼을 수행하겠습니다.

일단 여러분이 이것들을 이해하게 되면 여러분은 제가 여기서 보여드릴 것보다 여러분이 할 수 있는 것이 훨씬 더 많다는 것을 알게 될 것입니다. 하지만 저는 여러분에게 맛을 보여드리고 싶습니다.

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구현 세부사항이 "공개" 헤더가 아닌 "내부" 헤더 파일에 있기 때문입니다.
  • 우리는 OOP 언어가 수동으로 "이" 포인터를 전달해야 하는 것을 제외하고는 "인스턴스 메소드"를 구현합니다. 다른 언어는 이를 수행합니다. 하지만 큰 문제는 아닙니다.
  • "속성"이 있습니다.다시 말하지만, 다른 언어들은 속성 게터/설정을 더 좋은 구문으로 마무리할 것입니다. 하지만 그들이 실제로 하는 모든 것은 당신을 위해 어떤 게터/세터 메서드를 만들고 "속성"에 대한 호출을 메서드 호출로 변환하는 것입니다.

상속

그래서 만약 우리가 "하위 클래스"를 원한다면요?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);

Foo 하위 클래스_내부의.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그러나 구성원 변수를 초기화할 책임이 있습니다. 또한 실제로 구조를 자유롭게 하는 것이 아니라, 대신 "자유로운" 참조 등을 자유롭게/해제하는 할당 해제자가 정말 필요합니다.하만지... 사실 아래 가 왜 할 수 있는 아래 섹션에서 제가 왜 아직 그것에 신경 쓰지 않았는지 설명할 수 있는 무언가를 언급하려고 합니다.

우리의 - ▁our▁since▁now▁-▁you로후이▁that.FooSubclass의 첫 번째 멤버는 사실,Foo- 조구에 - a 모참든조대한에 .FooSubclass는 또한에대참조다니입유효한한▁a에 대한 .Foo어디서나 사용할 수 있다는 뜻입니다.

그러나 몇 가지 작은 문제가 있습니다. 이전에 단락에서 언급했듯이 이 기술은 실제로 기본 클래스의 동작을 변경할 수 없습니다.(예를 들어 인스턴스 할당 해제를 위해 수행할 작업)

다형성

를 들어,로 BS의 예를 해 낼 수 있는 - BS의 예는 - BS의 예입니다.calculate.

는 리는전기로 를 원합니다.calculate에서.Foo값을 합니다. 하의값반다니합환에 을 반환합니다. 그러나 이 값이 호출된 경우 다른 값을 반환합니다.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;
    // ...
}

이제 하위 클래스가 이 메서드를 재정의할 수 있는 몇 가지 방법을 만듭니다. 예를 들어, 다음에서 선언된 메서드를 예로 들 수 있습니다.Foo_Internal.hvoid FooSetCalculateMethod(Foo * this, calculateMethod value);그고리 voila!하위 클래스에서 재정의할 수 있는 메서드입니다.

모델

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

좋아요. 모델은 구현하기 가장 쉬운 것일 수도 있습니다. 즉, 데이터 스토리지 메커니즘으로 사용되는 간단한 "클래스".

특정 시나리오(내장형 시스템이기 때문에 정확한 제한 사항이 무엇인지 잘 모르겠습니다. RAM/지속성 등이 걱정된다면)에 대해 무언가를 알아내야 합니다. 하지만 어쨌든 제가 이 문제에 빠져드는 것을 원치 않으실 거라고 생각합니다.

보다

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 세부 정보를 캡슐화할 수 있는 클래스가 필요하다는 것입니다.

일반적으로 "IO"의 "I" 부분에는 보기에 최소한 작은 코드 조각이 필요합니다.

저는 이것이 이상적이라고 생각하지 않습니다. 하지만 대부분의 경우 "보기" 프록시 사용자가 컨트롤러에 다시 입력하도록 하는 좋은 방법은 없습니다.시스템을 사용하면 이 문제를 해결할 수 있는 좋은 방법이 있을 수 있습니다. 즉, 전체적인 제어가 가능하다는 점입니다.

이제 필요에 맞는 보기 클래스를 쉽게 만들 수 있는 방법을 알아보시기 바랍니다.

컨트롤러

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

이것은 보통 응용 프로그램의 핵심입니다.센서 데이터의 입력/처리를 위한 컨트롤러, 활성 UI를 위한 컨트롤러 및 기타 컨트롤러가 한 번에 두 개 이상 필요할 수 있습니다.

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

하지만 당신은 아마 이것보다 조금 더 화려해질 것입니다.하나의 컨트롤러에 여러 보기가 있는 경우 보기를 관리하기 위해 관찰자 패턴과 같은 것을 사용할 수 있습니다.하지만 이것을 사용하면 연결 가능한 뷰를 얻을 수 있습니다.저는 아마 현실적으로 좀 더 엄격할 것이고, 함수를 통해서만 모델을 변경하고, 함수를 통해서만 '디스플레이' 함수 포인터를 호출할 수 있도록 할 것입니다(직접 호출합니다).이를 통해 다양한 후크를 사용할 수 있습니다(먼저 모델 또는 뷰/함수 포인터가 null인지 확인하십시오).메모리 관리는 추가하는 것이 어렵지 않고, 일이 엉망으로 보이게 하기 때문에 생략했습니다.

저는 사람들이 어떤 제안을 할지 관심이 있지만, 저는 당신이 핵심을 맞췄다고 생각합니다 - 아마도 공식 OOP 구조가 부족하기 때문에 말이 되지 않을 것입니다.

하지만 OOP 개념을 ANSI-C에 소개할 도 있습니다. 한동안 이 PDF에 대한 링크를 가지고 있었지만 (일상 업무에서 C에 노출되지 않았기 때문에) 실제로 이 PDF를 흡수한 적은 없지만 확실히 성과가 있을 것 같습니다.

http://www.planetpdf.com/codecuts/pdfs/ooc.pdf

큰 작업이지만 궁극적으로 추가 MVC 스타일 개발을 매우 쉽게 작성할 수 있는 일종의 템플릿/프레임워크를 생각해 낼 수 있습니다. 하지만 저는 그 절충점이 시간 여유가 있다고 생각합니다.MVC가 제공하는 명확성의 이점이 성능/메모리 보호/쓰레기 수집의 부족과 물론 휠을 다시 발명해야 하는 순수한 노력보다 더 중요하다는 것과 같은 임베디드 플랫폼의 한계가 있습니까?

행운을 빌어요. 그리고 당신이 생각해 내는 것을 보고 매우 흥미로워할 거예요!

편집:

나중에 생각해 볼 때, 전체 MVC 구현을 수행하지 않고 OOP 기술 중 일부만 사용하는 것이 문제를 해결하는 데 도움이 될 수 있습니다. 인터페이스로 적절한 다형성 계층을 구현할 수 있었다면 코드 재사용이라는 목표를 달성하는 데 큰 도움이 되었을 것입니다.

이 다른 스택 오버플로는 Ansi C에서 OOP를 구현하는 것을 다루고 있습니다. 흥미로운 읽기이지만 동일한 PDF로 링크됩니다. C에서 객체 지향 코드를 작성할 수 있습니까?

언급URL : https://stackoverflow.com/questions/9355021/mvc-implemented-in-pure-c

반응형