简体   繁体   English

一般编程问题。 什么时候使用OOP?

[英]General programming question. When to use OOP?

My program needs to do 2 things. 我的程序需要做两件事。

  1. Extract stuff from a webpage. 从网页中提取内容。

  2. Do stuff with a webpage. 做一些网页的东西。

However, there are many webpages, such as Twitter and Facebook. 但是,有很多网页,如Twitter和Facebook。

should I do this? 我应该这样做吗?

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

Or, should I have some sort of class? 或者,我应该上某种课吗? When is it recommended to use classes, and when is it recommend to just use functions? 什么时候建议使用类,什么时候建议只使用函数?

"My program needs to do 2 things." “我的计划需要做两件事。”

When you start out like that, the objects cannot be seen. 当你这样开始时,无法看到对象。 You're perspective isn't right. 你的观点是不对的。

Change your thinking. 改变你的想法。

"My program works with stuff" “我的程序与东西一起工作”

That's OO thinking. 这是OO的想法。 What "stuff" does your program work with? 你的程序使用什么“东西”? Define the stuff. 定义东西。 Those are your basic classes. 那些是你的基础课程。 There's a class for each kind of stuff. 每种东西都有一个类。

"My program gets the stuff from various sources" “我的程序从各种来源获取东西”

There's a class for each source. 每个来源都有一个班级。

"My program displays the stuff" “我的程序显示的东西”

This is usually a combination of accessor methods of the stuff plus some "reporting" classes that gather parts of the stuff to display it. 这通常是东西的访问器方法和一些“报告”类的组合,这些类收集部分内容以显示它。

When you start out defining the "stuff" not the "do", you're doing OO programming. 当你开始定义“东西”而不是“做”时,你正在做OO编程。 OO applies to everything, since every single program involves "doing" and "stuff". OO适用于所有事物,因为每个程序都涉及“做”和“东西”。 You can chose the "doing" POV (which is can be procedural or functional), or you can chose the "stuff" POV (which is object-oriented.) 你可以选择“做”POV(可以是程序性的或功能性的),或者你可以选择“东西”POV(面向对象)。

My favorite rule of thumb: if you're in doubt (unspoken assumption: "and you're a reasonable person rather than a fanatic";-), make and use some classes. 我最喜欢的经验法则:如果你有疑问(不言而喻的假设:“你是一个理性的人而不是狂热的人”;-),那就制作并使用一些课程。 I've often found myself refactoring code originally written as simple functions into classes -- for example, any time the simple functions' best way to communicating with each others is with globals, that's a code smell, a strong hint that the system's factoring is not really good -- and often refactoring the OOP way is a reasonable fix for that. 我经常发现自己将最初编写为简单函数的代码重构为类 - 例如,任何时候简单函数与彼此通信的最佳方式都是全局变量,这是代码气味,强烈暗示系统的分解是不是很好 - 通常重构OOP方式是一个合理的解决方案。

Python is multi-paradigm, but its central paradigm is OOP (much like, say, C++). Python是多范式的,但它的核心范例是OOP(很像C ++)。 When a procedural or functional approach (maybe through generators &c) is optimal for some part of the system, that generally stands out -- for example, static functions are also a code smell, and if your classes have any substantial amount of those THAT is a hint to refactor things to avoid that requirement. 当程序或功能方法(可能通过生成器和c)对于系统的某些部分来说是最佳的时,这通常是突出的 - 例如,静态函数也是代码气味,并且如果您的类具有任何大量的那些,那么提示重构事物以避免这种要求。

So, assuming you have a rich grasp of all the paradigms Python affords -- if you're STILL in doubt, that suggests you probably want to go OOP for that part of your system! 因此,假设您对Python提供的所有范例有着丰富的把握 - 如果您仍然存在疑问,那么这表明您可能希望为系统的那一部分进行OOP! Just because Python supports OOP even more wholly than it supports functional programming and the like. 仅仅因为Python支持OOP甚至比它支持函数式编程等更全面。

From your very skeletal code, it seems to me that each extract/process pair belongs together and probably needs to communicate state, so a small set of classes with extraction and processing methods seems a natural fit. 从您的骨架代码来看,在我看来,每个提取/进程对都属于一起,可能需要通信状态,因此一小部分具有提取和处理方法的类似乎很自然。

It's up to you. 由你决定。 I personally try to stay away from Java-style classes when programming in python. 在python中编程时,我个人试图远离Java风格的类。 Instead, I use dicts and/or simple objects. 相反,我使用dicts和/或简单对象。

For instance, after defining these functions (the ones you defined in the question), I'd create a simple dict, maybe like this: 例如,在定义了这些函数(你在问题中定义的函数)之后,我会创建一个简单的dict,也许是这样的:

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

or, better yet, use introspection to get the process/extract function automatically: 或者,更好的是,使用内省自动获取进程/提取函数:

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

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

Where module is the current module (or the module that has these functions). 其中module是当前模块(或具有这些功能的模块)。

To get this module as an object: 要将此模块作为对象:

import sys
module = sys.modules[__name__]

