
本文介绍如何利用工厂方法模式,让调用方仅面向抽象类 PdfRenderer 编程,由内部逻辑根据运行时参数自动选择并创建具体渲染器(如 ReportlabRenderer),彻底解耦客户端代码与具体实现。
本文介绍如何利用工厂方法模式,让调用方仅面向抽象类 pdfrenderer 编程,由内部逻辑根据运行时参数自动选择并创建具体渲染器(如 reportlabrenderer),彻底解耦客户端代码与具体实现。
要实现 PdfRenderer(params).render(temp_file) 这种简洁调用,关键在于将实例化逻辑封装进抽象类自身,而非暴露具体子类。Python 中最自然、可扩展的方案是采用 工厂方法模式(Factory Method Pattern) —— 通过静态或类方法,在抽象基类中定义“如何根据参数创建具体实例”的规则。
以下是推荐的完整实现:
✅ 正确实现:在抽象类中内置工厂方法
from abc import ABC, abstractmethod
from typing import Union, IO, Type, Optional, Any
class CertificateData:
pass # 假设已定义
class PdfRenderer(ABC):
# 工厂方法:根据参数返回具体渲染器实例
@classmethod
def create(cls,
data: CertificateData,
image_path: str,
hash_value: str,
engine: str = "reportlab") -> 'PdfRenderer':
"""
根据 engine 参数动态选择并实例化具体渲染器。
支持未来扩展其他引擎(如 weasyprint、pdfkit 等)。
"""
if engine == "reportlab":
return ReportlabRenderer(data, image_path, hash_value)
elif engine == "weasyprint":
return WeasyPrintRenderer(data, image_path, hash_value)
else:
raise ValueError(f"Unsupported PDF rendering engine: {engine}")
@abstractmethod
def render(self, filename: Union[str, IO[bytes]]) -> None:
pass
# 具体实现类(保持原有结构)
class ReportlabRenderer(PdfRenderer):
def __init__(self, data: CertificateData, image_path: str, hash_value: str):
self.data = data
self.image_path = image_path
self.hash = hash_value
def render(self, filename: Union[str, IO[bytes]]) -> None:
print(f"[Reportlab] Rendering to {filename} with hash {self.hash}")
# 可选:新增其他实现(无需修改调用方)
class WeasyPrintRenderer(PdfRenderer):
def __init__(self, data: CertificateData, image_path: str, hash_value: str):
self.data = data
self.image_path = image_path
self.hash = hash_value
def render(self, filename: Union[str, IO[bytes]]) -> None:
print(f"[WeasyPrint] Rendering to {filename} with hash {self.hash}")✅ 调用方式:完全符合预期,零耦合
def render_and_send_pdf(self):
# ✅ 完全隐藏实现细节:只依赖抽象类 + 工厂参数
renderer = PdfRenderer.create(
data=self.certificate_data,
image_path=self.image_path,
hash_value=self.request.id,
engine="reportlab" # 或动态配置:config.get("pdf_engine")
)
renderer.render(temp_file)⚠️ 注意事项与最佳实践
-
避免
__new__或__init__直接做工厂逻辑:抽象类不可实例化,重写__new__易引发TypeError且破坏语义;工厂应显式命名(如create),提升可读性与可测试性。 -
参数设计建议:将决策依据(如
engine)作为显式参数传入,而非从全局/配置隐式读取——更利于单元测试与调试。 -
扩展性保障:新增渲染器时,只需继承
PdfRenderer并更新create()方法的分支,调用方代码完全无需修改。 -
类型提示友好:使用
Type[PdfRenderer]和Union可确保 IDE 和 mypy 正确推导返回类型(需配合from __future__ import annotations在 Python -
进阶可选:若需更灵活策略(如基于环境变量、配置文件或运行时条件),可将工厂方法改为委托给独立的
RendererFactory类,进一步解耦。
通过该设计,你既保留了面向接口编程的优雅性,又实现了运行时多态实例化——这才是抽象类在真实工程中“活起来”的正确姿势。

















