Общий вопрос программирования. Когда использовать ООП? - PullRequest
2 голосов
/ 29 октября 2009

Моя программа должна сделать 2 вещи.

  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

Или мне нужен какой-нибудь класс? Когда рекомендуется использовать классы, а когда рекомендуется просто использовать функции?

Ответы [ 6 ]

16 голосов
/ 29 октября 2009

«Моя программа должна делать 2 вещи.»

Когда вы начинаете так, объекты не видны. Ты не прав.

Измени свое мышление.

"Моя программа работает с вещами"

Это ОО мышление. С какими «вещами» работает ваша программа? Определите материал. Это ваши основные классы. Есть класс для каждого вида вещей.

«Моя программа получает информацию из разных источников»

Для каждого источника есть класс.

«Моя программа отображает материал»

Обычно это комбинация методов доступа к материалу плюс некоторые «отчетные» классы, которые собирают части материала для его отображения.

Когда вы начинаете определять «вещи», а не «делать», вы занимаетесь программированием ОО. ОО относится ко всему, так как каждая программа включает в себя «делать» и «вещи». Вы можете выбрать «выполнение» POV (которое может быть процедурным или функциональным), или вы можете выбрать «материал» POV (который является объектно-ориентированным).

11 голосов
/ 29 октября 2009

Мое любимое эмпирическое правило: если у вас есть сомнения (невысказанное предположение: «а вы разумный человек, а не фанатик» ;-), создайте и используйте некоторые классы. Я часто оказывался рефакторингом кода, изначально написанного как простые функции, в классы - например, в любое время лучший способ взаимодействия простых функций - это глобальные переменные, это запах кода, сильный намек на то, что факторинг системы не очень хорошо - и часто рефакторинг ООП является разумным решением для этого.

Python является мультипарадигмой, но его центральная парадигма - ООП (во многом как, скажем, C ++). Когда процедурный или функциональный подход (возможно, с помощью генераторов и с) оптимален для некоторой части системы, это обычно выделяется - например, статические функции также являются запахом кода, и если ваши классы имеют какое-то существенное количество этих намек на рефакторинг вещей, чтобы избежать этого требования.

Итак, при условии, что вы обладаете богатым пониманием всех парадигм, которые предоставляет Python - если вы ВСЕ ЕЩЕ сомневаетесь, то это говорит о том, что вы, вероятно, захотите использовать ООП для этой части своей системы! Просто потому, что Python поддерживает ООП даже более полно, чем функциональное программирование и тому подобное.

Из вашего самого скелетного кода мне кажется, что каждая пара извлечения / процесса принадлежит друг другу и, вероятно, должна сообщать о состоянии, поэтому небольшой набор классов с методами извлечения и обработки кажется естественным.

3 голосов
/ 29 октября 2009

Это зависит от вас. Я лично стараюсь держаться подальше от классов в стиле Java при программировании на python. Вместо этого я использую диктовки и / или простые объекты.

Например, после определения этих функций (тех, которые вы определили в вопросе) я бы создал простой диктовку, возможно, такую:

{ '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 выполняет что-то вроде этого:

    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
2 голосов
/ 29 октября 2009

Соберите как можно больше общих вещей в одну функцию. Как только вы учтете как можно больше, создайте механизм перехода к соответствующей функции для каждого веб-сайта.

Один из возможных способов сделать это - воспользоваться предложениями Python if/else, но если у вас много таких функций, вы можете захотеть что-то более элегантное, например

F = __import__('yourproject.facebookmodule')

Это позволяет вам разместить код, специфичный для Facebook, в своей области. Поскольку вы передаете __import__() строку, вы можете изменить ее во время выполнения в зависимости от того, к какому сайту вы обращаетесь, а затем просто вызвать функцию F в своем общем рабочем коде.

Подробнее об этом здесь: http://effbot.org/zone/import-confusion.htm

1 голос
/ 29 октября 2009

Я регулярно определяю классы для решения задач по нескольким причинам, ниже я приведу пример своего мышления. У меня нет никаких сомнений по поводу смешивания ОО-моделей и процедурных стилей, это часто больше отражает ваше рабочее общество, чем личную религию. Часто это имеет процедурный фасад для иерархии классов, если этого ожидают другие сопровождающие. (Пожалуйста, извините за синтаксис PHP.)

Я разрабатываю стратегии и могу следовать общей модели. Таким образом, возможное моделирование вашей задачи может заключаться в том, чтобы заставить что-то жевать пропущенные URL-адреса. Это работает, если вы хотите значительно упростить внешнюю логику и удалить условные выражения. Это показывает, что я могу использовать DNRY для метода collect ().
// 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 внутри их интерфейса и хочу сгруппировать эти подпрограммы по сходным темам. Ниже я показываю пример того, как мне нравится определять класс, определяющий общие константы, класс, определяющий основные методы обслуживания, и более конкретный класс для оповещений о погоде, потому что оповещение должно знать, как обновлять себя, и оно более специфично, чем погода сервис, но также использует константы 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();
1 голос
/ 29 октября 2009

Вы используете ООП, когда это имеет смысл, когда это ускоряет разработку решения и когда конечный результат легче читать, понимать и поддерживать.

В этом случае может имеет смысл создать общий интерфейс / класс Extractor, а затем иметь подклассы для Twitter, MySpace, Facebook и т. Д., Но это действительно зависит от того, насколько извлечен сайт. Идея такого рода абстракции состоит в том, чтобы скрыть такие детали. Если вы можете сделать это, это имеет смысл. Если вы не можете, вам, вероятно, нужен другой подход.

Возможно также, что аналогичные выгоды могут быть получены в результате хорошего разложения процедурного решения.

Помните, в конце концов, что все это всего лишь инструменты. Выберите лучший для этой конкретной работы, вместо того, чтобы брать молоток, а затем пытаться превратить все в гвоздь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...