基本思想
面向对象的基本思考方式是的将数据和处理数据的行为放在一起,降低耦合性,其要点就是不要将数据和处理数据的行为分开。
一.多态
所谓多态性就是指从调用者的角度看对象,会发现它们非常相似,难以区分,但是这些被调用对象的内部处理实际上各不相同。
结构体声明和调用:
typedef struct _Validator {
bool (* const validate)(struct _Validator *pThis, int val);
void * const pData;
} Validator;
bool validate(Validator *p, int val) {
if (! p) return true;
return p->validate(p, val);
}
多态,就是说用同一的接口代码处理不同的数据。比如说,这里的Validator结构就是一个通用的数据结构,我们也不清楚pData是什么数据,validate是什么处理函数?但是,我们处理的时候只要调用p->validate(p, val)就可以了。剩下来的事情我们不需要管,因为不同的接口会有不同的函数去处理,我们只要学会调用就可以了。
二.继承
面向对象程序设计中最重要的一个概念是继承。继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易。
前面的例子中其实已经对继承有所体现,用pData指针指向Range或PreviousValue结构体;
只不过这种父类子类的继承比较模糊;
typedef struct _Validator {
bool (* const validate)(struct _Validator *pThis, int val);
void * const pData;
} Validator;
typedef struct {
const int min;
const int max;
} Range;
typedef struct {
int previousValue;
} PreviousValue;
为了更直观的展示继承关系,使用如下解决方法:
在设计C语言继承性的时候,我们需要做的就是把基础数据放在继承的结构的首位置即可。这样,不管是数据的访问、数据的强转、数据的访问都不会有什么问题。
typedef struct _Validator {
bool (* const validate)(struct _Validator *pThis, int val);
} Validator;
typedef struct {
Validator base;
const int min;
const int max;
} RangeValidator;
typedef struct {
Validator base;
int previousValue;
} PreviousValueValidator;
三.封装
封装是指通过对象的状态和行为集中在一起,并规定其与外部的接口来进行抽象化的过程。
单纯从文字上很难理解,那么把状态认为是结构体中函数指针以外的成员,把行为认为是函数指针成员,就很容易了。
封装的主要含义是隐藏内部的行为和信息,使用者只用看到对外提供的接口和公开的信息。
例如:
typedef struct _Validator {
bool (* const validate)(struct _Validator *pThis, int val);
} Validator;
typedef struct {
Validator base;
const int min;
const int max;
} RangeValidator;
struct _Data;
typedef void (*process)(struct _Data* pData);
typedef struct _Data
{
int value;
process pProcess;
}Data;
封装性的意义在于,函数和数据是绑在一起的,数据和结构体是绑在一起的。这样,我们就可以通过简单的一个结构指针访问到所有的数据,遍历所有的函数。封装性,这是类拥有的属性,当然也是数据结构体拥有的属性。
数据隐藏:
1.在C语言中没有提供访问控制功能,无法实现数据隐藏,但这也并非大问题。实际上在前面的例子中,我们使用了const修饰符,这样就不必担心从外部调用不小心修改内部数据了。
2.可以规定成员命名规则来规避这个问题,例如不允许直接访问的成员命名时以下划线“_”开头。
完整代码示例:
stack.h
#ifndef _STACK_H_
#define _STACK_H_
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct _Validator {
bool (* const validate)(struct _Validator *pThis, int val);
} Validator;
typedef struct {
Validator base;
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;
} Stack;
bool validateRange(Validator *pThis, int val);
bool validatePrevious(Validator *pThis, int val);
#define newRangeValidator(min, max) \
{{validateRange}, (min), (max)}
#define newPreviousValueValidator \
{{validatePrevious}, 0}
bool push(Stack *p, int val);
bool pop(Stack *p, int *pRet);
#define newStack(buf) { \
0, sizeof(buf) / sizeof(int), (buf), \
NULL \
}
#define newStackWithValidator(buf, pValidator) { \
0, sizeof(buf) / sizeof(int), (buf), \
pValidator \
}
#ifdef __cplusplus
}
#endif
#endif
stack.c
#include <stdbool.h>
#include "stack.h"
static bool isStackFull(const Stack *p) {
return p->top == p->size;
}
static bool isStackEmpty(const Stack *p) {
return p->top == 0;
}
bool validateRange(Validator *p, int val) {
RangeValidator *pThis = (RangeValidator *)p;
return pThis->min <= val && val <= pThis->max;
}
bool validatePrevious(Validator *p, int val) {
PreviousValueValidator *pThis = (PreviousValueValidator *)p;
if (val < pThis->previousValue) return false;
pThis->previousValue = val;
return true;
}
bool validate(Validator *p, int val) {
if (! p) return true;
return p->validate(p, val);
}
// true: 成功, false: 失敗
bool push(Stack *p, int val) {
if (! validate(p->pValidator, val) || isStackFull(p)) return false;
p->pBuf[p->top++] = val;
return true;
}
// true: 成功, false: 失敗
bool pop(Stack *p, int *pRet) {
if (isStackEmpty(p)) return false;
*pRet = p->pBuf[--p->top];
return true;
}
测试代码:
#include "gtest/gtest.h"
#include <stdbool.h>
#include "stack.h"
TEST(StackTest, popFromEmptyStackReturnsFalse) {
int buf[16];
Stack stack = newStack(buf);
EXPECT_EQ(false, pop(&stack, 0));
}
TEST(StackTest, popReturnsStackTopAndRemoveIt) {
int buf[16];
Stack stack = newStack(buf);
EXPECT_EQ(true, push(&stack, 123));
int ret;
EXPECT_EQ(true, pop(&stack, &ret));
EXPECT_EQ(123, ret);
EXPECT_EQ(false, pop(&stack, &ret));
}
TEST(StackTest, pushToFullStackReturnsFalse) {
int buf[16];
Stack stack = newStack(buf);
for (int i = 0; i < 16; ++i) push(&stack, i);
EXPECT_EQ(false, push(&stack, 100));
int ret;
EXPECT_EQ(true, pop(&stack, &ret));
EXPECT_EQ(15, ret);
}
TEST(StackTest, pushWithRangeCheck) {
int buf[16];
RangeValidator validator = newRangeValidator(0, 9);
Stack stack = newStackWithValidator(buf, &validator.base);
EXPECT_EQ(false, push(&stack, -1));
EXPECT_EQ(false, push(&stack, 10));
}
TEST(StackTest, pushWithPreviousCheck) {
int buf[16];
PreviousValueValidator validator = newPreviousValueValidator;
Stack stack = newStackWithValidator(buf, &validator.base);
EXPECT_EQ(true, push(&stack, 3));
EXPECT_EQ(false, push(&stack, 2));
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}