Assuming of course, that the generic main function does something like this: 当然假设泛型main函数做了这样的事情:

figure out sitename based on input.
    get the extractor function for the site
    get processor function for the site
    call the extractor then the processor

Put as much of the common stuff together in a single function. 将尽可能多的常见内容放在一个函数中。 Once you've factored as much out as possible, build a mechanism for branching to the appropriate function for each website. 一旦你尽可能多地考虑了因素,就建立一个机制来分支到每个网站的相应功能。

One possible way to do this is with python's if/else clauses, but if you have many such functions, you may want something more elegant such as 一种可能的方法是使用python的if/else子句,但是如果你有很多这样的函数,你可能想要更优雅的东西,比如

F = __import__('yourproject.facebookmodule')

This lets you put the code that's specific for facebook in it's own area. 这使您可以将特定于facebook的代码放在其自己的区域中。 Since you pass __import__() a string, you can modify that at runtime based on which site you're accessing, and then just call function F in your generic worker code. 由于您传递__import__()一个字符串,您可以根据您正在访问的站点在运行时修改它,然后只需在您的通用工作程序代码中调用函数F.

More on that here: http://effbot.org/zone/import-confusion.htm 更多相关内容: http//effbot.org/zone/import-confusion.htm

You use OOP when it makes sense, when it makes developing the solution quicker and when it makes the end result easier to read, understand and maintain. 您可以在有意义的时候使用OOP,它可以更快地开发解决方案,并且可以使最终结果更易于阅读,理解和维护。

In this case it might make sense to create a generic Extractor interface/class and then have subclasses for Twitter, MySpace, Facebook, etc but this really depends on how site-specific the extraction is. 在这种情况下,创建一个通用的Extractor接口/类然后有Twitter,MySpace,Facebook等的子类可能是有意义的,但这实际上取决于特定于站点的提取方式。 The idea of this kind of abstraction is to hide such details. 这种抽象的想法是隐藏这些细节。 If you can do it, it makes sense. 如果你能做到,那就有意义了。 If you can't you probably need a different approach. 如果你不能,你可能需要一个不同的方法。

It may also be that similar benefits can be obtained from good decomposition of a procedural solution. 也可以从程序解决方案的良好分解中获得类似的益处。

Remember at the end of the day that all these things are just tools. 记住,在一天结束时,所有这些东西都只是工具。 Pick the best one for that particular job rather than picking the hammer and then trying to turn everything into a nail. 选择最适合那个特定工作的工作,而不是挑选锤子,然后尝试将所有东西变成钉子。

I regularly define classes for solving problems for a few reasons, I'll model an example of my thinking below. 我经常定义解决问题的课程有几个原因,我将在下面给出一个我的想法的例子。 I have no compunctions about mixing OO models and procedural styles, that's often more a reflection of your work society than personal religion. 我没有关于混合OO模型和程序风格的任何内容,这通常更多地反映了你的工作社会而不是个人宗教。 It often works to have a procedural facade for a class hierarchy if that's what other maintainers expect. 如果这是其他维护者所期望的,那么它通常可以为类层次结构设置程序外观。 (Please excuse the PHP syntax.) (请原谅PHP语法。)

  • I'm developing strategies and I can follow a generic model. 我正在制定策略,我可以遵循通用模型。 So a possible modeling of your task might involve getting something to chew on URLs passed. 因此,对您的任务进行可能的建模可能涉及获取一些东西来咀嚼传递的URL。 This works if you want to simplify the outer logic a lot, and remove conditionals. 如果您想要简化外部逻辑并删除条件,则此方法有效。 This shows that I can use DNRY for the gather() method. 这表明我可以将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 */ } } 

  • I'm creating a set of routines that are ideally hidden, and better yet, shared. 我正在创建一组理想隐藏的例程,更好的是共享。 An example of this might be having a class that defines toolbox methods common to a task. 这方面的一个例子可能是拥有一个定义任务共有的工具箱方法的类。 More specific tasks could extend the toolbox to develop their own behavior. 更具体的任务可以扩展工具箱以开发自己的行为。

     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 ); } } 

  • I want to organize my code to convey broader meaning to the maintainer. 我想组织我的代码来向维护者传达更广泛的意义。 Consider that if I have even one remote service I'm interfacing with, I might be diving into different APIs inside their interface, and I want to group those routines along similar topics. 考虑一下,如果我有一个远程服务我正在接口,我可能会在他们的界面中潜入不同的API,我想将这些例程分组到类似的主题中。 Below, I show an example how I like to define a class defining common constants, a class defining basic service methods, and a more specific class for weather alerts because the alert should know how to refresh itself, and it's a more specific than the weather service itself, but also leverages the WeatherAPI constants as well. 下面,我展示了一个示例,我喜欢定义一个定义公共常量的类,一个定义基本服务方法的类,以及一个更具体的天气警报类,因为警报应该知道如何刷新自己,它比天气更具体服务本身,但也利用WeatherAPI常量。

     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(); 
  • 声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

     
    粤ICP备18138465号  © 2020-2024 STACKOOM.COM