Объектно-ориентированный или последовательный?

Вы можете попробовать использовать следующий шаблон регулярных выражений:

^[^@]+@[^.]+[.][^.]{2,}([.][^.]{2,})*$

Самая правая часть шаблона означает:

[.]                match a literal dot
[^.]{2,}           followed by a domain component (any 2 or more characters but dot)
([.][^.]{2,})*     followed by dot and another component, zero or more times

Демо

[114 ]

Так что это будет соответствовать:

jon.skeet@google.com
jon.skeet@google.co.uk

Но не будет соответствовать:

gordonlinoff@blah

6
задан George Stocker 25 November 2008 в 21:20
поделиться

12 ответов

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

SolverPotential::solve(){
SolvePotential::interpolate()
SolverPotential::compute_flux()
SolverPotential::compute_energy()
// ... 
//  10 other high-level function calls with NO parameter lists (just use private member variables)
}

Это также делает в значительной степени невозможным поддержать инвариант класса, не так ли? Когда это допустимо для вызова compute_flux? Решить? Интерполировать? Что должно мешать мне делать его в неправильном порядке? Класс будет в допустимом состоянии, если я сделаю? Я вытащу допустимые данные из него?

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

// This struct could be replaced with something like typedef boost::tuple<double,double,double> coord3d
struct coord3d {
double x, y, z;
};

coord3d interpolate(const coord3d& coord, const coord3d& interpolated, double potential); // Just return the potential, rather than using messy output parameters
double compute_flux(const coord3d coord&flux); // Return the flux instead of output params
double compute_energy(const coord3d& coord); // And return the energy directly as well

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

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

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

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

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

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

Не становитесь слишком одержимыми ООП. Используйте инструменты в Вашем распоряжении.

Я не знаю достаточно контекста Вашего вопроса сказать наверняка, но мне кажется, что то, в чем Вы действительно нуждаетесь, не является классом, это - просто иерархия функций. Ваши пользовательские вызовы кода решают (). решите (), внутренне звонит, скажите (составленный, ради примера), интерполируйте () и compute_energy (). compute_energy () внутренне называет compute_flux () и так далее. Каждая функция только выполняет несколько вызовов для выполнения логических шагов, которые составляют ответственность из функции. Так нигде не сделайте у Вас есть огромный класс с дюжиной различных обязанностей или большая монолитная функция, которая делает все последовательно.

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

3
ответ дан 8 December 2019 в 18:43
поделиться

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

Кроме того, класс SolvePotential не имеет большой смысл, так как классом должен быть Объект с методом SolvePotential.

5
ответ дан 8 December 2019 в 18:43
поделиться

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

В некотором смысле Вы убрали основное () функция и теперь имеете грязный класс, который можно убрать далее по усмотрению. Своего рода разделите и завоюйте метод. Или возможно "давка весь мой спам в" методе на чердаке, где, по крайней мере, наиболее используемая часть дома является чистой.

1
ответ дан 8 December 2019 в 18:43
поделиться

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

Если у меня не было второго места для использования класса, я просто объявлю функции с явными аргументами - это будет более ясно (особенно новому человеку, смотрящему на этот код впервые), чем использование методов на классе, которые требуют скрытого состояния.

3
ответ дан 8 December 2019 в 18:43
поделиться

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

0
ответ дан 8 December 2019 в 18:43
поделиться

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

По-моему, функции имеют намного больше смысла здесь, так как Вы реализуете математические процедуры, которые не основаны на состоянии и не должны снова использовать данные. Используя OO здесь означает создавать объекты только, чтобы назвать один метод и затем уничтожить их. Это звучит более подверженным ошибкам и менее интуитивным мне, чем процедурный API. Кроме того, как bradheintz говорит, явный список параметров также удаляет проблему необходимости не забыть инициализировать класс прежде на самом деле использование он (типичная ошибка при рефакторинге).

Между прочим, с точки зрения функций возвращаемые значения использования вместо i/o параметров обычно заставляют API выглядеть намного более ясным.

Я даже смел бы говорить, что Вы могли бы хотеть смешать OO и процедуры, с помощью классов для понятий как Векторы (я вижу некоторый x, y, z вокруг). Это также удалило бы некоторые параметры если, именно это касается Вас так.

