
设计模式你在前端一定用过,但 C++ 的实现有其独特之处——需要和智能指针、虚函数、编译期特性结合。这篇从生产级 C++ 渲染框架中提炼了 5 个模式,展示"同一个模式在 C++ 中长什么样"。
框架定义"创建接口",业务方注册"怎么创建",运行时按名字查表创建。
// ========== 1. 定义接口和创建器类型 ==========
class IView {
public:
virtual void Render() = 0;
virtual ~IView() = default;
};
using ViewCreator = std::function<std::shared_ptr<IView>()>;
// ========== 2. 注册表(框架提供) ==========
class ViewFactory {
public:
static void Register(const std::string& name, ViewCreator creator) {
GetRegistry()[name] = std::move(creator);
}
static std::shared_ptr<IView> Create(const std::string& name) {
auto& registry = GetRegistry();
auto it = registry.find(name);
if (it != registry.end()) {
return it->second();
}
// fallback:返回通用实现
auto fallback = registry.find("__default__");
if (fallback != registry.end()) {
return fallback->second();
}
return nullptr;
}
private:
static std::unordered_map<std::string, ViewCreator>& GetRegistry() {
static std::unordered_map<std::string, ViewCreator> registry;
return registry;
}
};
// ========== 3. 业务方注册(启动时执行一次) ==========
class ImageView : public IView {
public:
void Render() override { /* 渲染图片 */ }
};
class VideoView : public IView {
public:
void Render() override { /* 渲染视频 */ }
};
// 启动时
ViewFactory::Register("ImageView", []() { return std::make_shared<ImageView>(); });
ViewFactory::Register("VideoView", []() { return std::make_shared<VideoView>(); });
// ========== 4. 框架内部使用 ==========
auto view = ViewFactory::Create("ImageView");
view->Render();// React 动态组件注册 — 同一思想
const componentRegistry = new Map<string, () => React.ComponentType>();
componentRegistry.set("ImageView", () => ImageView);
componentRegistry.set("VideoView", () => VideoView);
function createComponent(name: string) {
const Component = componentRegistry.get(name)?.();
return Component ? <Component /> : <FallbackView />;
}shared_ptr — 工厂创建的对象通过智能指针管理,调用方不用管释放static 局部变量实现注册表 — 避免全局变量初始化顺序问题(C++ 特有坑)确保一个类只有一个实例,提供全局访问点。
class EventCenter {
public:
// 获取唯一实例
static EventCenter& GetInstance() {
static EventCenter instance; // C++11 保证线程安全
return instance;
}
void Subscribe(const std::string& event, std::function<void()> handler) {
handlers_[event].push_back(std::move(handler));
}
void Emit(const std::string& event) {
auto it = handlers_.find(event);
if (it != handlers_.end()) {
for (auto& handler : it->second) {
handler();
}
}
}
// 禁止拷贝和赋值
EventCenter(const EventCenter&) = delete;
EventCenter& operator=(const EventCenter&) = delete;
private:
// 构造函数私有,外部无法 new
EventCenter() = default;
std::unordered_map<std::string, std::vector<std::function<void()>>> handlers_;
};
// 使用
EventCenter::GetInstance().Subscribe("ready", []() { /* ... */ });
EventCenter::GetInstance().Emit("ready");// TS 单例 — 思路一致但 JS 更简单
class EventCenter {
private static instance: EventCenter;
static getInstance() {
if (!this.instance) this.instance = new EventCenter();
return this.instance;
}
private constructor() {}
}
// 或者更 JS 的方式:直接导出一个对象
export const eventCenter = { handlers: {}, subscribe() {}, emit() {} };= delete 禁止拷贝 — JS 中对象天然不会被"拷贝出一份新的",但 C++ 会(值语义),必须显式禁止static 局部变量 — C++11 保证线程安全的懒初始化,比双重检查锁更简洁& — 避免指针判空,调用方直接 . 操作在不改变接口的前提下,在真实对象前面加一层"中间人",做额外的事情。
// ========== 接口 ==========
class IRenderLayer {
public:
virtual void CreateView(int tag, const std::string& name) = 0;
virtual void SetProp(int tag, const std::string& key, const std::string& value) = 0;
virtual void RemoveView(int tag) = 0;
virtual ~IRenderLayer() = default;
};
// ========== 真实实现 ==========
class RenderLayerImpl : public IRenderLayer {
public:
void CreateView(int tag, const std::string& name) override {
// 真正创建原生 UI 节点
}
void SetProp(int tag, const std::string& key, const std::string& value) override {
// 真正设置属性
}
void RemoveView(int tag) override {
// 真正删除节点
}
};
// ========== 代理层(缓存加速) ==========
class CacheProxyLayer : public IRenderLayer {
public:
CacheProxyLayer()
: real_layer_(std::make_shared<RenderLayerImpl>()) {}
void CreateView(int tag, const std::string& name) override {
// 额外逻辑:同时维护一棵虚拟节点树
virtual_tree_[tag] = {name, {}};
// 委托真实渲染层
if (!is_using_cache_) {
real_layer_->CreateView(tag, name);
}
}
void SetProp(int tag, const std::string& key, const std::string& value) override {
// 额外逻辑:记录到虚拟树
virtual_tree_[tag].props[key] = value;
// 委托真实渲染层
if (!is_using_cache_) {
real_layer_->SetProp(tag, key, value);
}
}
void RemoveView(int tag) override {
virtual_tree_.erase(tag);
if (!is_using_cache_) {
real_layer_->RemoveView(tag);
}
}
private:
std::shared_ptr<RenderLayerImpl> real_layer_;
std::unordered_map<int, VirtualNode> virtual_tree_;
bool is_using_cache_ = false;
};// JS Proxy — 语言内置支持
const realApi = { fetchData() { /* 真实请求 */ } };
const cachedApi = new Proxy(realApi, {
get(target, prop) {
return (...args) => {
if (cache.has(args)) return cache.get(args); // 额外逻辑
return target[prop](...args); // 委托真实对象
};
}
});shared_ptr,不是继承它if (!is_using_cache_) 让代理可以"拦截"而不是"转发"定义统一接口,让不同的具体实现可以互换插入。
// ========== 框架定义的统一接口 ==========
class IImageLoader {
public:
virtual void Load(const std::string& url, std::function<void(Image)> callback) = 0;
virtual ~IImageLoader() = default;
};
class ILogger {
public:
virtual void Info(const std::string& tag, const std::string& msg) = 0;
virtual void Error(const std::string& tag, const std::string& msg) = 0;
virtual ~ILogger() = default;
};
// ========== 适配器管理器 ==========
class AdapterManager {
public:
static AdapterManager& GetInstance() {
static AdapterManager instance;
return instance;
}
void SetImageLoader(std::shared_ptr<IImageLoader> loader) { image_loader_ = loader; }
void SetLogger(std::shared_ptr<ILogger> logger) { logger_ = logger; }
IImageLoader* GetImageLoader() { return image_loader_.get(); }
ILogger* GetLogger() { return logger_.get(); }
private:
std::shared_ptr<IImageLoader> image_loader_;
std::shared_ptr<ILogger> logger_;
};
// ========== App A 的实现 ==========
class GlideImageLoader : public IImageLoader {
void Load(const std::string& url, std::function<void(Image)> callback) override {
// 用 Glide 加载
}
};
// ========== App B 的实现 ==========
class CoilImageLoader : public IImageLoader {
void Load(const std::string& url, std::function<void(Image)> callback) override {
// 用 Coil 加载
}
};
// ========== 宿主 App 启动时注入 ==========
AdapterManager::GetInstance().SetImageLoader(std::make_shared<GlideImageLoader>());// 依赖注入 — 前端框架中很常见
interface IImageLoader {
load(url: string): Promise<HTMLImageElement>;
}
// 框架内部通过接口调用,不关心具体实现
class ImageView {
constructor(private loader: IImageLoader) {}
render(url: string) {
this.loader.load(url).then(img => { /* 渲染 */ });
}
}
// 不同环境注入不同实现
const loader = isNode ? new SharpLoader() : new BrowserLoader();
new ImageView(loader);对象用完不销毁,放回池子里,下次需要时直接取出来复用。
class ViewPool {
public:
// 从池中取一个可复用的 View(没有就返回 nullptr)
std::shared_ptr<IView> Acquire(const std::string& view_name) {
auto it = pool_.find(view_name);
if (it != pool_.end() && !it->second.empty()) {
auto view = it->second.back();
it->second.pop_back();
return view;
}
return nullptr;
}
// 用完放回池中
void Release(const std::string& view_name, std::shared_ptr<IView> view) {
view->Reset(); // 重置为初始状态
pool_[view_name].push_back(std::move(view));
}
// 清空池子(内存紧张时调用)
void Clear() {
pool_.clear();
}
private:
std::unordered_map<std::string, std::vector<std::shared_ptr<IView>>> pool_;
};
// ========== 框架使用 ==========
std::shared_ptr<IView> GetOrCreateView(const std::string& name) {
// 先尝试从池中取
auto view = pool.Acquire(name);
if (view) return view;
// 池中没有,新建
return ViewFactory::Create(name);
}
void RecycleView(const std::string& name, std::shared_ptr<IView> view) {
if (view->CanReuse()) {
pool.Release(name, view); // 可复用:回收
} else {
view->Destroy(); // 不可复用:销毁
}
}// React 中的 key 复用 + RecyclerView 思想
// 前端通常靠虚拟 DOM diff 实现复用,不需要手动管理池子
// 但 Canvas 游戏中经常有对象池:
class BulletPool {
private pool: Bullet[] = [];
acquire(): Bullet {
return this.pool.pop() ?? new Bullet();
}
release(bullet: Bullet) {
bullet.reset();
this.pool.push(bullet);
}
}Reset() 方法 — 复用前必须清理上一次的状态,否则数据"串台"CanReuse() 判断 — 不是所有对象都适合复用(如正在播动画的 View)Clear() 释放池中对象模式 | 核心诉求 | C++ 关键实现手段 |
|---|---|---|
工厂(注册制) | 解耦创建和使用 |
|
单例 | 全局唯一 |
|
代理 | 透明插入逻辑 | 实现同一接口 + 内部组合真实对象 |
适配器 | 统一不同实现 | 纯虚接口 + 运行时注入 |
对象池 | 避免频繁创建销毁 |
|
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。