Является ли мой код слишком процедурным?

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

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

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

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

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

Ответ 1

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

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

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

Источник данных, движок и все другие компоненты могут быть внедрены в ваш бизнес-процесс, поэтому: а) тестирование упрощается, 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
}

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

Ответ 2

Ничего себе! Это не похоже на 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

Ответ 3

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

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

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

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

и если да, то каков был бы более способ OO для достижения того же результата?

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

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

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


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


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

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

Ответ 4

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

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

Во-вторых, я не вижу логики проверки ошибок. Предположим, что resumeY сбой исключается, возможно, проблема заключается в resumeY, но она также может быть выше в pauseY или до loadDataSources, и проблема только проявляется как исключение позже.

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

Если вы пройдете и сделаете повторный факторинг, вы начнете видеть очевидные места, где вы сможете модулизовать свой код и улучшить его. ИМО, вы должны меньше беспокоиться о том, достаточно ли вы оооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооо. /p >

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

Ответ 5

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

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

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

Пол

Ответ 6

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

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

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

Ответ 7

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

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

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

Ответ 8

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