float SolvePotential(const Vector3& vn, float nOrder)
{
    // ...
    const float newPotential = interpolate(vn, v_interp, potential);
    const float flux         = compute_flux(vn);
    const float energy       = compute_energy(vn);
    // ...
    return result;
}

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

Надежда это помогает!

2
ответ дан 8 December 2019 в 18:43
поделиться

Вы забыли упоминать второй план, который должен начать писать объект, но передавать параметры его методам. Псевдокод-ly:

SolverPotential::solve(a, b, c, d){
  SolvePotential::interpolate(a, b);
  SolverPotential::compute_flux(b, c);
  SolverPotential::compute_energy(c, d)

Таким образом Вы начинаете свой рефакторинг путем размышления в (по-видимому, легче) последовательный режим, 'что я должен под рукой решить этот шаг?' Кроме того, Возможно, Вы будете видеть последовательности параметров и retvals, которые предлагают объектные подразделения ("После этого шага, который я никогда не использую снова. Возможно, первые два шага должны инкапсулироваться в другом классе".)

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

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

0
ответ дан 8 December 2019 в 18:43
поделиться

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

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

Я нахожусь в подобном положении, работавшем в Java в течение долгого времени и теперь работающем в C/C++. Вы проверяли вокруг, чтобы видеть, было ли это 500 основными методами строки по причине? Существуют издержки к инстанцированию объектов и проверке виртуальных таблиц и т.п.. Действительно ли производительность является проблемой здесь? Кажется, что это было бы в математическом вычислении как это. Если это так, все ставки выключены с подходом Martin-Fowler-esque.

Удачи.

0
ответ дан 8 December 2019 в 18:43
поделиться

Так как Вы используете объектно-ориентированный язык, необходимо пойти для объектов, предположив, что Вы могли снова использовать объекты в будущем.

Всего один совет: попытайтесь разработать хорошую диаграмму классов. Каковы объекты Вас программа? Я видел бы класс "уравнения" с полученным "differentialEquation" один и так далее.

-1
ответ дан 8 December 2019 в 18:43
поделиться

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

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

Я боюсь, что не знаю синтаксис C++, таким образом, я буду использовать Java вместо этого.

public class ValuePlusOne implements Computable {
    private int value;
    private int result;
    private Boolean hasRun;
    private static Map instanceMap = new HashMap();

    // Creates an instance reusing an existing one if possible
    public static getInstance(int value) {
        ValuePlusOne instance = (ValuePlusOne)instanceMap.get(value);

        if (instance = null) {
            instance = new ValuePlusOne(value);
            instanceMap.put(value,instance);
        }
        return instance;
    }

    // Private constructor
    private ValuePlusOne(int value) {
        this.value = value;
        hasRun = false;
    }

    // Computes (if not already computed) and returns the answer
    public int compute() {
        if (!hasRun) {
            hasRun = true;
            result = value + 1;
        }

        return result;
    }
}

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

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

0
ответ дан 8 December 2019 в 18:43
поделиться

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

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

Если при повреждении функции в подфункции и консолидации параметров в структуры, Вы видите хороший объект там, пойдите для нее, но я не провел бы время, пробуя к рожку для обуви ее в ту, если проблема не гравитирует там.

0
ответ дан 8 December 2019 в 18:43
поделиться

Так как Вы упоминаете, что одной из Ваших целей является инкапсуляция, я указал бы на Вас к подходу OO.

В Вашем примере кода я думаю, что Ваше имя класса немного выключено. Я сначала применил бы рефакторинг, который Вы делаете (Метод извлечения в меньшие функции). После этого проанализируйте, какие части данных идут с тем, какие части логики и Преобразовывают Процедурный Дизайн в Объекты (у Fowler нет ссылки для этого "Большого Рефакторинга").

Это - подход снизу вверх. Нисходящим подходом, который я проявил бы, является Шаблон "команда", который является по существу, что Вы имеете в своем исходном вопросе, сохраняете плохое имя класса: создайте класс под названием PotentialEquation, инициализируйте его через конструктора, фабрику или методы set, независимо от того, что Вы любите и затем выставляете решение () метод, возможно, как это:

PotentialSolution solve()

В PotentialSolution можно далее инкапсулировать решение уравнения, которое, вероятно, более сложно, чем тип примитива.

0
ответ дан 8 December 2019 в 18:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: