【问题标题】:General programming question. When to use OOP?一般编程问题。何时使用 OOP?
【发布时间】:2010-12-11 02:51:45
【问题描述】:

我的程序需要做两件事。

  1. 从网页中提取内容。

  2. 用网页做事。

但是,有很多网页,例如 Twitter 和 Facebook。

我应该这样做吗?

def facebookExtract():
    code here
def twitterExtract():
    code here
def myspaceExtract():
    code here
def facebookProcess():
    code here
def twitterProcess():
    code here
def myspaceProcess():
    code here

或者,我应该参加某种课程吗?什么时候推荐使用类,什么时候推荐只使用函数?

【问题讨论】:

    标签: python oop function


    【解决方案1】:

    “我的程序需要做两件事。”

    当你这样开始时,物体是看不见的。你的观点不对。

    改变你的想法。

    “我的程序可以处理东西”

    这是面向对象的想法。您的程序使用什么“东西”?定义东西。这些是你的基础课程。每种东西都有一个类。

    “我的程序从各种来源获取资料”

    每个来源都有一个类。

    “我的程序显示内容”

    这通常是东西的访问器方法加上一些“报告”类的组合,这些类收集部分东西以显示它。

    当您开始定义“stuff”而不是“do”时,您就是在进行 OO 编程。 OO 适用于所有事物,因为每个程序都涉及“做”和“做事”。您可以选择“做” POV(可以是程序性的或功能性的),也可以选择“东西” POV(它是面向对象的)。

    【讨论】:

      【解决方案2】:

      出于几个原因,我经常定义用于解决问题的类,我将在下面举例说明我的想法。我对混合 OO 模型和程序风格并不感到内疚,这通常更多地反映了你的工作社会,而不是个人信仰。如果这是其他维护者所期望的,它通常可以为类层次结构提供过程外观。 (请原谅 PHP 语法。)

    • 我正在制定策略,我可以遵循通用模型。因此,您的任务的可能建模可能涉及获取一些东西来咀嚼传递的 URL。如果您想大量简化外部逻辑并删除条件,这很有效。这表明我可以将 DNRY 用于gather() 方法。
      // batch process method
      function MunchPages( $list_of_urls )
      {
          foreach( $list_of_urls as $url )
          {
              $muncher = PageMuncher::MuncherForUrl( $url );
              $muncher->gather();
              $muncher->process();
          }
      }
      // factory method encaps strategy selection
      function MuncherForUrl( $url )
      {
          if( strpos( $url, 'facebook.com' ))
               return new FacebookPageMuncher( $url );
          if( ... ) 
               return new .... ;
      }
      // common tasks defined in base PageMuncher
      class PageMuncher 
      {
          function gather() { /* use some curl or what */ }
          function process() {}
      }
      class FacebookPageMuncher extends PageMuncher
      {
          function process() { /* I do it 'this' way for FB */ }
      }
      

    • 我正在创建一组理想情况下隐藏但更好的是共享的例程。这方面的一个例子可能是有一个定义任务通用工具箱方法的类。更具体的任务可以扩展工具箱以发展自己的行为。
      class PageMuncherUtils
      {
          static function begin( $html, $context )
          {
             // process assertions about html and context
          }
          static function report_fail( $context ) {}
          static function exit_retry( $context ) {}
      }
      // elsewhere I compose the methods in cases I don't wish to inherit them
      class TwitterPageMuncher
      {
          function validateAnchor( $html, $context )
          {
              if( ! PageMuncherUtils::begin( $html, $context )) 
                  return PageMuncherUtils::report_fail( $context );
          }
      }
      

    • 我想组织我的代码以向维护者传达更广泛的含义。考虑一下,如果我有一个正在与之交互的远程服务,我可能会在它们的接口中深入研究不同的 API,并且我想将这些例程按照相似的主题进行分组。下面,我展示一个例子,我喜欢定义一个定义公共常量的类,一个定义基本服务方法的类,以及一个更具体的天气警报类,因为警报应该知道如何刷新自己,而且它比天气更具体服务本身,但也利用 Wea​​therAPI 常量。
      class WeatherAPI
      {
          const URL = 'http://weather.net';
          const URI_TOMORROW = '/nextday/';
          const URI_YESTERDAY= '/yesterday/';
          const API_KEY = '123';
      }
      class WeatherService
      {
          function get( $uri ) {  }
          function forecast( $dateurl ) {  }
          function alerts( $dateurl )
          {
              return new WeatherAlert( 
                  $this->get( WeatherAPI::URL.$date
                             ."?api=".WeatherAPI::API_KEY ));
          }
      }
      class WeatherAlert
      {
          function refresh() {}
      }
      // exercise:
      $alert = WeatherService::alerts( WeatherAPI::URI_TOMORROW );
      $alert->refresh();
      
    • 【讨论】:

        【解决方案3】:

        我最喜欢的经验法则:如果您有疑问(不言而喻的假设:“并且您是一个理性的人而不是狂热者”;-),请创建并使用一些课程。我经常发现自己将最初编写为简单函数的代码重构到类中——例如,任何时候简单函数相互通信的最佳方式是使用全局变量,这就是代码异味,强烈暗示系统的因式分解是不是很好——通常重构 OOP 方式是解决这个问题的合理方法。

        Python 是多范式,但它的核心范式是 OOP(很像 C++)。当程序或功能方法(可能通过生成器和c)对于系统的某些部分来说是最佳的时,这通常会很突出——例如,静态函数也是一种代码味道,如果你的类有大量的那些,那就是重构事物以避免该要求的提示。

        所以,假设您对 Python 提供的所有范式有深入的了解——如果您仍然有疑问,这表明您可能想要为系统的这一部分使用 OOP!仅仅因为 Python 对 OOP 的支持比它对函数式编程等的支持更加全面。

        从您非常骨架的代码来看,在我看来,每个提取/进程对都属于一起并且可能需要通信状态,因此一小部分具有提取和处理方法的类似乎很合适。

        【讨论】:

        • 狂热还好;只要我不用听;)
        • python 中的一切都是对象;包括功能。此外,过早的抽象是万恶之源。
        • 不确定你指的是什么“狂热”(希望修复你的错误拼写也不符合条件?!-)你指的是,o @Matthew——我是多范式编程的圣骑士,我只是不相信与市政厅战斗。因此,当有疑问时在范式中,在 Python 中我选择 OOP,就像在 O'Caml 中我选择函数式一样——它们都是多范式语言,但很明显 O'Caml 是i>主要是功能性的(附加了一些 OOP),而 Python 主要是 OOP(附加了一些功能性)。有什么狂热的?!
        • @hasen,if 语句(例如)不是 Python 中的对象;这与 Smalltalk 非常不同,后者没有“if 语句”,而是 if 是布尔方法,因此是一个对象。 Smalltalk 是狂热地 OOP(你必须疯狂地用任何其他范例在 Smalltalk 中编程!),Python 只是 务实地 所以(有很多情况下非 OOP方法,尤其是 FP,在 Python 中显然更可取)。 WRT 抽象,cfr us.pycon.org/2009/conference/schedule/event/75 -- 我认为我在那里讨论得相当好;-)。
        • >>过早的抽象是万恶之源>> :-)
        【解决方案4】:

        这取决于你。我个人在用 python 编程时尽量远离 Java 风格的类。相反,我使用字典和/或简单对象。

        例如,在定义了这些函数(你在问题中定义的那些)之后,我会创建一个简单的 dict,可能是这样的:

        { 'facebook' : { 'process' : facebookProcess, 'extract': facebookExtract }, 
         ..... 
        }
        

        或者,更好的是,使用自省来自动获取进程/提取函数:

        def processor(sitename):
            return getattr(module, sitename + 'Process')
        
        def extractor(sitename):
            return getattr(module, sitename + 'Extractor')
        

        其中module 是当前模块(或具有这些功能的模块)。

        将此模块作为对象获取:

        import sys
        module = sys.modules[__name__]
        

        当然,假设通用的 main 函数是这样的:

        根据输入找出站点名称。 获取站点的提取器功能 获取站点的处理器功能 调用提取器,然后调用处理器

        【讨论】:

        • “Java 风格的类”是什么意思?
        【解决方案5】:

        当 OOP 有意义、可以更快地开发解决方案以及最终结果更易于阅读、理解和维护时,您就会使用 OOP。

        在这种情况下,可能创建一个通用的 Extractor 接口/类,然后为 Twitter、MySpace、Facebook 等创建子类,但这实际上取决于提取的特定于站点的程度。这种抽象的想法是隐藏这些细节。如果你能做到,那是有道理的。如果你不能,你可能需要一种不同的方法。

        也可能从程序解决方案的良好分解中获得类似的好处。

        最后请记住,所有这些东西都只是工具。为特定的工作选择最好的,而不是拿起锤子,然后试图把所有东西都变成钉子。

        【讨论】:

          【解决方案6】:

          将尽可能多的常见内容放在一个函数中。尽可能多地考虑因素后,为每个网站建立一个分支到适当功能的机制。

          一种可能的方法是使用 python 的 if/else 子句,但如果你有很多这样的函数,你可能想要更优雅的东西,比如

          F = __import__('yourproject.facebookmodule')

          这使您可以将特定于 facebook 的代码放在它自己的区域中。由于您传递了__import__() 一个字符串,因此您可以在运行时根据您访问的站点对其进行修改,然后只需在您的通用工作代码中调用函数 F。

          更多关于这里: http://effbot.org/zone/import-confusion.htm

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-12-16
            • 1970-01-01
            • 1970-01-01
            • 2021-10-12
            • 2018-05-06
            • 1970-01-01
            相关资源
            最近更新 更多