解耦代码和数据
解耦代码和数据的重要性
- 好处:
- 分开维护数据和代码(可维护性)
- 增加新的错误码,不需要修改代码(可维护性)
- 简化代码结构(代码质量)
- 提高执行效率(代码质量)
- 坏处
- 不太直观,降低可读性
结论:好代码是多次重构出来的!
注:工程师-发现问题,解决问题。设计东西。
手段
- 使用数组、枚举、宏定义等手段抽离、维护数据。(也可以索引值用枚举,字符串常量数据采用数组维护,数据也再次进行一次拆分)
- 为避免手误,或者程序、数据分离造成的两处更改不一致问题,使用编译器断言避免出现未有效维护的情形。
注:通常是更改数据,但是代码也需要更改,但是没有两者不对应,为了能保证两者对应采取的手段。
更复杂的情形
- 有如下格式的消息数据包
type:request
target:session/0
operation:create_plain_window
requestId:d56464ed464566d655s56544465dd644f
dataType:ejson
dataLen:<length of data in bytes>
{
workspace: 3456789,
name: "_current",
title: "Hello, world!",
class: "normal",
}
- 希望解析为如下的数据结构
typedef enum{
PCRDR_MSG_TYPE_REQUEST = 0,
PCRDR_MSG_TYPE_RESPONSE,
PCRDR_MSG_TYPE_EVENT,
} pcrdr_msg_type;
typedef emum{
PCRDR_MSG_TARGET_SESSION = 0,
PCRDR_MSG_TARGET_WINDOW,
PCRDR_MSG_TARGET_TAB,
PCRDR_MSG_TARGET_DOM,
}pcrde_msg_target;
typedef enum{
PCRDR_MSG_ELEMENT_TYPE_VOID = 0,
PCRDR_MSG_ELEMENT_TYPE_CSS,
PCRDR_MSG_ELEMENT_TYPE_XPATH,
PCRDR_MSG_ELEMENT_TYPE_HANDLE,
}pcrdr_msg_element_type;
pcrdr_msg_type pcrdr_message_get_type(const pcrdr_msg *msg);
typedef enum{
PCRDR_MSG_DATA_TYPE_VOID = 0,
PCRDR_MSG_DATA_TYPE_EJSON,
PCRDR_MSG_DATA_TYPE_TEXT,
}pcrdr_msg_data_type;
struct _pcrdr_msg{
pcrdr_msg_type type;
pcrde_msg_target target;
pcrdr_msg_element_type elementType;
pcrdr_msg_data_type dataType;
unsigned int retCode;
uintptr_t targetValue;
char * operation;
char * element;
char * property;
char * event;
char * requestId;
size_t dataLen;
char * data;
};
- 通过键值,返回特定数据类型
- 通过键值,索引函数指针
/* 构造方法和自己通常用的构造列表一样,前面索引值,后面函数指针 */
/**
* 优化
* 1. 使用二分查找法优化find_key_op 函数
* 2. 此方法也适用于稀疏整数集合
* 3. 通过sorted-array实现
* /
static key_op find_key_op(const char* key)
{
/* 巧用static, 编译阶段计算出,赋值给static */
static size_t max = sizeof(key_ops)/sizeof(key_ops[0]) -1;
size_t low = 0,high = max, mid;
while(low <= high)
{
int cmp;
mid = (low +high) / 2;
cmp = strcasecmp(key, key_ops[mid].key)
if (cmp == 0)
{
goto found;
}
else{
if (cmp < 0)
{
high = mid - 1;
}
else
{
low = mid + 1;
}
}
}
return NULL;
found:
return key_ops[mid].op;
}
终极大法
用脚本生成代码:
- 让程序处理大量的重复性编码工作,避免手工编写引入错误
- 处理字符串时,字符串较多时,二分查找性能不高,可以用哈希表;
- 用脚本,可以找到一个相对均衡的哈希算法
- 依赖构建系统自动维护代码的重新生成
一个例子
- 如何快速获取CSS属性(如width、height、color)的内部属性信息?
- 我们准备一个描述文件
- 然后用python脚本处理这个文件并自动生成代码
/* 描述文件内容 */
background-attachment
values: scroll fixed inherit
initial: scroll
inherited: no
background_color
values: <color> inherit
initial: transparent
inherited: no
......
"""
Make CSS property value macros:
1. Read 'propertylist.txt'file
2. Generate CSS property identifier list(enum)
3. Generate CSS property value keyword list(enum)
4. Generate CSS property value macros
5. Write code to 'csspropertyvalue.inc','cssdeclared.inc',and'cssinitial.cc'files
"""
/**
* 生成的源文件
* 1. 样式的属性值定义
* 2. 样式属性操作函数
* 3. 样式的初始化
*/
/* 样式的属性值定义 */
typedef enum _CssPropertyIds {
// background-attachment
PID_BACKGROUND_ATTACHMENT,
// background-color
PID_BACKGROUND_COLOR
......
}
typedef enum _CssPropertyValueTypes {
PVT_NONE = 0,
PVT_KEYWORD,
......
}
/* 生成操作函数 */
bool setBackgroundIamge(Uint32 value ,HTInt data){
return Css::setProperty (PID_BACKGROUND_IMAGE, value, data);
}
......
/* 初始化 */
#include "css/cssinitial.h"
namespace hfcl {
CssInitial* CssInitial::s_singleton = NULL;
CssInitial::CssInitial()
{
memset(&m_data, 0 ,sizeof(m_data));
memset(&m_arraysize, 0, sizeof(m_arraysize));
//Initial value
m_values[PID_BACKGROUND_ATTACHMENT] = MAKE_CSS_PPT_INITIAL_VALUE(CSS_PPT_VALUE_MARK_)
}
....
}