1. 模板方法模式
模板方法模式(Template Method Pattern) 是一种行为型设计模式,其核心思想 是:在抽象类 中定义一个算法的骨架 (模板方法),将算法中某些可变的步骤延迟到子类中实现,从而让子类在不改变算法整体结构的前提下,灵活定制算法的特定步骤。
模板方法模式包含以下关键角色:
- 抽象类: 定义算法的骨架(模板方法),并声明一些抽象方法(留给子类实现)和具体方法(算法中固定不变的步骤)。 模板方法通常会调用这些抽象方法和具体方法,完成整个算法流程。
- 具体子类: 继承抽象类,实现抽象类中声明的抽象方法,从而定制算法中的可变步骤。 子类不会修改模板方法本身(即算法的整体结构保持不变)。
以 "冲饮料" 为例,冲咖啡 和冲茶的流程相似(烧水→冲泡→倒杯→加调料),但 "冲泡" 和 "加调料" 的具体步骤不同,适合用模板方法模式实现:
cpp
#include <iostream>
#include <string>
// 抽象类:定义算法骨架
class Beverage {
public:
// 模板方法:定义制作饮料的整体流程(用final防止子类重写)
virtual void prepareRecipe() const final {
boilWater(); // 固定步骤:烧水
brew(); // 可变步骤:冲泡(子类实现)
pourInCup(); // 固定步骤:倒入杯子
if (customerWantsCondiments()) { // 钩子方法:可选步骤
addCondiments(); // 可变步骤:加调料(子类实现)
}
}
protected:
// 纯虚函数:必须由子类实现的步骤
virtual void brew() const = 0;
virtual void addCondiments() const = 0;
// 具体方法:固定不变的步骤
void boilWater() const {
std::cout << "烧开水" << std::endl;
}
void pourInCup() const {
std::cout << "倒入杯子中" << std::endl;
}
// 钩子方法(Hook):默认实现,子类可选择重写
virtual bool customerWantsCondiments() const {
return true; // 默认加调料
}
};
// 具体子类:咖啡
class Coffee : public Beverage {
protected:
void brew() const override {
std::cout << "用沸水冲泡咖啡粉" << std::endl;
}
void addCondiments() const override {
std::cout << "加牛奶和糖" << std::endl;
}
// 重写钩子方法:询问是否加调料
bool customerWantsCondiments() const override {
std::string answer;
std::cout << "请问需要加牛奶和糖吗?(y/n): ";
std::cin >> answer;
return answer == "y" || answer == "Y";
}
};
// 具体子类:茶
class Tea : public Beverage {
protected:
void brew() const override {
std::cout << "用沸水浸泡茶叶" << std::endl;
}
void addCondiments() const override {
std::cout << "加柠檬" << std::endl;
}
};
// 测试
int main() {
Beverage* coffee = new Coffee();
std::cout << "=== 制作咖啡 ===" << std::endl;
coffee->prepareRecipe();
Beverage* tea = new Tea();
std::cout << "\n=== 制作茶 ===" << std::endl;
tea->prepareRecipe();
delete coffee;
delete tea;
return 0;
}
特点:
- 算法骨架固定:模板方法(如prepareRecipe)定义了算法的整体流程,子类无法修改(通常用final修饰)。
- 可变步骤延迟:抽象方法(如brew、addCondiments)由子类实现,灵活定制具体细节。
- 代码复用:抽象类中封装了公共步骤(如boilWater),避免子类重复实现。
优点:
- 封装不变部分,扩展可变部分,符合 "开闭原则"。
- 提取公共代码,减少重复,便于维护。
- 父类控制算法流程,子类专注实现细节,职责清晰。
缺点:
- 每个具体实现都需要一个子类,可能导致类数量增多。
- 子类执行的结果会影响父类,一定程度上破坏了 **"里氏替换原则"**的纯粹性。
适用场景:
- 多个子类有相同的算法流程,但部分步骤实现不同(如框架中的生命周期方法)。
- 需要控制子类扩展时(只允许子类修改特定步骤,不允许改变整体流程)。
- 希望提取多个类的公共行为,集中到抽象类中。
2. 封装Socket类
AI告诉我,socket类的封装是模板方法模式的典型应用场景,但是我觉得不是很合理,原因如下。
根据我们之前编程的经验,我们可以按照套接字创建的流程和用法将套接字分为5类:
- TCP监听套接字:显式绑定地址,用于TCP服务端监听来自客户端的连接请求。
- TCP连接套接字:TCP监听套接字accept成功之后返回的用于为客户端提供服务的套接字。
- TCP客户端套接字:隐式绑定地址,通过connect与服务端建立连接。
- UDP服务端套接字:显式绑定地址,用于和客户端进行报文交流。
- UDP客户端套接字:隐式绑定地址,用于和服务端进行报文交流。
但是,显然这些套接字无论是创建的过程还是用法都大相径庭,很难说有什么固定的流程。
但是但是,AI都这么说了,我还是试着来封装一下。我希望封装之后的成果就是:各种套接字被被创建出来就直接完成了初始化,直接开始发挥自己主要的作用。
例如,TCP监听套接字被创建出来之后就可以开始调用自己的Accept方法等待连接,TCP服务端套接字、UDP服务端/客户端套接字被创建出来就可以开始不断地收发消息。
为了贴合主题,我们先将套接字初始化的流程固定一下:
cpp
// 模板方法:固定初始化流程(禁止子类重写)
virtual void Initialize() final
{
CreateSocket(); // 创建套接字
Bind(); // 绑定地址
Listen(); // 设置监听
Connect(); // 建立连接
}
这时候就有人问了:bind是每个套接字都要做的吗?listen是每个套接字都要做的吗?connect是每个套接字都要做的吗?
你说的很对,所以我们在实现子类的时候,不需要这些步骤的子类就提供对应方法的空实现即可。
做起来最麻烦的还是接口的设计,例如基类Read\Write接口的设计,如何设计参数与返回值能使得TCP套接字和UDP套接字都能通用。
总之也是非常麻烦,博主也懒得介绍具体的代码了,反正套接字编程的流程大家都很熟悉了,不知道如何设计接口的小伙伴可以参考一下博主的代码:
cpp
#pragma once
#include "Common.hpp"
#include "Mutex.hpp"
namespace SocketModule
{
const int default_pending_num = 10;
class TCPConnectSocket;
class Socket
{
protected:
int _sockfd;
InetAddr _addr;
MutexModule::Mutex _mutex; // 线程安全锁
// 基础套接字创建(供子类调用)
void CreateSocket(int type)
{
_sockfd = ::socket(AF_INET, type, 0);
if(_sockfd == -1)
{
LOG(LogLevel::FATAL) << "socket: 申请套接字失败! " << strerror(errno);
throw std::runtime_error("socket create failed");
}
}
// 模板方法的核心步骤(纯虚函数,由子类实现)
virtual void CreateSocket() = 0;
virtual void Bind() = 0;
virtual void Listen() = 0;
virtual void Connect() = 0;
// 模板方法:固定初始化流程(禁止子类重写)
virtual void Initialize() final
{
CreateSocket();
Bind();
Listen();
Connect();
}
public:
// 用于服务端/客户端套接字(未连接状态)
Socket(const std::string& ip, in_port_t port)
: _sockfd(-1)
, _addr(ip, port)
{}
// 用于已连接套接字(如accept返回的TCP连接)
Socket(const int sockfd, const InetAddr& addr)
: _sockfd(sockfd)
, _addr(addr)
{}
void Close()
{
if(_sockfd != -1)
::close(_sockfd);
_sockfd = -1;
}
// 虚析构函数确保子类资源正确释放
virtual ~Socket()
{
Close();
}
const InetAddr& addr() const { return _addr; }
// 纯虚函数:定义子类必须实现的接口
virtual std::shared_ptr<TCPConnectSocket> Accept() = 0;
// TCP协议不考虑第二个参数
virtual int Receive(std::string& recv_msg, InetAddr* peer = nullptr) = 0;
virtual int Send(const std::string& send_msg, const InetAddr& peer = InetAddr()) = 0;
};
// TCP连接套接字(由accept返回)
class TCPConnectSocket : public Socket
{
public:
TCPConnectSocket(int sockfd, const InetAddr& addr)
: Socket(sockfd, addr) // 直接使用已创建的套接字
{
Initialize();
}
// 重写基类方法(已连接套接字无需重新创建)
void CreateSocket() override {}
int Receive(std::string& recv_msg, InetAddr* peer = nullptr) override
{
MutexModule::LockGuard lock(_mutex);
char buffer[BUFFER_SIZE];
ssize_t size = ::recv(_sockfd, buffer, sizeof(buffer) - 1, 0);
if (size <= 0)
{
recv_msg.clear();
if (size < 0)
LOG(LogLevel::ERROR) << "recv error: " << strerror(errno);
return size;
}
buffer[size] = '\0';
recv_msg = buffer;
return size;
}
int Send(const std::string& send_msg, const InetAddr& peer = InetAddr()) override
{
MutexModule::LockGuard lock(_mutex);
ssize_t size = ::send(_sockfd, send_msg.c_str(), send_msg.size(), 0);
if (size == -1)
LOG(LogLevel::ERROR) << "send error: " << strerror(errno);
return size;
}
protected: // 修正访问权限
void Bind() override {} // 已连接套接字无需绑定
void Listen() override {} // 已连接套接字无需监听
void Connect() override {} // 已连接套接字无需再次连接
std::shared_ptr<TCPConnectSocket> Accept() override
{
return nullptr; // 连接套接字不处理accept
}
};
// TCP监听套接字
class TCPListenSocket : public Socket
{
public:
TCPListenSocket(in_port_t port)
: Socket("0.0.0.0", port) // 监听所有网卡
{
Initialize();
}
// 重写基类方法(显式标记override)
void CreateSocket() override
{
Socket::CreateSocket(SOCK_STREAM); // TCP类型
}
std::shared_ptr<TCPConnectSocket> Accept() override
{
MutexModule::LockGuard lock(_mutex); // 线程安全保护
InetAddr client;
int sockfd = ::accept(_sockfd, client.NetAddrPtr(), &client.AddrLen());
client = InetAddr(client.NetAddr());
if(sockfd == -1)
{
LOG(LogLevel::WARNING) << "accept: 建立连接失败! " << strerror(errno);
return nullptr;
}
LOG(LogLevel::INFO) << "accept: 建立连接成功! ";
return std::make_shared<TCPConnectSocket>(sockfd, client);
}
protected:
void Bind() override
{
int n = ::bind(_sockfd, _addr.NetAddrPtr(), _addr.AddrLen());
if(n == -1)
{
LOG(LogLevel::FATAL) << "bind: 绑定地址信息失败! " << strerror(errno);
throw std::runtime_error("bind failed");
}
}
void Listen() override
{
int n = ::listen(_sockfd, default_pending_num);
if(n == -1)
{
LOG(LogLevel::FATAL) << "listen: 设置监听套接字失败! " << strerror(errno);
throw std::runtime_error("listen failed");
}
}
void Connect() override // TCP监听套接字无需主动连接
{}
int Receive(std::string& recv_msg, InetAddr* peer = nullptr) override
{
return -1; // 监听套接字不处理接收
}
int Send(const std::string& send_msg, const InetAddr& peer = InetAddr()) override
{
return -1; // 监听套接字不处理发送
}
};
// TCP客户端套接字
class TCPClientSocket : public Socket
{
public:
TCPClientSocket(const std::string& ip, in_port_t port)
: Socket(ip, port)
{
Initialize();
}
void CreateSocket() override
{
Socket::CreateSocket(SOCK_STREAM); // TCP类型
}
void Connect() override
{
int n = ::connect(_sockfd, _addr.NetAddrPtr(), _addr.AddrLen());
if(n == -1)
{
LOG(LogLevel::FATAL) << "connect: 连接失败! " << strerror(errno);
throw std::runtime_error("connect failed");
}
LOG(LogLevel::INFO) << "客户端套接字已连接到[" << _addr.Info() << "]";
}
int Receive(std::string& recv_msg, InetAddr* peer = nullptr) override
{
MutexModule::LockGuard lock(_mutex);
char buffer[BUFFER_SIZE];
ssize_t size = ::recv(_sockfd, buffer, sizeof(buffer) - 1, 0);
if (size <= 0)
{
recv_msg.clear();
if (size < 0)
LOG(LogLevel::ERROR) << "recv error: " << strerror(errno);
return size;
}
buffer[size] = '\0';
recv_msg = buffer;
return size;
}
int Send(const std::string& send_msg, const InetAddr& peer = InetAddr()) override
{
MutexModule::LockGuard lock(_mutex);
ssize_t size = ::send(_sockfd, send_msg.c_str(), send_msg.size(), 0);
if (size == -1)
LOG(LogLevel::ERROR) << "send error: " << strerror(errno);
return size;
}
protected: // 修正访问权限
void Bind() override {} // 客户端通常不主动绑定
void Listen() override {} // 客户端无需监听
std::shared_ptr<TCPConnectSocket> Accept() override
{
return nullptr; // 客户端不处理accept
}
};
// UDP服务端套接字
class UDPServerSocket : public Socket
{
public:
UDPServerSocket(const std::string& ip, in_port_t port)
: Socket(ip, port)
{
Initialize();
}
void CreateSocket() override
{
Socket::CreateSocket(SOCK_DGRAM); // UDP类型
}
int Receive(std::string& recv_msg, InetAddr* peer = nullptr) override
{
MutexModule::LockGuard lock(_mutex);
char buffer[BUFFER_SIZE];
ssize_t size = ::recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0,
peer->NetAddrPtr(), &peer->AddrLen());
*peer = InetAddr(peer->NetAddr());
if (size <= 0)
{
recv_msg.clear();
if (size < 0)
LOG(LogLevel::ERROR) << "recvfrom error: " << strerror(errno);
return size;
}
buffer[size] = '\0';
recv_msg = buffer;
return size;
}
int Send(const std::string& send_msg, const InetAddr& peer = InetAddr()) override
{
MutexModule::LockGuard lock(_mutex);
if (peer.Ip().empty()) // 确保目标地址有效
{
LOG(LogLevel::ERROR) << "sendto error: 目标地址为空";
return -1;
}
ssize_t size = ::sendto(_sockfd, send_msg.c_str(), send_msg.size(), 0,
peer.NetAddrPtr(), peer.AddrLen());
if (size == -1)
LOG(LogLevel::ERROR) << "sendto error: " << strerror(errno);
return size;
}
protected: // 修正访问权限并添加override
void Bind() override
{
int n = ::bind(_sockfd, _addr.NetAddrPtr(), _addr.AddrLen());
if(n == -1)
{
LOG(LogLevel::FATAL) << "bind: 绑定地址信息失败! " << strerror(errno);
throw std::runtime_error("bind failed");
}
}
void Listen() override {} // UDP无需监听
void Connect() override {} // UDP服务端无需主动连接
std::shared_ptr<TCPConnectSocket> Accept() override
{
return nullptr; // UDP不支持accept
}
};
// UDP客户端套接字
class UDPClientSocket : public Socket
{
public:
UDPClientSocket(const std::string& ip, in_port_t port)
: Socket(ip, port)
{
Initialize();
}
void CreateSocket() override
{
Socket::CreateSocket(SOCK_DGRAM); // UDP类型
}
int Receive(std::string& recv_msg, InetAddr* peer = nullptr) override
{
MutexModule::LockGuard lock(_mutex);
char buffer[BUFFER_SIZE];
ssize_t size = ::recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0,
peer->NetAddrPtr(), &peer->AddrLen());
*peer = InetAddr(peer->NetAddr());
if (size <= 0)
{
recv_msg.clear();
if (size < 0)
LOG(LogLevel::ERROR) << "recvfrom error: " << strerror(errno);
return size;
}
buffer[size] = '\0';
recv_msg = buffer;
return size;
}
int Send(const std::string& send_msg, const InetAddr& peer = InetAddr()) override
{
MutexModule::LockGuard lock(_mutex);
InetAddr target = peer.Ip().empty() ? _addr : peer; // 支持默认目标地址
ssize_t size = ::sendto(_sockfd, send_msg.c_str(), send_msg.size(), 0,
target.NetAddrPtr(), target.AddrLen());
if (size == -1)
LOG(LogLevel::ERROR) << "sendto error: " << strerror(errno);
return size;
}
protected: // 修正访问权限并添加override
void Bind() override {} // UDP客户端通常不绑定
void Listen() override {} // UDP无需监听
void Connect() override {} // UDP无需连接
std::shared_ptr<TCPConnectSocket> Accept() override
{
return nullptr; // UDP不支持accept
}
};
}
deepseek也是给出了中肯的评价: