Мой код слишком процедурный? - PullRequest
11 голосов
/ 14 мая 2011

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

if(downloadFeeds(ftpServer, ftpUsername, ftpPassword, getFtpPathToLocalPathMap())) {
    loadDataSources();
    initEngine();
    loadLiveData();
    processX();
    copyIds();
    addX();
    processY();
    copyIds();
    addY();
    pauseY();
    resumeY();
    setParameters();
}

Эти различные методы затем создают целую кучу различных объектов, ипри необходимости вызывайте различные методы для этих объектов.

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

Все комментарии очень ценятся!

Ответы [ 9 ]

7 голосов
/ 14 мая 2011

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

Таким образом, код может выглядеть примерно так:

if(downloadFeeds(ftpServer, ftpUsername, ftpPassword, getFtpPathToLocalPathMap())) {
    datasource.load();
    engine.init();
    data.load();
    engine.processX(data);
    data.copyIds()
    foo.addX();
    engine.processY();
    // ...
}

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

Обратите внимание, что процедурный фрагмент кода не всегда плох:

class Person {
    public void getMilk() 
    {
        go(kitchen);
        Glass glass = cupboard.getGlass(); 
        fridge.open(); 
        Milk milk = fridge.getMilk(); 
        use(glass, milk);
        drink(glass);
    } 
    // More person-ish stuff
}

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

6 голосов
/ 14 мая 2011

Мой вопрос - это часть кода, которая четко управляет вашим приложением, например, это указывает на процедурное программирование,

Невозможно сказать, является ли этот фрагмент кода "слишком процедурным" .

  • Эти вызовы могут быть всеми методами экземпляра текущего объекта, работая с отдельными объектами или с переменными экземпляра текущего экземпляра. Это сделало бы код ОО, по крайней мере, до некоторой степени.

  • Если эти методы static, то код действительно процедурный. Будет ли это плохо, зависит от того, получают ли методы доступ и обновляют ли они состояние, хранящееся в полях static (Судя по именам, им, вероятно, придется это сделать.)

и если да, то какой будет более ОО-способ достижения того же результата?

Трудно сказать, не глядя на остальную часть кода, но на картинке, похоже, есть некоторые скрытые объекты; например

  • источники данных и (возможно) менеджер источников данных или реестр
  • engine какой-то
  • что-то для хранения данных в реальном времени, X и Y
  • и т. Д.

Некоторые из них, вероятно, должны быть классами (если они еще не являются классами) ... но какие из них зависят от того, что они делают, насколько они сложны и так далее. Состояние, которое не имеет смысла, так как классы (по какой-либо причине) можно сделать «более ОО», превратив статические переменные в переменные экземпляра.


Другие ответы предлагают конкретные рефакторинги, избавление от всех глобальных переменных, использование внедрения зависимостей и так далее. Я полагаю, что этой информации недостаточно, чтобы судить, помогут ли эти предложения.


Но стоит ли это того?

Простое создание приложения "more OO" не является полезной или стоящей целью. Ваша цель должна состоять в том, чтобы сделать код более читабельным, обслуживаемым, тестируемым, многократно используемым и т. Д. То, улучшится ли использование ОО, зависит от текущего состояния вашего кода, а также от качества вашего нового дизайна и работы по рефакторингу. Простое внедрение ООП не исправит плохой дизайн или не превратит «плохой» код в «хороший» код.

6 голосов
/ 14 мая 2011

Ух ты! Это не похоже на OO-Style. Как примерно так:

    ConnectionData cData = new ConnectionData(ftpServer, ftpUsername, ftpPassword, getFtpPathToLocalPathMap());


    if(downloadFeeds(cData)) {
      MyJobFacade fc = new MyJobFacade(); 
      fc.doYourJob();
    }

MyJobFacade.java

public class MyJobFacade {

    public void doYourJob() {
          /* all your do operations maybe on different objects */
    }
}

кстати Фасадная Выкройка http://en.wikipedia.org/wiki/Facade_pattern

4 голосов
/ 14 мая 2011

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

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

Второе,Я не вижу никакой логики проверки ошибок.Допустим, резюмеY завершается с ошибкой, может быть, проблема в возобновлении, но оно также может быть выше при pauseY или настолько высоко, как loadDataSources, и проблема проявляется только позже как исключение.

Не знаюзнаю, является ли это производственный код или нет, но это хороший кандидат для перефакторинга в несколько этапов.На первом этапе вы можете пройти и заставить каждую функцию возвращать логический успех или нет, и в теле каждой функции проверять известные случаи ошибок.После того, как вы обнаружите ошибки, начните избавляться от глобальных данных, передавая аргументы функции и возвращая данные результата;Вы можете заставить свои функции возвращать нулевые значения в случае сбоя или перейти к обработке исключений, я предлагаю исключения.После этого подумайте о том, чтобы сделать отдельные части тестируемыми изолированно;например, чтобы вы могли тестировать downloadFeeds отдельно от функции обработки данных и наоборот.

Если вы пройдете и проведете некоторый ре-факторинг, вы увидите очевидные места, где вы можете модулировать свой код и улучшать его.ИМО, вам следует меньше беспокоиться о том, достаточно ли вы ООП, и больше о том, можете ли вы это сделать 1. Эффективно отладить, 2. Эффективно протестировать и 3. Понять, оставив его в покое и вернувшись через 6 месяцев, чтобы поддерживать его..

Этот ответ закончился довольно долго, я надеюсь, что вы нашли его части полезными.: -)

3 голосов
/ 14 мая 2011

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

В более ОО подходе я ожидал бы увидеть что-то вроде этого:

if(downloadFeeds(ftpServer, ftpUsername, ftpPassword, getFtpPathToLocalPathMap()))
{
  MyDatasources ds = loadDataSources();
  Engine eng = initEngine(ds);
  DataObj data = loadLiveData(eng);
  Id[] xIds = processX(data.getX());
  Id[] newXIds = xIds.clone();
  data.addX(newXIds);
  Id[] yIds = processY(data.getY());
  Id[] newYIds = yIds.clone();
  data.addY(newYIds);
  pauseY();
  resumeY();
  someObject.setParameters(data);
}

Пол

2 голосов
/ 14 мая 2011

Вот как я научился различать:

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

Лучшим способом было бы назначить эти обязанности реальному объекту, который лучше всего справится с ними. После того, как эти обязанности будут должным образом распределены, реализуется программный шаблон, который может эффективно управлять этими функциями (например, шаблон Фасада).

1 голос
/ 14 мая 2011

Это действительно процедурное программирование. Если вы пытаетесь сделать его более оригинальным, я бы попробовал комбинацию из них:

  • Старайтесь, чтобы классы представляли данные, которые вы держите. Например, есть класс FeedReader для обработки каналов, класс DataLoader для загрузки данных и т. Д.
  • Попробуйте разделить методы на классы со связной функциональностью. Например, группа resume (), pause () в предыдущем классе FeedReader.
  • Сгруппируйте методы process () в класс ProcessManager.

По сути, просто попытайтесь представить в своей программе группу сотрудников, работающих на вас, и вам необходимо назначить им обязанности. (PM делает это для меня, затем DM делает это с результатом этого). Если вы возьмете всю ответственность на одного сотрудника, он или она сгорят.

0 голосов
/ 25 февраля 2013

Мой лучший совет: Чистый код: справочник по гибкому программному мастерству

Это отличная книга с рекомендациями по стилю кода.Немного долго, но стоит времени.

0 голосов
/ 14 мая 2011

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

...