如何设计文本格式化对象以实现多状态多模式的多样化打印
嘿,这个需求其实可以通过策略模式结合枚举状态来优雅实现,既满足不同格式的扩展性,又能清晰关联Mode和State的逻辑。我给你捋捋具体的设计思路和代码实现:
核心思路
我们把「格式化逻辑」和「状态/模式管理」拆分开:
- 定义格式化策略基类,每种输出格式对应一个策略子类
Formatter类负责管理当前的Mode、State和数据字段,并根据状态组合自动选择对应的格式化策略- 重载
operator<<让Formatter可以直接输出到流,符合C++的IO风格
具体代码实现
首先修正并完善枚举定义(用enum class更安全,避免隐式转换):
#include <iostream> #include <vector> #include <string> #include <memory> // 模式枚举 enum class Mode { mode1 = 0, mode2 }; // 状态枚举 enum class State { state1 = 0, state2 }; // 存储单个键值对的结构体 struct DataField { std::string name; std::string value; };
接下来定义格式化策略的抽象基类:
// 格式化策略基类:所有格式都要实现这个接口 class FormatStrategy { public: virtual ~FormatStrategy() = default; // 核心方法:接收输出流、数据字段、当前模式和状态,完成格式化输出 virtual void format(std::ostream& os, const std::vector<DataField>& fields, Mode mode, State state) const = 0; };
然后为三种需求格式实现具体的策略类:
// 策略1:name1 value1 name2 value2 ... 格式 class KeyValuePairStrategy : public FormatStrategy { public: void format(std::ostream& os, const std::vector<DataField>& fields, Mode mode, State state) const override { for (size_t i = 0; i < fields.size(); ++i) { if (i > 0) os << " "; os << fields[i].name << " " << fields[i].value; } // 可以根据模式/状态做额外调整,比如mode2时自动换行 if (mode == Mode::mode2) os << "\n"; } }; // 策略2:只输出值,value1 value2 value3 格式 class ValueOnlyStrategy : public FormatStrategy { public: void format(std::ostream& os, const std::vector<DataField>& fields, Mode mode, State state) const override { for (size_t i = 0; i < fields.size(); ++i) { if (i > 0) os << " "; os << fields[i].value; } // 示例:state2时末尾加逗号 if (state == State::state2) os << ","; } }; // 策略3:name1 : value1. name2 : value2 ... 格式 class ColonKeyValueStrategy : public FormatStrategy { public: void format(std::ostream& os, const std::vector<DataField>& fields, Mode mode, State state) const override { for (size_t i = 0; i < fields.size(); ++i) { if (i > 0) os << " "; os << fields[i].name << " : " << fields[i].value; // 按照示例,第一个字段后加句号 if (i == 0) os << "."; } } };
最后实现Formatter类,负责状态管理和策略选择:
class Formatter { private: std::unique_ptr<FormatStrategy> strategy_; // 当前使用的格式化策略 Mode mode_; State state_; std::vector<DataField> fields_; // 存储所有要打印的键值对 // 根据当前mode和state自动选择对应的策略(这里可以自定义映射逻辑) void selectStrategy() { // 示例映射规则:你可以根据实际需求修改 if (mode_ == Mode::mode1) { strategy_ = std::make_unique<KeyValuePairStrategy>(); } else if (mode_ == Mode::mode2 && state_ == State::state1) { strategy_ = std::make_unique<ValueOnlyStrategy>(); } else if (mode_ == Mode::mode2 && state_ == State::state2) { strategy_ = std::make_unique<ColonKeyValueStrategy>(); } else { // 默认策略,防止未覆盖的状态组合 strategy_ = std::make_unique<KeyValuePairStrategy>(); } } public: // 构造函数:初始化模式、状态和数据字段 Formatter(Mode mode, State state, std::vector<DataField> fields) : mode_(mode), state_(state), fields_(std::move(fields)) { selectStrategy(); } // 动态修改模式,自动切换策略 void setMode(Mode mode) { mode_ = mode; selectStrategy(); } // 动态修改状态,自动切换策略 void setState(State state) { state_ = state; selectStrategy(); } // 添加新的键值对 void addField(const DataField& field) { fields_.push_back(field); } // 重载ostream运算符,支持直接输出Formatter对象 friend std::ostream& operator<<(std::ostream& os, const Formatter& formatter) { formatter.strategy_->format(os, formatter.fields_, formatter.mode_, formatter.state_); return os; } // 快捷打印方法,直接输出到cout void print() const { std::cout << *this << std::endl; } };
使用示例
int main() { // 准备测试数据 std::vector<DataField> testFields = { {"name1", "value1"}, {"name2", "value2"}, {"name3", "value3"} }; // 测试mode1 + state1 Formatter fmt(Mode::mode1, State::state1, testFields); std::cout << "Mode1 + State1 输出:" << fmt << "\n\n"; // 切换到mode2 + state1 fmt.setMode(Mode::mode2); fmt.setState(State::state1); std::cout << "Mode2 + State1 输出:" << fmt << "\n\n"; // 切换到mode2 + state2 fmt.setState(State::state2); std::cout << "Mode2 + State2 输出:" << fmt << "\n"; return 0; }
设计优势与扩展建议
- 扩展性极强:以后要加新格式?只需要新增一个
FormatStrategy的子类,修改selectStrategy里的映射规则即可,完全不用改动原有代码(符合开闭原则) - 职责清晰:策略类只负责格式生成,
Formatter只负责状态和数据管理,逻辑不混乱 - 灵活性高:可以动态切换
Mode和State,自动适配格式;也可以随时添加新的键值对 - 可选优化:可以把策略对象做成单例,避免重复创建;或者允许外部传入自定义策略,进一步提升灵活性
内容的提问来源于stack exchange,提问作者Adrian
相关产品推荐
相关产品推荐

