从C++示例理解开闭原则

开闭原则要求我们在编写代码时,尽量不去修改原先的代码,当出现新的业务需求时,应该通过增加新代码的形式扩展业务而不是对原代码进行修改。

假如我们现在有一批产品,每个产品都具有颜色和大小,产品其定义如下:

cpp 复制代码
enum class Color { Red, Green, Blue };
enum class Size { Small, Medium, Large };

struct Product 
{
    string name;
    Color color;
    Size size;
};

这里 Product 定义为 struct 是因为 struct 默认的访问权限是公有方便书写,并且 struct 除了访问权限其他语法与 class 相同。

我们现在需要给一组产品提供过滤功能。于是定义下面的过滤器:

cpp 复制代码
struct ProductFilter 
{
    using Items = vector<Product*>;
}

当我们需要针对 Color 的过滤时,我们增加方法 by_color:

cpp 复制代码
struct ProductFilter
{
    using Items = vector<Product*>;
    // 新增方法 by_color
    Items by_color(Items items, Color color);
}

当我们需要针对 Size 的过滤时,我们增加方法 by_size:

cpp 复制代码
struct ProductFilter
{
    using Items = vector<Product*>;
    Items by_color(Items items, Color color);
    // 新增方法 by_size
    Items by_size(Items items, Size size);
}

当我们需要针对 Color 和 Size 同时满足的筛选时,再添加...

可以看出当我们有新的需求时,必须要对 ProductFilter 类进行修改,并没有遵循开闭原则,所以我们希望重新设计使这个程序满足开闭原则,重构主要用到 template 模版编程。

首先,我们需要将过滤器分为两部分:过滤器本身和指定的过滤规范。

首先我们先定义一个规范接口,不同的过滤需求将通过继承此接口来满足:

cpp 复制代码
template <typename T> 
struct Specification 
{
    virtual bool is_satisfied(T* item) = 0;
}

这里的类型 T 可以由我们自由地指定,我们可以指定为类型 Product 也可以指定为其他类型,这就意味着,这个规范将不再局限于 Product,我们可以在任何其他类型中使用它。

接下来是过滤器接口的定义:

cpp 复制代码
template <typename T>
struct Filter
{
    virtual vector<T*> filter(vector<T*> items, Specification<T>& spec) const = 0;
}

同样地,这里使用模版编程来让过滤器不局限于对 Product 进行过滤。在虚函数 filter 中,我们接受 T 类型的容器,并通过 Specification 指定过滤规范。

然后我们需要继承 Filter 实现针对于 Product 的过滤器:

cpp 复制代码
```cpp
struct BetterFilter: Filter<Product>
{
    vector<Product*> filter(vector<Product*> items, Specification<Product>& spec) const override {
        vector<Product*> result;
        for(auto& p: items) {
            if(spec.is_satisfied(p)) {
                result.push_back(p);
            }
        }
        return result;
    }
};

在 filter 方法中我们会调用 Specification& 中实现过滤规范对 vector<Product*> 容器中的对象进行筛选。

当我们有了以上的过滤器和规范接口之后,我们便可以在不修改代码的情况下,扩展业务了。

比如:当我们需要对于颜色的过滤器时,我们只需要继承 Specification 并覆盖 is_satisfied 方法来实现颜色的过滤法则,即可达到我们的目的:

cpp 复制代码
// 颜色筛选规范
struct ColorSpecification : Specification<Product>
{
    Color color;
    explicit ColorSpecification(const Color& color) : color(color) {}
    bool is_satisfied(Product* item) override {
        return item->color == color;
    }
};

当我们需要针对 Size 的过滤时:

cpp 复制代码
// 大小筛选规范
struct SizeSpecification : Specification<Product>
{
    Size size;
    explicit SizeSpecification(const Size& size) : size(size) {}
    bool is_satisfied(Product* item) override {
        return item->size == size;
    }
};

可以看到,我们不再需要修改过滤器来达到我们的目的,很显然我们遵从了开闭原则。

需要查看完整的示例代码可以访问 Github 仓库 GnCDesignPatterns

参考:C++20设计模式

相关推荐
王老师青少年编程3 小时前
gesp(C++五级)(14)洛谷:B4071:[GESP202412 五级] 武器强化
开发语言·c++·算法·gesp·csp·信奥赛
DogDaoDao3 小时前
leetcode 面试经典 150 题:有效的括号
c++·算法·leetcode·面试··stack·有效的括号
一只小bit4 小时前
C++之初识模版
开发语言·c++
CodeClimb5 小时前
【华为OD-E卷 - 第k个排列 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
apz_end6 小时前
埃氏算法C++实现: 快速输出质数( 素数 )
开发语言·c++·算法·埃氏算法
仟濹6 小时前
【贪心算法】洛谷P1106 - 删数问题
c语言·c++·算法·贪心算法
北顾南栀倾寒7 小时前
[Qt]系统相关-网络编程-TCP、UDP、HTTP协议
开发语言·网络·c++·qt·tcp/ip·http·udp
old_power8 小时前
【PCL】Segmentation 模块—— 基于图割算法的点云分割(Min-Cut Based Segmentation)
c++·算法·计算机视觉·3d
涛ing8 小时前
21. C语言 `typedef`:类型重命名
linux·c语言·开发语言·c++·vscode·算法·visual studio
PaLu-LI9 小时前
ORB-SLAM2源码学习:Initializer.cc⑧: Initializer::CheckRT检验三角化结果
c++·人工智能·opencv·学习·ubuntu·计算机视觉