如何维持具有多个依赖性的SRP(单个责任原则)



我在班级必须取决于其他因素的情况下感到困惑。

例如

class Storage:
    def __init__(self):
        self.logger = Logger()
        self.client = Elasticsearch()
    def index(document):
        try:
            self.client.index(document)
         except ElasticsearchException as e:
             self.logger.error(str(e))

在这里,我的班级必须有记录器和Elasticsearch对象才能执行其操作。在这种情况下,我该如何维护SRP,在这种情况下可能需要更改我的班级,例如:

  1. 我切换到不同的数据库
  2. 我切换到不同的记录库

有人可能会说,我不应该让客户端类处理异常。但是,在客户端仅 yield ing 的情况下,要插入的文档并接受了失败的索引操作,客户类类并不麻烦错误。另外,即使我重新介绍了客户端类的例外,那里也会发生相同的SRP问题。

我会在我的上下文中得到解释性答案。

我认为问题的一部分在标题中:" ..具有多个依赖关系"。您的依赖项是高度耦合的,因为在存储课内安装。这就是为什么我会使用依赖注入(我有0个python知识,可能是错字):

interface StorageClientInterface:
    def index(document)    
interface LoggerInterface:
    def error(Exception e) 
class ElasticSearchStorage implements storageClientInterface:
    def index(document):
        // implements ElasticSearch specific storage logic
class MyDefaultLogger implements LoggerInterface:
    def error(Exception e):
        // implements MyDefaultLogger specific logging logic, totally agnostic from ElascticSearch
class Storage:
    def __init__(self, StorageClientInterface storageClient, LoggerInterface logger):        
        self.client = storageClient
        self.logger = logger
    def index(document):
        try:
            self.client.index(document)
         except Exception as e:
             self.logger.error(e)

// usage
elasticSearch = ElasticSearch()
logger = MyDefaultLogger()
document = Document();
storage = Storage(elasticSearch, logger)    
storage.index(document)

以这种方式,您的存储类不与您的存储策略或记录策略耦合。它只是知道它可以使用这两个接口。如果更改数据库,只要此新的存储策略实现您的StorageClientInterface,就不必更改存储类中的任何内容。如果您更改记录错误的方式,也是如此。只需安装一个新的混凝土记录仪,然后注入它。

您可以通过引入其他层来将抽象API定义为此功能:一个用于数据库,另一个用于进行日志记录。执行此操作后,您的Storage类必须仅限于使用它们,而不是直接调用或任何"真实"方法。

这样,除非必须出于某种原因更改一个抽象接口(如果设计良好,则不需要),除非必须更改一个抽象接口,否则他们(像重写Storage类一样)不需要更改。这两个抽象接口中的任何一个的具体实现都只有一个责任(即,通过某些特定日志或数据库库中可用的内容仿真API)。

您可以做到这一点的一种方法是使用装饰器图案。装饰器的图案将使您可以将所有错误处理逻辑封装在单独的类中,然后可以用来将其用于包装装饰类的行为,在这种情况下,这将是您的存储类。我不太了解Python,所以请原谅任何语法错误,但看起来像这样:

class Storage:
    def __init__(self, storageClient):        
        self.client = storageClient
    def index(document):
        self.client.index(document)

class ElasticSearchExceptionPolicy:
    def __init__(self, decorated, logger):
        self.logger = logger
        self.decorated = decorated
    def index(document):
        try:
            decorated.index(document)
        except ElasticsearchException as e:
            self.logger.error(str(e))

然后可以像这样使用这些对象:

elasticSearch = ElasticSearch()
logger = Logger()
storage = Storage(elasticSearch)    
loggedStorage = ElasticSearchExceptionPolicy(storage, logger)
loggedStorage.index(document)

,如果您想遵循开放/封闭的原则,您也可能希望将Elasticsearch对象和Logger对象传递到各自的类中。

最新更新