对数据操作封装的一点心得

假设缓存中数据的格式如下所示:

id | name level exp time

下面,我们考虑对数据操作进行封装,先定义一个类

class CCacheData;

在对数据进行操作时,可能需要读写name,于是我们写了一个接口,这个接口会实时更新缓存

class CCacheData

{

    SetName(string &name);

};

由于业务需要,我们需要一个接口更新exp,于是又多了一个接口 

class CCacheData

{

    SetName(string &name);

    SetExp(int exp);

};

由于业务需要,我们需要一个接口更新exp的同时,更新time,于是又多了一个接口

class CCacheData

{

    SetName(string &name);

    SetExp(int exp);

    SetExp(int exp, int time);

}; 

由于业务需要,我们需要一个接口更新level,于是又多了一个接口

class CCacheData

{

    SetName(string &name);

    SetExp(int exp);

    SetExp(int exp, int time);

    SetLevel(int level);

}; 

由于业务需要,我们需要一个接口更新level的同时,更新time,于是又多了一个接口

class CCacheData

{

    SetName(string &name);

    SetExp(int exp);

    SetExp(int exp, int time);

    SetLevel(int level);

    SetLevel(int level, int tim);

}; 

由于业务需要,我们需要一个接口 ...

........

上面的方式,有几个弊端:

1.接口会随着业务不断增加

2.每次更新都会更新数据源,开销比较大,如果业务需要多次更新时,不能保证原子操作

于是,我们想了一个办法,用一个结构体把数据封装起来,然后定义读写接口,如下所示:

class CCacheData

{

    struct stData{

         int id;

         string name;

         int exp;

         int level;

         int time;

    };

    Get(stData &data);

    Set(stData &data);

}; 

上面的方式业务简单而且清晰,也解决了第一种封装方式的问题,但存在安全性的问题。

比如,一个调用者,可能在业务逻辑中多次调用,在外部多次修改结构体,很有可能会不小心修改了不应该改的字段,如不小心赋了下初值等,这样set的时候会直接回写,数据就会出错。

于是我们想到一种更好的方式,暴露给调用者的只有接口,而数据结构本身做为一个成员变量保存在类中,如下所示:

class CCacheData

{

    struct stData{

         int id;

         string name;

         int exp;

         int level;

         int time;

    };

public: 

    Query(int id);

    Commit();

    SetName(string &name);

    string GetName();

    SetExp(int exp);

    int GetExp();

    SetLevel(int level);

    int GetLevel();

    SetTime(int time);

    int GetTime();

private:

    stData m_data;

}; 

这样,每次调用时先Query,把数据读出,并给m_data赋值,然后,可以多次调用get和set接口,如果有set过的话,最后只要调用commit接口,就可提交,这种方式有几个优点:

1.保证了原子性,无论是读或写几个字段,都只要两次操作即可完成

2.保证了类成员数据安全,封装等级比较高

3.数据结构调整时(比如添加一个flag字段),由于暴露给上层的只是接口,所以调用者代码无需修改

总结:

对缓存或数据库数据操作进行封装时,一定要注意原子性、通用性和可扩展性,最好给上层提供统一的接口类或实现,这样,才能保证代码不会随业务复杂性增加而快速暴涨,同时也能保证代码的清晰与易用性。

上面的封装采用了Facade模式,其实设计模式在编码中处处可以体现,这也对我们开发人员提出更高的要求,如果能合理、适度的使用设计模式,使自己的代码更简洁、更优美。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏互扯程序

java多线程-概念&创建启动&中断&守护线程&优先级&线程状态

在现代操作在运行一个程序时,会为其创建一个进程。例如启动一个QQ程序,操作系统就会为其创建一个进程。而操作系统中调度的最小单位元是线程,也叫轻量级进程,在一个进...

15330
来自专栏运维小白

10.7 free命令

监控系统状态 free 查看内存使用情况 free -m / -g / -h buffer/cache区别 公式:total=used+free+buff/ca...

26970
来自专栏FreeBuf

一次安全测试引发的对Django框架文件上传安全机制的初步分析

我司的堡垒机是基于jumpserver 0.3版本进行二次开发,进行了大量的重构和新功能的添加,基本满足了公司安全运维的需求。在对文件上传接口进行安全审计的时候...

12420
来自专栏分布式系统进阶

Kafka源码分析-配置文件

作为Class KafkaConfig的伴生类,定义了创建KafkaConfig对象的工厂方法:

9510
来自专栏程序员同行者

django基础之二

14340
来自专栏coolblog.xyz技术专栏

AbstractQueuedSynchronizer 原理分析 - Condition 实现原理

Condition是一个接口,AbstractQueuedSynchronizer 中的ConditionObject内部类实现了这个接口。Condition声...

481100
来自专栏小白安全

代码审计工具 Cobra 源码分析

0x00 前言 @0r3ak 师傅向我推荐了一款代码审计工具Cobra(wufeifei/cobra),该工具基于Python开发,可以针对多种语言的源...

50070
来自专栏Golang语言社区

Go语言并发模型:以并行处理MD5为例

简介 Go语言的并发原语允许开发者以类似于 Unix Pipe 的方式构建数据流水线 (data pipelines),数据流水线能够高效地利用 I/O和多核 ...

48260
来自专栏向治洪

android代码混淆

proguard 原理 Java代码编译成二进制class 文件,这个class 文件也可以反编译成源代码 ,除了注释外,原来的code 基本都可以看到。为了...

26980
来自专栏一个爱瞎折腾的程序猿

常用cmd代码片段及.net core打包脚本分享

保存:set currentPath=%cd% 输出:echo %currentPath

13430

扫码关注云+社区

领取腾讯云代金券