为遵循依赖倒置原则,先实例化依赖再后续更新其值是否合理?
依赖倒置原则(DIP)适配方案合理性分析
场景与初始实现
假设我们有一个Database类,其get_image方法负责返回Image对象。为遵循依赖倒置原则(DIP),设计了一系列接口,初始代码实现如下:
import abc class ImageInterface(abc.ABC): """表示并操作图像""" class ImageParserInterface(abc.ABC): """图像解析器""" @abc.abstractmethod def decode(self, data: str) -> ImageInterface: """将字符串解析为Image对象""" class DatabaseInterface(abc.ABC): """与数据库交互""" @abc.abstractmethod def get_image(self, image_id: str) -> ImageInterface: """根据ID获取数据库中存储的图像""" class ImageRGB(ImageInterface): """表示并操作RGB图像""" class ImageParserMongoDBToRGB(ImageParserInterface): """将MongoDB查询结果转换为ImageRGB对象""" def decode(self, data: str) -> ImageInterface: """将字符串解析为Image对象""" return ImageRGB() class DatabaseMongoDB(DatabaseInterface): """与MongoDB数据库交互""" def __init__(self, image_parser: ImageParserInterface): self._image_parser = image_parser @abc.abstractmethod def fetch_image_data(self, image_id: str) -> str: ... def get_image(self, image_id: str) -> ImageInterface: image_data = self.fetch_image_data(image_id) return self._image_parser.decode(image_data)
依赖倒置原则(DIP)定义
DIP包含以下核心规则:
- A1:高层模块不应依赖低层模块的具体实现
- A2:高层模块与低层模块都应依赖抽象(如接口)
- B1:抽象不应依赖细节
- B2:细节(具体实现)应依赖抽象
初始代码的DIP合规性问题
初始实现符合A1和B1:所有接口仅依赖其他接口,没有耦合具体实现。但不符合A2和B2,因为ImageParserMongoDBToRGB直接依赖了具体类ImageRGB,而非抽象的ImageInterface。
修改方案的合理性分析
为满足DIP,有人提出如下修改方案,通过传入空的ImageRGB实例初始化ImageParserMongoDBToRGB:
class ImageParserMongoDBToRGB(ImageParserInterface): """将MongoDB查询结果转换为ImageRGB对象""" def __init__(self, image: ImageInterface): self.image = image def decode(self, data: str) -> ImageInterface: """将字符串解析为Image对象""" self.image.data = data return self.image
合理性判断
这种方案部分符合DIP要求,但存在明显设计缺陷:
- 符合DIP的部分:
ImageParserMongoDBToRGB现在依赖抽象的ImageInterface而非具体的ImageRGB,满足了A2和B2的要求,实现了解耦——后续如果需要替换为其他图像类型(如ImageCMYK),只需传入对应实现类的实例即可,无需修改解析器代码。 - 存在的缺陷:
- 状态复用问题:如果同一个
ImageParserMongoDBToRGB实例被多次调用decode方法,会复用同一个image实例,导致前一次解析的图像数据被覆盖,引发状态污染。 - 违背单一职责:解析器的核心职责应该是创建/解析出图像对象,而非修改外部传入的对象状态。这种设计把对象的创建职责转移到了调用方,增加了调用方的复杂度。
- 状态复用问题:如果同一个
更优的替代方案
如果要严格遵循DIP同时避免上述问题,可以通过依赖注入工厂类的方式实现:
class ImageFactoryInterface(abc.ABC): @abc.abstractmethod def create_image(self) -> ImageInterface: """创建Image实例""" class ImageRGBFactory(ImageFactoryInterface): def create_image(self) -> ImageInterface: return ImageRGB() class ImageParserMongoDBToRGB(ImageParserInterface): def __init__(self, image_factory: ImageFactoryInterface): self._image_factory = image_factory def decode(self, data: str) -> ImageInterface: image = self._image_factory.create_image() image.data = data return image
这种方案中:
ImageParserMongoDBToRGB依赖抽象的ImageFactoryInterface,完全满足DIP要求- 每次调用
decode都会创建新的图像实例,避免状态污染 - 图像对象的创建职责由工厂类承担,符合单一职责原则
内容的提问来源于stack exchange,提问作者amarion
相关产品推荐
相关产品推荐

