Домашнее задание рефакторинга кода?

Это код, который мне нужно для рефакторинга для моей домашней работы:

if (state == TEXAS) {
    rate = TX_RATE;
    amt = base * TX_RATE;
    calc = 2 * basis(amt) + extra(amt) * 1.05;
} else if ((state == OHIO) || (state == MAINE)) {
    rate = (state == OHIO) ? OH_RATE : MN_RATE;
    amt = base * rate;
    calc = 2 * basis(amt) + extra(amt) * 1.05;
    if (state == OHIO)
        points = 2;
} else {
    rate = 1;
    amt = base;
    calc = 2 * basis(amt) + extra(amt) * 1.05;
}

Я сделал что-то вроде этого

if (state == TEXAS) {
    rate = TX_RATE;
    calculation(rate);
} 
else if ((state == OHIO) || (state == MAINE))
    {
rate = (state == OHIO) ? OH_RATE : MN_RATE;

calculation(rate);

if (state == OHIO)
    points = 2;
}

else {
    rate = 1;
    calculation(rate);
}

function calculation(rate)
{
    amt = base * rate;
    calc = 2 * basis(amt) + extra(amt) * 1.05;
}

Как я мог сделать лучше?
Изменить Я сделал редактирование кода amt = base * rate;

Ответ 1

Позиция Стива о выражении switch хорошая, но я хотел бы предложить другой подход: массивы.

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

Рассмотрим это:

#define OTHER 0
#define OHIO 1
#define MAINE 2
#define TEXAS 3

int rates[4];
rates[OTHER] = ...
rates[OHIO] = ...
rates[MAINE] = ...
rates[TEXAS] = ...

Посмотрите, что это может сделать функцию calculate по-разному. (Обратите внимание, что в "реальной жизни" массив int rates[4] можно было бы сделать разными способами - хэшмап, простой массив объектов struct rate { char state[12]; int rate; } с именами состояний и скоростями, хранящимися вместе во время выполнения, или простой статически назначенный массив int rates[4] = {0, 2, 3, 10}; Я выбрал это, потому что он показывает индексирование массива с помощью #define d content. enum также работает.)

Ответ 2

class State {
private :
  double taxRate;
  int baseWeight;
  int extraWeight;
  string name;
  base;
public:
  State(string name, double taxRate = 1, int point =0, double baseWeight=2, double extraWeight=1.05); //implement the method yourself
  double extra(double base);
  double basis(double base);
  double calculate(double base){
      return baseWeight * basis(base) + baseWeight * extra(base);
  }
  int point(){return point};

};

Теперь, как его использовать:

State ohio ("OHIO", OH_RATE, 2);
cout << "OHIO result:" ohio.calculate() << " point:" << ohio.point() << endl;

Ответ 3

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

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

 abstract class State:
   protected abstract int getAmt()

   protected int basis(amt):
      return ...?

   protected int extra(amt):
      return ...?

   public int getPoints()
      return 1 // Just a guess ?

   public final int calculateTax():
      return 2 * basis(getAmt()) + extra(getAmt()) * 1.05


 final class DefaultState > State:
   protected int getAmt():
      return base


 final class Texas > State:
   protected int getAmt():
      return base * TX_RATE


 final class Ohio > State:
   public getPoints():
      return 2

   protected int getAmt():
      return base * OH_RATE


 final class Ohio > State:
   protected int getAmt():
      return base * MN_RATE

Используемая здесь концепция называется "Open Recursion", если вы задавались вопросом

Ответ 4

У вас есть тег 'Java', поэтому, предполагая, что это на самом деле Java-совместимый, я бы сделал это с Enum:

enum USStates
{
    TEXAS(TX_RATE), OHIO(OH_RATE), MAINE(MN_RATE), OTHER(1);

    final double rate;

    USStates(double rate)
    {
        this.rate = rate;
    }

    public double calc(double base)
    {
        double amt = amt(base);
        return 2.0 * basis(amt) + extra(amt) * 1.05;
    }

    public double amt(double base)
    {
        return base * rate;
    }
}

Затем в вашем фактическом исполняемом коде:

    rate = state.rate;
    amt = state.amt(base);
    calc = state.calc(base);

    if (USStates.OHIO == state)
    {
        points = 2;
    }

Если "base" - это константа (которая не ясна из кода примера), это может быть упрощено далее, обратившись к ней напрямую как final, а не передавая ее как параметр.

Это решение имеет несколько преимуществ. Во-первых, фактические ставки для состояния фактически не должны находиться в их собственной отдельной константе, используя соглашение об именах, но фактически могут быть сохранены как часть самого Enum (поэтому вместо "TEXAS (TX_RATE)" вы могли бы просто ввести "TEXAS (1.4)" (или независимо от его значения)), и затем скорость поддерживается как часть перечисляемого типа "TEXAS".

Также полезно, что логика вычислений захватывается (инкапсулируется, даже) вместе с константами, на которых она работает.

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

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

Ответ 5

Не хочу делать домашнее задание для вас, поэтому вот подталкивание: взгляните на переключатель .

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

Ответ 6

1) Используйте переключатель, как сказал Стив:

switch(state) {
  case TEXAS: calcTexas(); break;
  case OHIO: calcOhio(); break;
  case MAINE: calcMaine() break;
  default: calcDefault(); break;
}


2) Рефакторинг методом "extract method" (вы сделали это, но у вас ошибка в вашем примере):

int calculation(int rate) {
  amt  = base * rate;
  return (2 * basis(amt)) + (extra(amt) * 1.05);
}


3) Если дополнительный (amt) возвращает тип int, запомните, чтобы его применить к float, потому что int * float = int (По крайней мере, на С++, я не уверен, что это было то же самое в java)

Ответ 7

Как и ответ куки:

switch(state)
{
    case TEXAS: rate = TX_RATE; break;
    case OHIO: rate = OH_RATE; break;
    case MAINE: rate = MN_RATE; break;
    default: rate = 1; break;
}

amt = base * rate;

calc = 2 * basis(amt) + extra(amt) * 1.05

//if the OHIO points = 2 thing is really necessary
if(OHIO == state) points = 2;

Не может быть ООП, но он уверен, что он меньше (и, imo, более обслуживаем);)

Ответ 8

Вместо использования операторов If/else или switch используйте шаблон стратегии. Затем вы можете добавить новое состояние в свой пример без изменения кода.