You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

InstanceManager:继承与组合的技术选型探讨

选择继承还是组合使用InstanceManager?

我已实现如下InstanceManager类,用于管理所有创建的实例,并支持通过指定查找键快速查找和检索特定值:

from typing import Callable, Iterable, Hashable, Optional as Opt
import threading

class InstanceManager[InstanceT, LookupT: Hashable]():
    def __init__(
        self, 
        get_lookups: Callable[[InstanceT], Iterable[LookupT]],
        on_add: Opt[Callable[[InstanceT], None]] = None,
        on_remove: Opt[Callable[[InstanceT], None]] = None
    ) -> None:
        self._on_add = on_add
        self._on_remove = on_remove
        self._get_lookups = get_lookups
        self._all: list[InstanceT] = []
        self._lookup: dict[LookupT, InstanceT] = {}
        self._LOCK = threading.RLock()
    
    def lookup(self, lookup: LookupT) -> InstanceT:
        return self._lookup[lookup]  # atomic operation
    
    def all_instances(self) -> list[InstanceT]:
        with self._LOCK:
            return list(self._all)
    
    def get_lookups(self, instance: InstanceT) -> tuple[LookupT, ...]:
        with self._LOCK:
            return tuple(k for k, v in self._lookup.items() if v == instance)
    
    def add(self, instance: InstanceT) -> None:
        with self._LOCK:
            lookup_values = self._get_lookups(instance)
            
            if instance in self._all:
                raise ValueError("Instance already in added")
            
            self._all.append(instance)

            for lv in lookup_values:
                if lv in self._lookup:
                    raise ValueError("lookup value already used for different instance")
                
                self._lookup[lv] = instance
        
        if self._on_add is not None:
            self._on_add(instance)  # 修正原代码漏传参问题
   
    def remove(self, instance: InstanceT):
        with self._LOCK:
            # 修正原代码遍历字典时pop报错问题:遍历键的副本
            for k in list(self._lookup.keys()):
                if self._lookup[k] == instance:
                    self._lookup.pop(k)
            
            self._all.remove(instance)

        if self._on_remove is not None:
            self._on_remove(instance)  # 修正原代码漏传参问题

现咨询:使用该功能的类应选择继承InstanceManager,还是将其作为类字段采用组合方式?我虽知晓继承是‘is a’关系、组合是‘has a’关系,但仍无法据此做出判断;且多数目标类需继承其他类,采用继承会引发多重继承问题,希望得到专业建议。


专业建议:优先选择组合方式

1. 规避多重继承的复杂度

多数目标类已有自身的父类,Python的多重继承虽然语法支持,但会带来菱形继承、方法解析顺序(MRO)冲突等问题,大幅提升代码的维护难度。组合方式完全避开这些陷阱,目标类只需维护一个InstanceManager类型的字段,逻辑更清晰。

2. 符合语义逻辑

从业务语义看,你的目标类不是一个InstanceManager,而是拥有实例管理的能力。比如一个OrderService,它需要管理订单实例,但OrderService的核心职责是处理订单业务,而非单纯做实例管理,这完全符合"has a"的组合语义,而非"is a"的继承语义。

3. 灵活性更强

  • 组合方式允许你随时替换实例管理的实现(比如后续扩展出PersistentInstanceManager),无需修改目标类的继承结构。
  • 可以在目标类中封装InstanceManager的方法,对外暴露符合业务场景的接口,避免暴露冗余的底层方法。比如目标类可以提供get_order_by_id,而非直接暴露InstanceManager的lookup方法,更贴合单一职责原则。

4. 继承仅适用于极特殊场景

只有当你的目标类本身就是InstanceManager的特化扩展时,比如OrderInstanceManager(专门管理订单实例的管理器),继承才有意义。但根据你的描述,目标类是需要使用实例管理功能的业务类,而非管理器本身,因此继承并不适用。

组合实现示例

class Order:
    def __init__(self, order_id: str, amount: float):
        self.order_id = order_id
        self.amount = amount

class OrderService:
    def __init__(self):
        # 组合InstanceManager,指定查找键为order_id
        self._instance_manager = InstanceManager[Order, str](
            get_lookups=lambda order: [order.order_id],
            on_add=lambda order: print(f"订单 {order.order_id} 已添加"),
            on_remove=lambda order: print(f"订单 {order.order_id} 已移除")
        )

    def create_order(self, order: Order):
        self._instance_manager.add(order)

    def get_order(self, order_id: str) -> Order:
        return self._instance_manager.lookup(order_id)

    def get_all_orders(self) -> list[Order]:
        return self._instance_manager.all_instances()

    def cancel_order(self, order: Order):
        self._instance_manager.remove(order)

内容的提问来源于stack exchange,提问作者JoniKauf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 03:22:02