본문 바로가기

프로그래밍 언어/Architecture

객체 지향 & 패턴 in C (1)

[Contents]

C언어와 객체지향1

C언어와 객체지향2 - 기능 추가

C언어와 객체지향3 - 확장성을 고려한 기능 추가

C언어와 객체지향4 - 상속된 기능들(기능의 상속화)

 

[출처] '모던 C언어 프로그래밍' - 하나이 시세이


C언어와 객체지향1

- 자료구조는 구조체로 표현하여 '객체'처럼 생성하면 편리하다.

- 매크로를 사용하여 쉽게 구조체 초기화를 한다.

 

Stack.h

#include <stddef.h>

typedef struct{
    int top;
    const size_t size;
    int *const pBuf;
} Stack;

bool push(Stack *p, int val);
bool pop(Stack *p, int *pRet);

#define newStack(buf) {0, sizeof(buf) / sizeof(int), (buf)}

main.c

#include <stdbool.h>
#include "stack.h"

int main()
{
    int buf[16];
    Stack stack = newStack(buf);

    int buf2[16];
    Stack stack2 = newStack(buf2);
}

 

C언어와 객체지향2 - 기능 추가

- 위의 '스택'에 기능을 추가하여 보자. 구조체를 변경하기 보단 기능은 따로 분리한다.

- 예를 들어 '스택 범위 제한 기능'을 추가하여 본다.

- 이점

    - 기능을 수행하는 구조체 포인터를 추가하여 기존 자료형의 변화를 최소화 한다.

Stack.h

#include <stddef.h>

typedef struct{
    const int min;
    const int max;
} Range;

typedef struct{
    int top;
    const size_t size;
    int *const pBuf;
    const Range *const pRange; //'범위 검사 기능'은 구조체 포인터로 구현  /* new */
} Stack;                       //min, max를 Stack 구조체 안에 두지 않는다.

bool push(Stack *p, int val);
bool pop(Stack *p, int *pRet);
static bool isRangeOK(const Range *p, int val);                       /* new */

#define newStack(buf) {0, sizeof(buf) / sizeof(int), (buf)} 
#define newStackWithRangeCheck(buf, pRange) {   \                     /* new */
    0, sizeof(buf) / sizeof(int), (buf), pRange \
}

Stack.c

static bool isRangeOK(const Range *p, int val)              
    return p == NULL || (p->min <= val && val <= p->max);

bool push(Stack *p, int val) {
    if( !isRangeOk(p->pRange, val) || isStackFull(p) )
    	return false;
    p->pBuf[p->top++] = val;
    return true;
}

 

C언어와 객체지향3 - 확장성을 고려한 기능 추가

- 위의 방법은 기능이 바뀔 때 마다 Stack 구조체가 변경된다.

- 따라서, Stack 구조체 입장에서는 기능을 신경 안쓰고 싶다. Validator(기능과 데이터 포함하는) 구조체를 둔다

- 실제 기능을 수행하는 함수에서 인자를 형변환 하여 필요에 의해 쓴다.

Stack.h

/* Abstract Struct */
typedef struct Validator {
    bool (*const validate)(struct Validator *pThis, int val); //function pointer
    void *const pData;                                        //검사에 사용할 데이터, 유동적
} Validator;

typedef struct{
    const int min;
    const int max;
} Range;

typedef struct {
    int previousValue;
} PreviousValue;

typedef struct{
    int top;
    const size_t size;
    int *const pBuf;
    Validator *const pValidator;
} Stack;

/* Stack.c */
bool validateRange(Validator *pThis, int val) {
    Range *pRange = (Range *)(pThis->pData);  // 기능 수행 함수에서 데이터 형변환하여 쓴다.
    return pRange->min <= val && val <= pRange->max;
}

bool validatePrevious(Validator *pThis, int val){
    PreviousValue *pPrevious = (PreviousValue *)pThis->pData; // 기능 수행 함수에서 데이터 형변환하여 쓴다.
    if(val < pPrevious->previousValue)
        return false;
    pPrevious->previousValue = val;
    return true;
}

#define rangeValidator(pRange) { validateRange, pRange }    // 함수와 범위
#define previousValidator(pPrevious) { validatePrevious, pPrevious } // 함수와 이전 값
#define newStackWithValidator(buf, pValidator) {0, sizeof(buf) / sizeof(int), (buf), pValidator}
/*
    이것 자체가 Validator 이며, 함수 포인터에 함수 주소를 넘겨주고, 데이터를 넘겨준다.
*/

main.c

Range range = {0, 9};
Validator validator = rangeValidator(&range);
Stack stack = newStackWithValidator(buf, &validator);
push(&stack, 123);

PreviousValue previoud = {0};
Validator validator = previousValidator(&previous);
Stack stack2 = newStackWithValidator(buf, &validator);
push(&stack2, 123);

 

C언어와 객체지향4 - 상속된 기능들(기능의 상속화)

- Validator가 기능을 수행하므로, 추가된 구조체인 Range, PreviousValue 구조체도 기능으로 만들어 버린다.

- 기존의 Validator는 부모 구조체로 만든다.

- Validator를 형 변환하여 쓴다

- Stack을 생성할 때 &validator.base를 넣음에 주의한다. 구조체의 첫번째 멤버가 일치함으로 가능한 것.

Stack.h

/* Abstract Struct */
typedef struct Validator {
    bool (*const validate)(struct Validator *pThis, int val); //function pointer
} Validator;

bool validateRange(Validator *p, int val);
bool validatePrevious(Validator *p, int val);

typedef struct{
    Validator base;              //(1)함수주소를 가리킨다.
    const int min;
    const int max;
} RangeValidator;

typedef struct {
    Validator base;
    int previousValue;
} PreviousValueValidator;

typedef struct{
    int top;
    const size_t size;
    int *const pBuf;
    Validator *const pValidator;  //(2)함수 주소를 전달 (min, max 도 딸려온다?)
} Stack;

bool validateRange(Validator *p, int val)
{
    RangeValidator *pThis = (RangeValidator *)p;                // Validator 형변환
    return pThis->min <= val && val <= pThis->max;
}

bool validatePrevious(Validator *p, int val)
{
    PreviousValueValidator *pThis = (PreviousValueValidator*)p; // Validator 형변환
    if (val < pThis->previousValue) return false;
    pThis->previousValue = val;
    return true;
}

#define newRangeValidator(min, max) { validateRange, min, max }    // 함수와 범위
#define newPreviousValueValidator { validatePrevious, 0 }          // 함수와 이전 값
#define newStackWithValidator(buf, pValidator) {0, sizeof(buf) / sizeof(int), (buf), pValidator}

main.c

RangeValidator validator = newRangeValidator(0, 9); //{ validateRange, min, max } 
Stack stack = newStackWithValidator(buf, &validator.base);

반응형

'프로그래밍 언어 > Architecture' 카테고리의 다른 글

객체 지향 & 패턴 in C (2)  (0) 2021.09.27
SOLID (객체 지향 설계) 설명  (0) 2021.04.17