这个模式啊,怎么说呢,很多书里都说它是个补救模式,在设计的时候不要去考虑。
文章目录
- 出国旅游必备之“电源适配器”
- 生火做饭
- 舍它其谁的场景
- 可用可不用的场景
出国旅游必备之“电源适配器”
见过电源适配器吗?电脑手机充电器就是“电源适配器”,但是我国的标准电压是220V,出了国可就不好乱插,别的国家的电压和我国不同。这时候就需要一个便携的电源变压器将国外电压转换成220V,这便是“适配器模式”的精髓:将你不能用的东西,在不破坏原物的前提下,通过中转变成你能用的东西。
生火做饭
上面呢,是对于“适配器模式”的标准理解,但是我有自己的理解。
我们的祖先呢,一开始是吃生的东西,后来有了火呢,他们就吃熟的东西了,那时候没那么多讲究,就是熟的好吃,所以吃熟的。看现在的人,也是可以吃生肉的,只是通过火的作用,会使得食物更加健康。
这个火,在我看来就是属于“适配器”,将本来可吃可不吃的生肉,变成了熟肉。我觉得吧,只要能使得项目某些小方面更加的便利,就可以采用适当的适配转换,不一定非要到了“别无他法”的时候才采用“适配器”。
舍它其谁的场景
来看个解压包模块的部分代码:
//Packetbase.h
#define MAX_PACKET_LENTH 1024
//设置包最大长度为1024,
// ! ! ! 暂时不考虑会太小的情况 ! ! !
typedef struct packet_header_st
{
int fd;//用于前后端通信即目标客户端fd(服务器用到)
int funcId; // 功能号
//登录包0x01,注册包0x02,找回密码0x03,修改密码0x04
//客户端获取文件列表0x11,上传文件0x12,下载文件0x13,共享文件0x14,
//新增目录0x15,删除目录0x16,删除文件0x17,文件移动0x18,目录移动0x19,文件重命名0x20,目录重命名0x22
//心跳0x21
//边缘服务器向中控服务器认证 0x30
int optid; // 操作码:请求0x00 和 应答0x01
int usrlenth;// 包体的长度
int packet_seq; //包序号
int packet_sum; //包总数
char to_fd[6]; //目标服务器名称
char dstAddr[6]; //预留
int syn; // 判断包头是否正确 0x04
}packet_header_t;
typedef struct packet_tali_st//包尾,用来验证数据包的完整性
{
int pack_tail;//设置为0x05
}packet_tali_t;
typedef struct packet_all_st
{
packet_header_t Head;
char* body;
packet_tali_st tail;
}packet_all_st;
上面的部分,是属于包头、包体、包尾的,属于解压包模块内聚部分,不对外开放。
接下来是实际拓展包:
//客户端登录请求包
typedef struct login
{
int id;
int pwd; //密码
}Login_t;
//登录应答包
typedef struct res_login_st
{
int login_ret; //登录结果: 1-登录成功,0-登录失败
int dir_id; //初始文件列表id
char reson[20]; //如果登录失败,返回失败原因,如果登录成功,返回初始目录名
}res_login_t;
//客户端注册请求包
typedef struct Register
{
int id; //账号
char tel[12]; //11位手机号
int pwd; //密码
}Register_t;
//注册应答包
typedef struct res_register_st
{
int register_ret; //失败返回0,成功返回1
char reson[20];
}res_register_t;
那么,要如何将不同的结构体塞进固定数据类型的包体 char* body; 呢?
使用适配器:
bool PacketCommand1::replyLogin(int state, int dir_id, char* reson, int fd) {
int sz = sizeof(res_login_t);
this->setBodySize(sz);
this->Body = new char[sz];
Head.funcId = 0x01;
Head.optid = 0x01;
Head.fd = fd;
Head.usrlenth = sz;
Head.syn = 0x04;
strcpy(Head.to_fd, "Front");
res_login_t* body = (res_login_t*)Body; //适配器
body->login_ret = state;
body->dir_id = dir_id;
strcpy(body->reson, reson);
Tail.pack_tail = 0x05;
return this->pack();
}
这里的适配器,体现在这句:res_login_t* body = (res_login_t*)Body; 以及后面的部分。
解压包模块中的包,动辄几十上百种,且包大小都不一样,如果每种包都要有一个专属包体的话,那成何体统?所以包体初始化只为char*,具体情况具体定。
可用可不用的场景
我呢,弄了个中控服务器,和几个边缘服务器。
中控服务器的功能呢,转接各边缘服务器的数据通信,开放接口:accept、read、send
边缘服务器各司其职,比方说epoll吧,开放接口:accept(面向客户端)、read(面向客户端)、send(面向客户端)。
所以呢,这两个服务器之间并不能直接通信。其实也可以直接在原有功能上加一个connect的函数,然后配置信息使这条连接面向中控,在在其中读写信息。但是,我的边缘服务器都写好了,硬塞这个功能进去实在是烦,而且每个边缘服务器都有向中控服务器通信的需求,于是我干脆弄了个转接类,专门处理边缘到中介之间的转接:
class EtoC //Edge to Command
{
private:
int connect_fd;
public:
EtoC();
void Start(const char* commandIP); //建立连接
void send_name(char* name); //发送认证包
int Read_date(char* date); //读取数据
int get_fd(); //获取通信套接字
int Write_date(char* date,int len); //写入数据
};
功能很简单,但是足以满足它们之间连通的需求了。
这么多设计模式嘛,还是要活用,死守定律就没啥意思了。