基于依赖注入的C#文本编辑器代码重构咨询
问题1:带必填构造参数的DocumentObject生成方案
优先采用工厂模式结合依赖注入的实现,既集中管理解析规则,又能灵活处理必填参数与外部依赖:
定义基础结构
- 先明确
DocumentObject基类,仅保留数据属性(如匹配到的文本、位置信息),不包含创建逻辑:from abc import ABC import re class DocumentObject(ABC): def __init__(self, match: re.Match): self.match = match self.raw_content = match.group(0) self.start_pos = match.start() self.end_pos = match.end() - 各子类继承基类,根据自身需求添加属性,构造函数强制传入
match:class VariableDO(DocumentObject): def __init__(self, match: re.Match): super().__init__(match) self.var_name = match.group(1) class ScriptDO(DocumentObject): def __init__(self, match: re.Match, script_executor): super().__init__(match) self.script_code = match.group(1) self.script_executor = script_executor # 外部依赖通过DI注入 class DbCallDO(DocumentObject): def __init__(self, match: re.Match, db_connection_pool): super().__init__(match) self.query = match.group(1) self.db_pool = db_connection_pool
- 先明确
实现工厂类
- 工厂类通过依赖注入获取外部服务(如脚本执行器、数据库连接池),同时维护正则表达式→对象创建逻辑的映射表:
class DocumentObjectFactory: def __init__(self, script_executor=None, db_connection_pool=None): self.script_executor = script_executor self.db_pool = db_connection_pool # 定义匹配规则与创建函数的映射 self.type_mappings = [ (re.compile(r"\${(\w+)}"), self._create_variable), (re.compile(r"```python(.*?)```", re.DOTALL), self._create_script), (re.compile(r"{{db:(.*?)}}"), self._create_db_call), ] def _create_variable(self, match: re.Match): return VariableDO(match) def _create_script(self, match: re.Match): return ScriptDO(match, self.script_executor) def _create_db_call(self, match: re.Match): return DbCallDO(match, self.db_pool) def parse(self, text: str) -> list[DocumentObject]: """解析文本生成所有DocumentObject""" objects = [] for pattern, creator in self.type_mappings: for match in pattern.finditer(text): objects.append(creator(match)) # 按位置排序,避免替换时顺序混乱 return sorted(objects, key=lambda x: x.start_pos)
- 工厂类通过依赖注入获取外部服务(如脚本执行器、数据库连接池),同时维护正则表达式→对象创建逻辑的映射表:
优势说明
- 集中管理所有解析规则,新增DocumentObject类型只需在映射表中添加一行,符合开闭原则;
- 必填参数
match直接传递,外部依赖(如脚本执行器)通过构造注入,避免硬编码; - 工厂类承担创建逻辑,DocumentObject子类仅负责数据存储,职责单一。
问题2:文档替换逻辑的实现位置
优先选择服务/处理器类实现执行逻辑,而非在DocumentObject中添加Execute方法,核心原因是贴合单一职责原则与长期维护需求:
处理器模式的实现
- 定义处理器接口,统一执行入口:
from abc import ABC, abstractmethod class IDocumentProcessor(ABC): @abstractmethod def execute(self, doc_obj: DocumentObject, original_text: str) -> str: """执行替换逻辑,返回替换后的文本片段或完整内容""" pass - 为每种DocumentObject实现对应的处理器,注入所需外部服务:
class VariableProcessor(IDocumentProcessor): def __init__(self, variable_store): self.variable_store = variable_store def execute(self, doc_obj: VariableDO, original_text: str) -> str: # 变量替换逻辑 return original_text[:doc_obj.start_pos] + self.variable_store.get(doc_obj.var_name) + original_text[doc_obj.end_pos:] class ScriptProcessor(IDocumentProcessor): def __init__(self, script_executor): self.script_executor = script_executor def execute(self, doc_obj: ScriptDO, original_text: str) -> str: # 脚本执行并替换逻辑 script_result = self.script_executor.run(doc_obj.script_code) return original_text[:doc_obj.start_pos] + str(script_result) + original_text[doc_obj.end_pos:] class DbCallProcessor(IDocumentProcessor): def __init__(self, db_connection_pool): self.db_pool = db_connection_pool def execute(self, doc_obj: DbCallDO, original_text: str) -> str: # 数据库查询并替换逻辑 with self.db_pool.get_connection() as conn: result = conn.execute(doc_obj.query).fetchall() return original_text[:doc_obj.start_pos] + str(result) + original_text[doc_obj.end_pos:] - 再实现一个调度器,根据DocumentObject类型匹配对应的处理器:
class DocumentExecutionScheduler: def __init__(self): self.processors = { VariableDO: VariableProcessor(variable_store), ScriptDO: ScriptProcessor(script_executor), DbCallDO: DbCallProcessor(db_pool) } def execute_all(self, doc_objects: list[DocumentObject], original_text: str) -> str: current_text = original_text # 按逆序执行(避免替换后位置偏移) for doc_obj in reversed(sorted(doc_objects, key=lambda x: x.start_pos)): processor = self.processors[type(doc_obj)] current_text = processor.execute(doc_obj, current_text) return current_text
- 定义处理器接口,统一执行入口:
为什么不选DocumentObject内置Execute
- 违反单一职责:DocumentObject本应是数据载体,内置执行逻辑会让它同时承担数据存储与业务处理职责,代码耦合度高;
- 依赖管理困难:执行逻辑往往依赖外部服务(如数据库、脚本引擎),放在DocumentObject中会导致这些依赖被硬编码到实体类,难以测试和替换;
- 扩展性差:新增DocumentObject类型时,需要修改基类或子类,而处理器模式只需新增处理器类,无需改动原有实体代码。
特殊情况的折中
- 如果某些DocumentObject的执行逻辑极其简单(如静态文本替换),可以在基类中添加一个默认的
execute方法作为兜底,但仍建议核心逻辑用处理器实现,保持一致性。
- 如果某些DocumentObject的执行逻辑极其简单(如静态文本替换),可以在基类中添加一个默认的
内容的提问来源于stack exchange,提问作者iomismo
相关产品推荐
相关产品推荐

