如何将此过程代码设计为基于 class(面向对象)?

How do I design this procedural code as class based (object oriented)?

我是一名初中级自学 Python 开发人员,

在我完成的大部分项目中,我都可以看到以下过程在重复。我没有任何外部代码经验,我认为下面的代码不是很专业,因为它不可重用,而且看起来它不能完全放在一个容器中,而是在不同模块上松散耦合的功能。

def get_query():
    # returns the query string
    pass

def make_request(query):
    # makes and returns the request with query
    pass

def make_api_call(request):
    # calls the api and returns response
    pass

def process_response(response):
    # process the response and returns the details
    pass

def populate_database(details):
    # populates the database with the details and returns the status of population
    pass

def log_status(status):
    # logs the status so that developer knows whats happening
    pass

query = get_query()
request = make_request(query)
response = make_api_call(request)
details = process_response(response)
status = populate_database(details)
log_status(status)

如何将此程序设计为基于 class 的设计?

这里可能有多个 class。仅举几例,QueryRequestResponseDatabaseLogger

您的部分功能可能映射如下:

  1. make_query -> Query.make() 构造函数或 class 方法
  2. make_request -> Request.make(查询)构造函数或class方法
  3. make_api_call -> Request.make_api_call()
  4. process_response -> Response.process()
  5. populate_database -> Database.populate()
  6. log_status -> Logger.status 考虑使用日志模块

您必须考虑您的应用程序并将其设计为协作对象的交互。这只是一个起点,以便您在 class 之间划分应用程序的功能。

其中一些 类 可能是单例,这意味着它们仅在应用程序开始时实例化一次并在其他任何地方访问。 DatabaseLogger 适合这个角色。

这里是一些骨架定义:

class Query(object):
    @classmethod
    def make(cls, *args, **kwargs):
        pass

class Request(object):
    @classmethod
    def make(cls, query):
        pass

    def make_api_call(self, *args, **kwargs):
        # possibly return Response
        pass

class Response(object):
    def process_response(self):
        pass

class Database(object):
    _the_db = None

    @classmethod
    def get_db(cls):
        # Simple man's singleton
        if not cls._the_db:
            cls._the_db = Database()
        return cls._the_db

    def populate(self):
        pass

class Logger(object):
    def log(self):   
        # consider using logging module
        pass

如果我没理解错的话,你希望这组函数被重用。好的方法是使用如下所示的这些方法创建抽象基础 class:

from abc import ABCMeta

class Generic(object):
__metaclass__  = ABCMeta


    def get_query(self):
        # returns the query string
        pass

    def make_request(self, query):
        # makes and returns the request with query
        pass

    def make_api_call(self, request):
        # calls the api and returns response
        pass

    def process_response(self, response):
        # process the response and returns the details
        pass

    def populate_database(self, details):
        # populates the database with the details and returns the status of population
        pass

    def log_status(self, status):
        # logs the status so that developer knows whats happening
        pass

现在,无论何时您需要在项目中使用这些方法中的任何一种,都可以从此摘要 class 中继承您的 class。

class SampleUsage(Generic):

    def process_data(self):
        # In any of your methods you can call these generic functions
        self.get_query()

然后您可以创建对象以实际获得您想要的结果。

obj = SampleUsage()
obj.process_data()

您还可以使用 class 名称保存一个 Python 文件,或者您可以创建具有某些功能的外部模块,根据它们的作用将它们组织到模块中。有些模块只包含一个功能;其他人将包含 lot.

我认为你的问题缺少的是目标感。您不会无缘无故地将完美的过程代码转换为面向对象的代码。根据原因,有几种方法可以做到这一点。由于这个问题很常见,因此有一些已知的常用技术由于某些常见原因可以很好地工作。

那么,假设您将主要过程封装在一个对象中。你有什么需求?

  • 允许重新使用该过程,可能会覆盖某些部分?请参阅下面的模板方法模式。
  • 允许根据外部因素在运行时动态改变过程的行为?查看 Strategy 模式。
  • 允许根据内部因素在运行时动态改变过程的行为?例如,如果某个请求可能会将程序切换为"maintenance mode"?查看 State 模式。

我将只描述模板方法模式,它看起来最接近 Marty 的关注点。 我将示例缩减为 3 个步骤,这样更容易解释,但我给你做了一个 fully working example gist

template method

您想提供一种方法来重用该过程,同时允许覆盖一些明确定义的部分吗?让我们创建一个空白的填空样式模板:

class BaseRequestProcesor(object):
    def get_query(self):
        raise NotImplementedError()

    def process_query(self, query):
        raise NotImplementedError()

    def log_status(self, status):
        raise NotImplementedError()

    def process(self): # main procedure
        query = self.get_query()
        status = self.process_query(query)
        self.log_status(status)

    __call__ = process # allow "calling" the requestprocessor

我们有了基本模板。让我们创建一些模板填充器:

class DemoQueryReader(object):
    def get_query(self):
        return 'this is a query'

class HelloQueryProcessor(object):
    def process_query(self, query):
        return 'Hello World, {}!'.format(query)

class StdoutLogProcessor(object):
    def log_status(self, status):
        print(status)

现在从我们想要的位构建一个完整的请求处理器。这是各个部分组合在一起的地方:

class DemonstrationProcessor(DemonQueryReader, HelloQueryProcessor, StdoutLogProcessor, BaseRequestProcessor):
    pass

正在控制台中演示:

>>> from marty_example import DemonstrationProcessor
>>> processor = DemonstrationProcessor()
>>> processor()
Hello World, this is a query!

这是您可以构建的最迂腐的示例。您可以在有意义的时候提供默认实现(大多数情况下什么都不做)。如果有意义,您可以将覆盖组合在一起。

重点是,您将流程设为模板,允许轻松覆盖所选细节,同时仍然控制整个工作流程。这是 inversion of control.

的一种形式