随手涂鸦的:
// HellowTest2008.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "CLock.h"
/*
V1表示旧的数据,V2表示扩充的数据
旧数据nVersion表示为0, 扩充数据nVersion表示为1
*/
struct Role1
{
int V1n1;
int V2n1;
};
struct Role2
{
int V2n1;
int V1n1;
};
class CRole
{
public:
private:
public:
int V1n1;
Role1 V1Role1;
Role2 V1Role2;
int V2n1;
int V2n2;
int V2n3;
private:
};
struct ChunkHead
{
int nLen;
};
struct Chunk
{
int nLen;
int ChunkType;
char Buffer;
int CRC;
};
/*
CSave表示一个已经从介质里读取出来的数据
*/
class CSave
{
public:
CSave();
void V1Read(CRole *pRole);
void V1Write(CRole *pRole);
void V2Read(CRole *pRole);
private:
public:
/*
V1
*/
int nVersion;
char Buffer[1024];
/*
V2
*/
char V2Buffer[1024]; //这段的组成规则是ChunkHead|Chunk|Chunk|Chunk
private:
};
CSave::CSave()
{
nVersion = 0;
}
void CSave::V1Read(CRole *pRole)
{
/*
BufferXXX表示产出一个最终结果值,这里我们不关心
*/
if(0==nVersion)
{
pRole->V1n1 = BufferXXX;
/*
每个结构是否有自己的序列化函数,这都不重要.关心的是一个写值的动作
*/
pRole->V1Role1.V1n1 = BufferXXX;
pRole->V1Role1.V1n1 = BufferXXX;
}
/*
扩充后,根据版本号来读取。
这里的扩充一般会有两种情况,
1.先扩充存储介质(比如数据库)
2.先扩充代码,根据写入时来自动扩充数据库
3.两边一起扩充
如果是第一种,或许不要版本号,只要代码扩充好,然后把介质中新加的字段一个初始值,
最后再精心修改下0==nVersion中的代码,算好介质中各种新加入的偏移值。这明显是最差的,把同介质之间的接口作用放大。
如果是第二种(大多数下是),首先在读的时候仍然按照旧版本读(这个时候介质还没扩充),写入时按照
新版本的写,然后修改版本号。
如果是第三种,其实跟第一种类似,把同介质之间的接口作用放大。还要需要修改数据库代码。而数据库,更倾向看成是一个
read,write的东西。只要关心数据和长度这两个参数即可.如果你想要查看,我觉得单独做个读取工具更好。而且这段读取的
代码和你真正工程里读取数据的代码区别不大。
下面代码给出第二种方法
*/
if(1==nVersion)
{
//先调用0==nVersion的函数读一遍
pRole->V1Role1.V2n1 = BufferXXX;
pRole->V2n1 = BufferXXX;
//......
}
if( 2==nVersion )
{
//掉调用1==nVersion的。。
//......
}
/*
这种用版本号的方法可以解决兼容的问题。但是不好的地方是让第二个版本的数据知道了第一个版本的数据,在
数据的意义上它们是等同的,但是处理方法却变成了一种兼容的方式。同时,BufferXXX获取的接口中,也要从一个
大数据里面取出对应的第二个版本数据。这些都明显增加了二次开发和扩充的代价。我以前一次工作中,数据库的设计
是针对每个角色都有特别大的空间,防止以后扩充不够。结果每次增加新东西时,总要奔赴在修改各种旧的代码之间,
即使你不用动它,它们也总是在你调试的时候跳来跳去。更可恶的是数据库的字段名,一开始只是简单的叫d1,d2,d3...dn,
在核对各个字段上又浪费了不少时间。结果在扩充一些大功能的时候,另外建立了单独的空间,终于让它们享受一样的
待遇了。
于是就想,为什么不把扩充的数据也当成一个全新的数据,只是让他们最终流向的Role结构不一样而已。完全可以
让它们在新旧版本上的数据流程都一样,就像新写了一遍。
*/
}
void CSave::V2Read(CRole *pRole)
{
/*
每个新加的数据只要按Chunk块排好,读、写总的函数完全不用变,每个人关心自己的
HandleChunkType()即可。
*/
int nHasLen = 0;
while(ChunkHead.nLen>nHasLen)
{
switch(Chunk.ChunkType)
{
HandleChunkType();
}
nHasLen += Chunk.nLen;
}
}
void CSave::V1Write(CRole *pRole)
{
/*
写这里相对简单, 因为得到的都是最后一组、最新的数据。不做更多的阐述
*/
}
int _tmain(int argc, _TCHAR* argv[])
{
return 0;
}
但是,结合PNG的这种格式,仍然可以在游戏中做点事情。比如可以在扩充字段时,旧的方案需要修改2-3个点,完全可以改为只要修改一个点就够了。
修改的2-3个点包括,对数据库字段,SQL语句,变量的读取。如果有一些中间件变化得可能更多。
用一些小技巧,可以把这些修改点都集中到一个函数上。所以PNG还是有很大启发的。