乐于分享
好东西不私藏

组合模式 | 文档树结构

组合模式 | 文档树结构

"""任务:实现组合模式表示文档层级结构场景:文档可能包含章节、段落、句子等嵌套结构"""class DocumentComponent(ABC):    """文档组件基类"""    @abstractmethod    def get_text(self):        pass    @abstractmethod    def get_word_count(self):        passclass Sentence(DocumentComponent):    """句子(叶子节点)"""    def __init__(self, text):        self.text = text    def get_text(self):        return self.text    def get_word_count(self):        return len(self.text.split())class Paragraph(DocumentComponent):    """段落(组合节点)"""    def __init__(self):        self.sentences = []    def add(self, sentence: Sentence):        self.sentences.append(sentence)    def get_text(self):        """返回所有句子拼接"""        return ' '.join(s.get_text() for s in self.sentences)    def get_word_count(self):        """返回所有句子的总字数"""        return sum(s.get_word_count() for s in self.sentences)class Chapter(DocumentComponent):    """章节(组合节点)"""    def __init__(self, title):        self.title = title        self.paragraphs = []    def add(self, paragraph: Paragraph):        self.paragraphs.append(paragraph)    def get_text(self):        para_texts = [p.get_text() for p in self.paragraphs]        return f"# {self.title}\n\n" + '\n\n'.join(para_texts)    def get_word_count(self):        return sum(p.get_word_count() for p in self.paragraphs)# 测试chapter = Chapter("第一章:Python基础")# 第一段para1 = Paragraph()para1.add(Sentence("Python是一种编程语言。"))para1.add(Sentence("它简单易学。"))# 第二段para2 = Paragraph()para2.add(Sentence("Python广泛用于AI开发。"))chapter.add(para1)chapter.add(para2)# 测试text = chapter.get_text()word_count = chapter.get_word_count()print(text)print(f"\n总字数: {word_count}")assert "Python" in textassert word_count > 0print("\n✅ 组合模式测试通过")

一、场景

应用于有层级、树形结构、需要整体 / 单个都能用同一套方法操作的场景,整体和单个能用同样的方式使用,可以嵌套、递归统计 / 遍历 / 渲染,具体场景示例:

  • 文档结构(章节 → 段落 → 句子)

  • 文件目录(文件夹 → 子文件夹 → 文件)

  • 菜单系统(主菜单 → 子菜单 → 菜单项)

  • 组织架构(公司 → 部门 → 员工)

二、个人理解/最初卡在哪里

1、在下面语句加了 return

def add(selfsentence:Sentence):    self.sentences.append(sentence) 

组合模式里的 add 只需要 “添加子节点”,不需要返回任何东西。

list.append () 本身返回的是 None,如果加了return,调用 add 会得到 None,破坏组合模式。

2、中文字符统计

def get_word_count(self):    return len(self.text.split())

split() 是按空格分词,只对英文有效,中文没有空格,所以统计出来字数是1句算1个词,所以运行后总字数为3。例如:

len("Python是一种编程语言。".split())

中文没有空格 → split 切不开 → 长度 = 1

英文:

len("Python is good".split()) → 3 正确

如果想正确统计中文字数(不含标点):

def get_word_count(self):    # 只统计中文/英文/数字    clean_text = re.sub(r'[^\w\u4e00-\u9fa5]'''self.text)    return len(clean_text)
  • \w:单词字符(在 Python 3 默认 Unicode 模式下,包括字母、数字、下划线,以及部分其他语言的字母数字,但不包括中文);

  • \u4e00\u9fa5:Unicode 范围,对应常用汉字(CJK 统一表意文字,基本区,约 20992 个字符);

  • 如果想严格只保留 ASCII 字母数字和下划线 + 汉字,可以设置 re.ASCII 标志:re.sub(r'[^\w\u4e00\u9fa5]’, ”, text, flags=re.ASCII),这样 \w 就只匹配 [a-zA-Z0-9_]。

三、代码中的关键洞察

  • 组合模式 = 树形结构 + 统一接口

  • add 方法只添加,不 return

  • 单个对象和组合对象使用方式完全一样

四、关键代码/可复用片段

① 顶层抽象

class DocumentComponent(ABC):    @abstractmethod    def get_text(self):        pass    @abstractmethod    def get_word_count(self):        pass

② 最小单元:叶子(无 add)

class Sentence(DocumentComponent):    def __init__(selftext: str):        self.text = text    def get_text(self):        return self.text    def get_word_count(self):        return len(self.text.split())

③ 中间容器:只装 Sentence

class Paragraph(DocumentComponent):    def __init__(self):        self.sentences = []    def add(selfsentence:Sentence):        self.sentences.append(sentence)    def get_text(self):        return ' '.join(s.get_text() for s in self.sentences)    def get_word_count(self):        return sum(s.get_word_count() for s in self.sentences)

④ 顶层容器:只装 Paragraph

class Chapter(DocumentComponent):    def __init__(self, title: str):        self.title = title        self.paragraphs = []    def add(self, paragraph: Paragraph):        self.paragraphs.append(paragraph)    def get_text(self):        body = '\n\n'.join(p.get_text() for p in self.paragraphs)        return f"# {self.title}\n\n{body}"    def get_word_count(self):        return sum(p.get_word_count() for p in self.paragraphs)