1. C过滤器模式深度解析过滤器模式Filter Pattern是C中一种结构型设计模式它允许我们按照特定条件筛选对象集合。这种模式在数据处理、日志系统、游戏开发等领域应用广泛特别是在需要多层筛选逻辑的场景中优势明显。我在实际项目中最常用到过滤器模式的场景是游戏开发中的碰撞检测系统。比如需要从数百个游戏对象中筛选出满足以下条件的对象位于玩家半径5米内、生命值大于0、属于敌方阵营。传统if-else嵌套写法会让代码难以维护而过滤器模式通过链式调用可以优雅地解决这个问题。2. 过滤器模式的核心结构2.1 基础类设计标准的过滤器模式包含三个核心组件// 过滤标准接口 class Criteria { public: virtual bool meetCriteria(const GameObject obj) const 0; virtual ~Criteria() default; }; // 具体过滤实现 class HealthCriteria : public Criteria { float minHealth; public: explicit HealthCriteria(float health) : minHealth(health) {} bool meetCriteria(const GameObject obj) const override { return obj.health minHealth; } }; // 过滤容器 class Filter { public: static std::vectorGameObject filterObjects( const std::vectorGameObject objects, const Criteria criteria) { std::vectorGameObject result; for (const auto obj : objects) { if (criteria.meetCriteria(obj)) { result.push_back(obj); } } return result; } };2.2 组合过滤器过滤器模式的强大之处在于可以组合多个条件class AndCriteria : public Criteria { const Criteria first; const Criteria second; public: AndCriteria(const Criteria f, const Criteria s) : first(f), second(s) {} bool meetCriteria(const GameObject obj) const override { return first.meetCriteria(obj) second.meetCriteria(obj); } }; // 使用示例 HealthCriteria healthy(50.0f); FactionCriteria enemy(Faction::Enemy); AndCriteria healthyEnemy(healthy, enemy); auto targets Filter::filterObjects(gameObjects, healthyEnemy);3. 现代C的优化实现3.1 使用lambda表达式C11之后我们可以用lambda简化过滤器实现auto filter [](const auto objects, auto predicate) { std::vectorstd::decay_tdecltype(objects[0]) result; std::copy_if(objects.begin(), objects.end(), std::back_inserter(result), predicate); return result; }; // 使用示例 auto results filter(gameObjects, [](const GameObject obj) { return obj.health 50 obj.faction Faction::Enemy; });3.2 模板元编程实现对于性能敏感场景可以使用编译期过滤器template typename T, typename Predicate constexpr auto compileTimeFilter(const T container, Predicate p) { T result{}; for (const auto item : container) { if (p(item)) { result.insert(result.end(), item); } } return result; } // 使用示例 constexpr std::arrayint, 5 arr{1,2,3,4,5}; constexpr auto evens compileTimeFilter(arr, [](int x) { return x % 2 0; }); static_assert(evens.size() 2);4. 性能优化技巧4.1 缓存友好设计过滤器模式可能造成内存碎片我们可以优化内存访问class BatchFilter { std::vectorsize_t indices; // 预分配内存 public: template typename Container, typename Predicate const Container filter(Container c, Predicate p) { indices.clear(); for (size_t i 0; i c.size(); i) { if (p(c[i])) indices.push_back(i); } // 原地交换元素避免拷贝 for (size_t i 0; i indices.size(); i) { std::swap(c[i], c[indices[i]]); } c.resize(indices.size()); return c; } };4.2 并行过滤对于大型数据集可以使用并行算法#include execution auto parallelFilter [](const auto container, auto pred) { std::vectorstd::decay_tdecltype(container[0]) result; std::mutex mutex; std::for_each(std::execution::par, container.begin(), container.end(), [](const auto item) { if (pred(item)) { std::lock_guard lock(mutex); result.push_back(item); } }); return result; };5. 实际应用案例5.1 游戏中的AI决策系统在游戏AI中我们经常需要筛选符合条件的攻击目标class TargetSelector { std::vectorstd::unique_ptrCriteria criteriaChain; public: TargetSelector addCriteria(std::unique_ptrCriteria crit) { criteriaChain.push_back(std::move(crit)); return *this; } std::vectorGameObject selectTargets( const std::vectorGameObject candidates) const { std::vectorGameObject results candidates; for (const auto criteria : criteriaChain) { results Filter::filterObjects(results, *criteria); if (results.empty()) break; // 提前终止 } return results; } }; // 构建过滤链 TargetSelector selector; selector.addCriteria(std::make_uniqueRangeCriteria(player, 5.0f)) .addCriteria(std::make_uniqueHealthCriteria(0.0f)) .addCriteria(std::make_uniqueFactionCriteria(Faction::Enemy));5.2 日志系统过滤在日志系统中实现多级过滤class LogFilter : public Criteria { LogLevel minLevel; std::string keyword; public: LogFilter(LogLevel level, std::string_view kw) : minLevel(level), keyword(kw) {} bool meetCriteria(const LogEntry entry) const override { return entry.level minLevel entry.message.find(keyword) ! std::string::npos; } }; // 使用示例 LogFilter errorFilter(LogLevel::Error, timeout); auto criticalLogs Filter::filter(logEntries, errorFilter);6. 常见问题与解决方案6.1 过滤器顺序优化过滤器的应用顺序会影响性能。通常应该将最严格的条件放在前面将计算量小的条件放在前面考虑各条件的过滤比例// 不好的顺序 - 先进行昂贵计算 selector.addCriteria(std::make_uniqueDistanceCalcCriteria()) // 昂贵 .addCriteria(std::make_uniqueTypeCriteria()); // 简单 // 优化后的顺序 selector.addCriteria(std::make_uniqueTypeCriteria()) // 先过滤掉大部分 .addCriteria(std::make_uniqueDistanceCalcCriteria()); // 对少量对象计算6.2 动态过滤器注册实现运行时可配置的过滤器系统class FilterFactory { std::unordered_mapstd::string, std::functionstd::unique_ptrCriteria(const Json) creators; public: template typename T void registerFilter(const std::string name) { creators[name] [](const Json config) { return std::make_uniqueT(config); }; } std::unique_ptrCriteria create( const std::string name, const Json config) const { if (auto it creators.find(name); it ! creators.end()) { return it-second(config); } throw std::runtime_error(Unknown filter type); } }; // 注册过滤器类型 factory.registerFilterHealthCriteria(health); factory.registerFilterRangeCriteria(range); // 从配置文件创建过滤链 auto criteria factory.create(json[type], json[config]);7. 测试策略7.1 单元测试过滤器为过滤器编写全面的测试用例TEST(HealthFilterTest, FiltersCorrectly) { std::vectorGameObject objects { GameObject{100}, GameObject{50}, GameObject{10} }; HealthCriteria criteria(50.0f); auto result Filter::filterObjects(objects, criteria); ASSERT_EQ(result.size(), 2); EXPECT_GE(result[0].health, 50.0f); EXPECT_GE(result[1].health, 50.0f); } TEST(CompositeFilterTest, AndConditionWorks) { HealthCriteria health(30.0f); FactionCriteria faction(Faction::Enemy); AndCriteria combined(health, faction); GameObject healthyEnemy{40, Faction::Enemy}; GameObject healthyAlly{40, Faction::Ally}; EXPECT_TRUE(combined.meetCriteria(healthyEnemy)); EXPECT_FALSE(combined.meetCriteria(healthyAlly)); }7.2 性能测试比较不同实现方式的性能void benchmarkFilters() { std::vectorGameObject objects(1000000); // 填充测试数据... BENCHMARK(Traditional filter) { return Filter::filterObjects(objects, HealthCriteria(50.0f)); }; BENCHMARK(Lambda filter) { return filter(objects, [](const auto obj) { return obj.health 50.0f; }); }; BENCHMARK(Parallel filter) { return parallelFilter(objects, [](const auto obj) { return obj.health 50.0f; }); }; }8. 扩展模式变体8.1 装饰器模式结合通过装饰器模式动态添加过滤条件class DecoratedCriteria : public Criteria { protected: const Criteria wrapped; public: explicit DecoratedCriteria(const Criteria crit) : wrapped(crit) {} }; class NotCriteria : public DecoratedCriteria { public: using DecoratedCriteria::DecoratedCriteria; bool meetCriteria(const GameObject obj) const override { return !wrapped.meetCriteria(obj); } }; // 使用示例 HealthCriteria healthy(50.0f); NotCriteria notHealthy(healthy); // 过滤不健康的对象8.2 策略模式切换在不同过滤算法间动态切换class FilterStrategy { std::unique_ptrCriteria strategy; public: void setStrategy(std::unique_ptrCriteria newStrategy) { strategy std::move(newStrategy); } std::vectorGameObject apply( const std::vectorGameObject objects) const { if (!strategy) return {}; return Filter::filterObjects(objects, *strategy); } }; // 运行时切换策略 FilterStrategy strategy; strategy.setStrategy(std::make_uniqueHealthCriteria(30.0f)); auto result1 strategy.apply(objects); strategy.setStrategy(std::make_uniqueRangeCriteria(player, 10.0f)); auto result2 strategy.apply(objects);9. 与其他模式的协作9.1 与观察者模式结合实现动态更新的过滤器class ObservableCriteria : public Criteria, public Observable { std::unique_ptrCriteria baseCriteria; public: explicit ObservableCriteria(std::unique_ptrCriteria crit) : baseCriteria(std::move(crit)) {} void updateCriteria(std::unique_ptrCriteria newCrit) { baseCriteria std::move(newCrit); notifyObservers(); } bool meetCriteria(const GameObject obj) const override { return baseCriteria-meetCriteria(obj); } }; // 使用示例 auto observable std::make_sharedObservableCriteria( std::make_uniqueHealthCriteria(50.0f)); // 当需要修改条件时 observable-updateCriteria(std::make_uniqueHealthCriteria(70.0f));9.2 与工厂模式结合创建可配置的过滤器工厂class CriteriaFactory { public: static std::unique_ptrCriteria createHealthFilter(float minHealth) { return std::make_uniqueHealthCriteria(minHealth); } static std::unique_ptrCriteria createRangeFilter( const GameObject center, float radius) { return std::make_uniqueRangeCriteria(center, radius); } static std::unique_ptrCriteria createAndFilter( std::unique_ptrCriteria lhs, std::unique_ptrCriteria rhs) { return std::make_uniqueAndCriteria(*lhs, *rhs); } }; // 使用工厂创建复杂过滤器 auto filter CriteriaFactory::createAndFilter( CriteriaFactory::createHealthFilter(50.0f), CriteriaFactory::createRangeFilter(player, 10.0f) );10. 最佳实践总结经过多个项目的实践验证我总结了以下C过滤器模式的最佳实践优先使用标准库算法在简单场景下std::copy_iflambda通常比完整实现过滤器模式更简洁注意过滤器对象的生命周期组合过滤器时确保被组合的过滤器生命周期足够长考虑异常安全性过滤器函数应该是nothrow的避免在过滤过程中抛出异常性能关键路径避免虚函数在性能敏感场景可以考虑CRTP模式替代虚函数提供良好的诊断信息为过滤器实现operator便于调试时输出当前过滤条件std::ostream operator(std::ostream os, const Criteria crit) { if (auto hc dynamic_castconst HealthCriteria*(crit)) { return os Health hc-minHealth; } // 其他类型的输出... return os Unknown criteria; }支持撤销操作考虑实现逆向过滤器可以恢复被过滤掉的元素内存分配优化预分配结果容器内存避免多次重新分配线程安全考虑如果过滤器需要修改内部状态确保适当的同步机制支持序列化为过滤器实现序列化接口便于保存和加载过滤条件文档化过滤语义明确记录每个过滤器的前置条件和后置条件