2015-10-01 15 views
3

我是一名初学者 - 中级自学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) 

如何将此过程设计为基于类的设计?

+1

*“它不可重复使用”* - 您为什么这么认为?一些简短的单一用途函数肯定比将所有东西合并成一个函数更加可重用。 *“不同模块上的松散耦合功能”* - 它们似乎在同一个模块中,松耦合通常被认为是一件好事! – jonrsharpe

+0

@jonrsharpe对于遍布某些模块的函数,我总是发现很难追踪模块,复制代码并使其在第二个项目的模块结构上的另一个项目上工作 – Marty

+2

相关的,可重用的功能应该被打包成一个单一模块,但除此之外,还不清楚为什么你认为现在有什么问题。如果你直接将它传递给类,你最终会得到[*“两种方法,其中之一是'__init__'”*](https://www.youtube.com/watch?v=o9pEzgHorH0)。 – jonrsharpe

回答

4

如果我理解正确的话,你想这些功能组被重用。当您需要使用任何这些方法在您的项目,从这个抽象类继承类

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 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() 
+0

用于定义抽象基类的元类 – Vinod

5

你可能在这里有几个类。仅举几例,QueryRequestResponseDatabaseLogger

您的一些功能可能映射如下:

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

你要想想你的应用程序,设计它作为合作对象的互动。这只是一个起点,以便您在类之间划分应用程序的功能。

其中一些类可能是单例,意味着它们在应用程序的开始处仅实例化一次,并在其他地方访问。 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 
2

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

+0

我觉得管理很多模块非常困难,可能是由于命名不佳。 – Marty

+0

当你处理面向对象时,你会发现它很容易。它实际上比在单个模块中完成所有操作更简单更好,因为在导入主模块时会将不必要的功能一起加载。尝试做一个层次结构,如主模块和附加模块。 – Gustavo6046

+0

**哦,更重要的是,它还允许将插件制作为模块,甚至文件夹。 _注意:我还没有理解你的问题。提问时请容易一些;)_ – Gustavo6046

2

我认为你的问题缺乏的是目的感。你不会没有理由地将完美的程序代码切换到面向对象的代码。根据原因,有几种方法可以做到这一点。由于这个问题相当普遍,有一些常见的技术可以在一些常见的原因下正常工作。

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

  • 允许重新使用该过程,可能会覆盖某些部分?请参阅下面的模板方法模式。
  • 允许根据外部因素动态更改过程在运行时的行为?看看Strategy模式。
  • 允许在运行时动态更改过程的行为,具体取决于内部因素?例如,如果某些请求可能会将程序切换到“维护模式”?查看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的一种形式。