新谈设计模式 Chapter 20 — 策略模式 Strategy
Chapter 20 — 策略模式 Strategy灵魂速记导航 APP——同一段路选最快/最短/不走高速路线随你换。秒懂类比打开高德地图导航策略 1时间最短走高速策略 2距离最短走小路策略 3不走高速省过路费同一件事导航不同的做法路线运行时随便换。问题引入// 灾难现场排序时写死了算法classSorter{voidsort(std::vectorintdata){// 写死了冒泡排序for(inti0;idata.size();i)for(intj0;jdata.size()-1;j)if(data[j]data[j1])std::swap(data[j],data[j1]);}};// 想换快速排序改这个函数// 想根据数据量自动选算法加 if-else// 想让用户选又加 if-else// 模式结构┌──────────────┐ ┌──────────────┐ │ Context │───────→│ Strategy │ │ (导航APP) │ 持有 │ (路线策略) │ ├──────────────┤ ├──────────────┤ │ setStrategy│ │ execute() │ │ doWork() │ └───────┬──────┘ └──────────────┘ │ 实现 ┌────────┴────────┐ │ │ ┌─────┴──┐ ┌─────┴──┐ │FastRoute│ │ShortRoute│ │execute│ │execute │ └────────┘ └─────────┘C 实现方式一经典多态版#includeiostream#includememory#includevector#includealgorithm// 策略接口 classSortStrategy{public:virtual~SortStrategy()default;virtualvoidsort(std::vectorintdata)0;virtualstd::stringname()const0;};// 具体策略 classBubbleSort:publicSortStrategy{public:voidsort(std::vectorintdata)override{for(size_t i0;idata.size();i)for(size_t j0;j1data.size()-i;j)if(data[j]data[j1])std::swap(data[j],data[j1]);}std::stringname()constoverride{return冒泡排序;}};classQuickSort:publicSortStrategy{public:voidsort(std::vectorintdata)override{sortRange(data,0,static_castint(data.size())-1);}std::stringname()constoverride{return快速排序;}private:voidsortRange(std::vectorintd,intlo,inthi){if(lohi)return;intpivotd[hi],ilo;for(intjlo;jhi;j)if(d[j]pivot)std::swap(d[i],d[j]);std::swap(d[i],d[hi]);sortRange(d,lo,i-1);sortRange(d,i1,hi);}};classSTLSort:publicSortStrategy{public:voidsort(std::vectorintdata)override{std::sort(data.begin(),data.end());}std::stringname()constoverride{returnstd::sort;}};// Context classSorter{public:voidsetStrategy(std::unique_ptrSortStrategystrategy){strategy_std::move(strategy);}voidsort(std::vectorintdata){if(!strategy_)throwstd::runtime_error(未设置排序策略);std::cout使用 [strategy_-name()] 排序 data.size() 个元素\n;strategy_-sort(data);}private:std::unique_ptrSortStrategystrategy_;};voidprintVec(conststd::vectorintv){for(intx:v)std::coutx ;std::cout\n;}intmain(){Sorter sorter;std::vectorintdata{5,2,8,1,9,3};// 运行时切换策略sorter.setStrategy(std::make_uniqueBubbleSort());autod1data;sorter.sort(d1);printVec(d1);sorter.setStrategy(std::make_uniqueQuickSort());autod2data;sorter.sort(d2);printVec(d2);sorter.setStrategy(std::make_uniqueSTLSort());autod3data;sorter.sort(d3);printVec(d3);}输出使用 [冒泡排序] 排序 6 个元素 1 2 3 5 8 9 使用 [快速排序] 排序 6 个元素 1 2 3 5 8 9 使用 [std::sort] 排序 6 个元素 1 2 3 5 8 9方式二std::function版现代 C 推荐#includefunctional#includevector#includealgorithmclassSorter{public:usingStrategystd::functionvoid(std::vectorint);voidsetStrategy(Strategy s){strategy_std::move(s);}voidsort(std::vectorintdata){if(strategy_)strategy_(data);}private:Strategy strategy_;};intmain(){Sorter sorter;std::vectorintdata{5,2,8,1,9};// Lambda 策略升序sorter.setStrategy([](autod){std::sort(d.begin(),d.end());});sorter.sort(data);// Lambda 策略降序sorter.setStrategy([](autod){std::sort(d.begin(),d.end(),std::greater{});});autodata2data;sorter.sort(data2);}如果策略只是一个函数用std::function Lambda 比搞一堆类简洁多了。什么时候用✅ 适合❌ 别用有多种算法可选运行时切换算法固定不变想消除 if-else 选算法的代码只有 2 种算法且不会增加算法需要独立测试和复用算法很简单内联就行防混淆Strategy vs State这是最容易混淆的一对记住StrategyState谁决定切换客户端选策略状态自己决定转换状态间关系策略之间互不知道状态之间互相知道切换频率通常设置一次频繁自动切换类比你选导航路线红绿灯自动轮转终极区分法如果对象的行为变化是用户主动选择的用 Strategy。如果是对象内部自动流转的用 State。Strategy vs Template MethodStrategyTemplate Method核心机制组合持有策略对象继承子类重写步骤粒度替换整个算法替换算法中的某个步骤灵活度运行时切换编译时确定类数量策略类可能很多子类相对少Strategy vs CommandStrategyCommand语义怎么做算法选择做什么请求封装可撤销不关心天然支持类比选路线下订单