如何为需要初始化的具体类实现依赖反转和接口分离?

1 投票
1 回答
37 浏览
提问于 2025-04-14 15:54

背景

根据我的理解,SOLID面向对象编程中的依赖倒置原则和接口隔离原则告诉我们,编写程序时要关注接口,而不是内部细节。因此,我正在尝试用Python开发一个简单的股票市场数据收集器,下面是一个大致的对象图,其中main封装了应用程序的业务逻辑、用户输入处理等。我们来看看这个图的意思:

  • 粉色代表具体的功能或类,绿色代表抽象类
  • 空心箭头表示子类或实现关系,实心箭头表示使用关系(遵循Robert Martin的清洁架构的约定)

股票价格读取器的对象图

所以,主函数使用一个抽象接口来获取某个股票符号的价格。这个抽象类看起来像这样:

#!/usr/bin/env python3
# encoding: utf-8

"""
Defines the abstract stock price reader
"""
from abc import ABC, abstractmethod
class StockPriceReader(ABC):
    """Defines the general tick reader interface."""
    @abstractmethod
    def get_price(self, symbol:str)->float:
        """
        Gets the price of a stock represented by the symbol, e.g 
        when symbol='AAPL', it gets the Apple Inc stock price. 
        """
        raise NotImplementedError

TickReaderConcrete类实现了内部细节,通过类似Bloomberg或交易所API的调用来获取实际的股票价格。进行API调用所需的凭证必须是内部细节的一部分。这里不展示代码,因为实现起来相对简单。

困惑

根据上面的简单类依赖图,书中(清洁架构)似乎暗示(我在这里强调):

主块甚至不应该知道存在TickReaderConcrete。

至少,这是我对书中内容的理解,因为从mainTickReaderConcrete之间没有箭头,如果我错了请纠正我。

但是当我编写main.py时,我无法假装TickReaderConcrete不存在,换句话说,似乎main不得不知道TickReaderConcrete的存在,当代码看起来像这样:

#!/usr/bin/env python3
# encoding: utf-8

"""
The main function to invoke the stockprice reader
"""
from tickreader import TickReaderConcrete
...
if __name__ == '__main__':
    # This line gives rise to the alternative class diagram below
    reader=TickReaderConcrete(...) 

    # After initialised, we can use the interface permitted by the abstract base class
    reader.get_price(symbol='IBM')
问题

那么,如何确保main不知道具体读取器的存在?如果main根本不导入具体读取器,它甚至无法实例化具体读取器对象,而抽象读取器也无法初始化。那么,基本上如何组织代码以正确实现上面的对象图呢?

稍微改写的问题

即使抽象基类暴露了必要的公共方法,至少在初始化时也需要知道具体子类的存在。具体子类能否隐藏在抽象基类后面?看看替代对象图,这就是上面代码片段所实现的。如何去掉那条虚线?

在这里输入图像描述

1 个回答

2

我读《清晰架构》已经有些年头了,但我想提出一种不同的理解。依赖倒置原则(DIP)确实是说,抽象不应该依赖于具体的实现,而是应该反过来。

定义一个高层次的接口或抽象基类,然后用一个具体的类来实现它,这个具体类包含所有的细节,这是处理这类问题的标准方法。

DIP的意思是,你的代码几乎都应该是围绕这个高层次的抽象来写的。这就是说,如果你有一些业务逻辑需要调用get_price方法,那么它应该使用一个StockPriceReader的抽象对象来进行交互。

不过,这条规则有一个例外,因为你需要在某个地方组合对象图,通常是在__main__方法里。在这里,你会创建一个具体类的实例,然后把它传递给所有期望抽象类的其他代码。

撰写回答