如何优雅封装多模型类?现有API传递式封装的优化方案咨询
这是个很常见的OOP封装设计问题,我来给你几个实用的方案参考:
方案1:组合模式+动态属性注入(最推荐)
这个方案既能满足你想要的api.Launch(params...)的简洁调用方式,又不会让Api类变得臃肿,同时保持模型类的独立性。
核心思路是:在Api初始化时,动态把独立的模型类和当前API实例绑定,让模型方法自动使用当前API实例,无需手动传递。
示例代码:
class Api: def __init__(self, api_key=None, base_url="https://launchlibrary.net/1.4/"): self.api_key = api_key self.base_url = base_url # 动态绑定需要的模型类,注入当前API实例 self.Launch = self._bind_model(LaunchModel) self.Rocket = self._bind_model(RocketModel) # 其他模型类同理添加 def _bind_model(self, model_cls): # 包装模型类,让它的方法自动使用当前API实例 class BoundModel(model_cls): @classmethod def fetch(cls, **params): # 调用原模型方法时自动传入当前API实例 return super().fetch(api=self, **params) # 保持原模型类的名称,方便调试和使用 BoundModel.__name__ = model_cls.__name__ return BoundModel # 独立的模型类,专注于数据逻辑和API交互 class LaunchModel: @classmethod def fetch(cls, api, **params): # 这里直接使用传入的api实例调用接口 url = f"{api.base_url}launch" # 处理参数、请求逻辑... response = requests.get(url, params={**params, "api_key": api.api_key}) return response.json()
使用方式完全符合你的预期:
import launchlibrary as ll api = ll.Api(api_key="your_key") launches = api.Launch.fetch(limit=10)
这个方案的优势:
Api类不会被大量模型代码塞满,只负责初始化和绑定逻辑- 模型类依然独立,便于单独维护、测试和扩展
- 用户调用体验简洁,符合直觉
方案2:使用模型基类+API实例注入
如果不想用动态绑定,也可以定义一个模型基类,让所有模型继承它,基类自动持有当前API实例的引用。
示例代码:
class BaseModel: _api = None @classmethod def set_api(cls, api): cls._api = api @classmethod def get_api(cls): if not cls._api: raise ValueError("请先初始化Api实例") return cls._api class Api: def __init__(self, api_key=None): self.api_key = api_key # 给所有模型基类设置当前API实例 BaseModel.set_api(self) # 提供快捷访问方式 self.Launch = LaunchModel self.Rocket = RocketModel class LaunchModel(BaseModel): @classmethod def fetch(cls, **params): api = cls.get_api() # 用api实例处理请求... pass
这个方案的好处是结构更清晰,模型类通过基类统一获取API实例,缺点是如果同时使用多个Api实例,会存在全局状态冲突的问题,适合单实例场景。
方案3:使用实例化模型(而非类方法)
如果愿意调整调用方式,也可以让模型先和API实例绑定,再调用方法,这样更加符合OOP的实例化逻辑:
class Api: def __init__(self, api_key=None): self.api_key = api_key def launch(self): return LaunchModel(api=self) class LaunchModel: def __init__(self, api): self.api = api def fetch(self, **params): # 使用self.api处理请求... pass
使用方式:
api = ll.Api(api_key="your_key") launches = api.launch().fetch(limit=10)
这个方案的优势是完全符合OOP的实例依赖注入逻辑,每个模型实例都明确持有对应的API实例,适合复杂场景下的多实例管理。
总结
如果优先考虑用户调用的简洁性和代码结构的清爽,方案1是最优选择;如果项目规模不大、单实例场景居多,方案2足够好用;如果追求严格的OOP实例化逻辑,方案3更合适。
内容的提问来源于stack exchange,提问作者Plutoberth
相关产品推荐
相关产品推荐

