Осуществление банковских операций - побочные эффекты барьеров


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

#include <cstdio>
#include <vector>
#include <string>
#include <numeric>
using namespace std;

// Interfaces to the roles.
class IsourceAccount {
public:
  virtual int availableBalance() = 0;
  virtual void withdraw(int amount) = 0;
};

class IdestAccount {
public:
  virtual int availableBalance() = 0;
  virtual void deposit(int amount) = 0;
};

// MODEL Classes.
class Account: public IsourceAccount, public IdestAccount {
private:
  int balance;
public:
  Account(): balance(0) { }
  int availableBalance() { return balance; }
  void deposit(int amount) { balance += amount; }
  void withdraw(int amount) { balance -= amount; }
};

class Account2: public IsourceAccount, public IdestAccount {
private:
  vector<int> audit;
public:
  Account2() { }
  int availableBalance() { return accumulate( audit.begin(), audit.end(), 0 ); }
  void deposit(int amount) { audit.push_back(amount); }
  void withdraw(int amount) { audit.push_back(-amount); }
};

// How the Model objects interact
class moneyTransferCTX {
  // Role methods (from context and from roleplayers).
  class /*role*/ sourceAccount: public IsourceAccount {
    public:
      void transferTo(moneyTransferCTX *ctx, int amount);
  } *sourceAccount;

  class /*role*/ destAccount: public IdestAccount {
    public:
      void receive(moneyTransferCTX *ctx, int amount);
  } *destAccount;

public:
  moneyTransferCTX(IsourceAccount *src, IdestAccount *dst):
    // Cast the objects (src,dst) to their roles (sourceAccount, destAccount).
    sourceAccount(static_cast<decltype(sourceAccount)>(src)),
    destAccount(static_cast<decltype(destAccount)>(dst))
  { }

  void execute(int amount) {
    this->sourceAccount->transferTo(this, amount);
  }  
};

void moneyTransferCTX::sourceAccount::transferTo(moneyTransferCTX *ctx, int amount) {
  if (this->availableBalance() < amount) throw -1;
  this->withdraw(amount);
  ctx->destAccount->receive(ctx, amount);
}

void moneyTransferCTX::destAccount::receive(moneyTransferCTX *ctx, int amount) {
  this->deposit(amount);
}

// Testing...
int main() {
  Account2 *savings = new Account2();
  Account *checking = new Account(); //Changing the class type shouldn't hurt the code.

  moneyTransferCTX *ctx[2];

  savings->deposit(10);
  checking->deposit(100);

  ctx[0] = new moneyTransferCTX(savings, checking);
  ctx[1] = new moneyTransferCTX(checking, savings);

  printf("Opening Status- Savings: %d, Checking: %d\n", savings->availableBalance(), checking->availableBalance());  

  try {
    printf("Transfer Savings to Checking 50\n");
    ctx[0]->execute(50);
  } catch(int n) {
    printf("Error: not enough funds\n");
  }

  printf("Savings: %d, Checking %d\n", savings->availableBalance(), checking->availableBalance());  

  try {
    printf("Transfer Checking to Savings 30\n");
    ctx[1]->execute(30);
  } catch(int n) {
    printf("Error: not enough funds\n");
  }

  printf("Closing Status- Savings: %d, Checking %d\n", savings->availableBalance(), checking->availableBalance());

  delete ctx[0];
  delete ctx[1];
  delete savings;
  delete checking;
  return 0;
}


718
7
задан 17 декабря 2011 в 11:12 Источник Поделиться
Комментарии
1 ответ

В moneyTransferCTX:

Вы используете указатели. А вы не ждите, что они будут иметь значение null (и они не могут быть изменены), вы должны быть с использованием ссылок.

Конструктор-это приведение объектов к вещам, которые не обязательно верны.

moneyTransferCTX(IsourceAccount *src, IdestAccount *dst):
// Cast the objects (src,dst) to their roles (sourceAccount, destAccount).
sourceAccount(static_cast<decltype(sourceAccount)>(src)),
destAccount(static_cast<decltype(destAccount)>(dst))
{}

Просто потому, что РКЦ-это IsourceAccount не означает, что он также sourceAccount поэтому бросать его не может быть действительным. Но, похоже, что вы пытаетесь создать класс-оболочку (как это):

class sourceAccount
{
IsourceAccount& account;
public:
sourceAccount(IsourceAccount &acc): account(acc) {}
void transferTo(moneyTransferCTX& ctx, int amount)
{
if (account.availableBalance() < amount) throw -1;
account.withdraw(amount);

ctx.destAccount->receive(ctx, amount);
}
} sourceAccount;

Лично я считаю, что это перебор и вы должны просто поставить работу, что ты вкладывал в эти классы в метод execute.

Я хотел бы сделать это:

// How the Model objects interact
class moneyTransferCTX
{
IsourceAccount& sourceAccount;
IdestAccount& destAccount;

public:
moneyTransferCTX(IsourceAccount& src, IdestAccount& dst)
: sourceAccount(src)
, destAccount(dst)
{}

void execute(int amount)
{
if (sourceAccount.availableBalance() < amount) throw -1;
sourceAccount.withdraw(amount);
destAccount.deposit(amount);
}
};

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

Account2 *savings = new Account2();
Account *checking = new Account(); //Changing the class type shouldn't hurt the code.

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

Account2 savings;
Account checking; //Changing the class type shouldn't hurt the code.

Научиться использовать C++ потоки (это обеспечивает безопасность типов):

printf("Opening Status- Savings: %d, Checking: %d\n",
savings->availableBalance(),
checking->availableBalance());

//

std::cout << "Opening Status- Savings: " << savings->availableBalance()
<< ", Checking: " << checking->availableBalance()
<< "\n";

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

Другая вещь, которую я хотел бы отметить, это то, что вы, вероятно, не должны использовать int (хотя я подозреваю, что ты делаешь это только в качестве демонстрации) как исключение. Либо выбрать один из стандартных исключений, или создать свой собственный (производный от std::образом).

  try {
printf("Transfer Savings to Checking 50\n");
ctx[0]->execute(50);
} catch(int n) {
printf("Error: not enough funds\n");
}

Больше указатель комментарии. Нормальный (современный C++) содержит очень мало указателей. И даже менее явные вызовы для удаления. Любой динамически выделяемый объект должны быть обернуты внутри смарт-указатель (или контейнер как импульс объекта::ptr_container приходит на ум здесь). Это делает есть исключения использование безопасным.

std::auto_ptr<moneyTransferCTX> tr(new moneyTransferCTX(checking, savings));
// Note I am using auto_ptr as this is the only smart pointe in C++03
// But it is not considered a good one (as it is easy to misuse)
// Look up the boost::shared_ptr or if you have C++11 compiler it is part of the standard.

// Note there are several good smart pointers and you should learn when to use
// each one appropriately.

3
ответ дан 18 декабря 2011 в 07:12 Источник Поделиться