用组合替代深层继承:构建灵活可扩展的 Python 绘图类体系

秋丽姑娘_7631

秋丽姑娘_7631

2026-09-06

803人浏览

原创

用组合替代深层继承:构建灵活可扩展的 Python 绘图类体系

本文介绍如何通过策略模式与组合(composition)重构过度依赖抽象类继承的绘图系统,避免爆炸式增长的抽象基类,提升代码可维护性、复用性与可测试性。

本文介绍如何通过策略模式与组合(composition)重构过度依赖抽象类继承的绘图系统,避免爆炸式增长的抽象基类,提升代码可维护性、复用性与可测试性。

在面向对象设计中,当面对“为每种数据维度+图表类型组合都定义一个抽象类”(如 AbstractMonthlyContourPlot、AbstractSeasonalLinearPlot)时,往往已落入继承滥用的陷阱。你直觉上感到“越来越深的继承链让代码更复杂”,这非常准确——它违背了开闭原则(难以新增图表类型而不修改基类),也违反了单一职责原则(一个类被迫承载“时间结构逻辑”和“渲染逻辑”双重责任)。

上述示例中,原始设计将绘图行为(contourf vs plot)与布局结构(按月排列子图)耦合在同一个继承链中,导致类爆炸风险。而重构后的方案采用组合优于继承(Composition over Inheritance)这一核心 Pythonic 原则,将关注点清晰分离:

  • Plotter 策略接口:封装如何绘制单个子图,每个具体实现(ContourPlotter、LinearPlotter)只专注一种可视化逻辑;
  • MonthlyPlot 容器类:封装如何组织子图布局(如 12 个月、共享坐标轴等),不关心具体绘图细节,仅依赖 Plotter 抽象协议;
  • 运行时组合:通过构造函数注入具体 Plotter 实例,实现行为的动态装配。

这种设计带来显著优势:

Python Testing
Python Testing

Python 测试速查:运行 pytest、使用 mock/patch、参数化、fixtures、异步、覆盖率测试。

下载

✅ 高内聚低耦合:ContourPlotter 不再需要知道“月份”概念;MonthlyPlot 也不需了解 contourf 的 API;
✅ 无限可扩展:新增 HeatmapPlotter 或 BarPlotter 只需实现 Plotter 接口,无需修改任何现有类;
✅ 易于测试:可单独单元测试 ContourPlotter.plot(),也可用 mock Plotter 测试 MonthlyPlot.plot() 的布局逻辑;
✅ 符合 Python 哲学:“显式优于隐式”——行为由传入对象决定,而非继承关系推导。

以下是关键代码片段(含类型提示与健壮性增强):

from abc import ABC, abstractmethod
from numpy import ndarray
from typing import List, Tuple, Protocol
import matplotlib.pyplot as plt

# 1. 定义绘图策略协议(Protocol 更灵活,非强制继承)
class Plotter(Protocol):
    def plot(self, ax: plt.Axes, data: Tuple[ndarray, ...]) -> None: ...

# 2. 具体策略实现(无继承,纯函数式职责)
class ContourPlotter:
    def plot(self, ax: plt.Axes, data: Tuple[ndarray, ...]) -> None:
        ax.contourf(*data)

class LinearPlotter:
    def plot(self, ax: plt.Axes, data: Tuple[ndarray, ...]) -> None:
        ax.plot(*data)

# 3. 组合容器:专注结构逻辑,依赖协议而非具体类
class MonthlyPlot:
    def __init__(self, plotter: Plotter, n_months: int = 12):
        self.plotter = plotter
        self.n_months = n_months  # 支持自定义周期(如季度=4)

    def plot(self, month_to_data: List[Tuple[ndarray, ...]]) -> plt.Figure:
        if len(month_to_data) != self.n_months:
            raise ValueError(f"Expected {self.n_months} months of data, got {len(month_to_data)}")

        fig, axs = plt.subplots(self.n_months, 1, figsize=(8, 2 * self.n_months))
        # 处理单子图时 axs 不是数组的情况
        if self.n_months == 1:
            axs = [axs]

        for month, data in enumerate(month_to_data):
            self.plotter.plot(ax=axs[month], data=data)
        return fig

# 使用示例:灵活组合,语义清晰
monthly_contour = MonthlyPlot(ContourPlotter(), n_months=12)
fig = monthly_contour.plot(month_to_data)

# 同一布局逻辑,切换渲染策略仅需换一个对象
monthly_bar = MonthlyPlot(BarPlotter(), n_months=4)  # 季度柱状图

注意事项与进阶建议:
⚠️ 避免过度设计:若仅需 2–3 种图表且无复用需求,直接函数式编程(如 def plot_monthly_contour(...))可能更 Pythonic;
⚠️ 考虑使用 functools.singledispatch 或 @dataclass + __call__ 实现轻量策略;
? 进一步解耦:可将 month_to_data 结构抽象为 DataSource 协议,使 MonthlyPlot 完全脱离数据格式假设;
? 生产级增强:添加 plt.style.context 支持、savefig 封装、异常统一处理等。

总结而言,当你发现抽象类名称开始包含多个形容词(AbstractSeasonalMultiPanelContour),就是该按下暂停键、审视是否该用组合来“搭积木”,而非用继承去“建高楼”——这才是 Python 社区推崇的清晰、灵活、可持续的面向对象实践。

Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

python

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

2023.07.20

1571

4

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

2023.07.25

3764

7

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.07.31

1589

3

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

2023.08.03

21557

23

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.04

2667

5

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.04

2707

5

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.11

1103

5

python合并两个列表
python合并两个列表

Python是一种强大的编程语言,具有许多方便的功能和工具。在Python中,有多种方法可以合并两个列表。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.10

576

4

python是前端还是后端
python是前端还是后端

Python属于前端也属于后端,其灵活性和丰富的生态系统使得开发人员能够在不同的领域中灵活运用。本专题为大家提供python相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.11

2103

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程