「Lean Architecture for Agile Software Development」のドラフトのAPPENDIX AのScalaのコードを意訳してみる。
本文中にC++のコードがあるんですが、まとまった形のコードになっていなかったので、他言語での例から逆にC++に翻訳してみました。
ここではmain
関数がContextの役目を果たしています。たぶん。
Scalaでtratとして表現しているものを、仮想クラスで表現してみようとしたんですが、MoneySink::increaseBalance
, MoneySink:updateLog
とAccount::increaseBalance
, Account::updateLog
の衝突をうまく解消できず、苦肉の策で transferTo
を関数テンプレートにしたんですが、こうしてしまうとMoneySink
を定義する意味がなくなってしまい、すっきりしません。
本質的なところでなくて言語に依存する実装のところで引っかかっているのが悔しい。
以下コード。
// see: http://sites.google.com/a/gertrudandcope.com/info/Publications/LeanArchitecture.pdf #include <stdexcept> #include <string> #include <iostream> class InsufficientFundsException : public std::runtime_error { public: InsufficientFundsException() : std::runtime_error("InsufficientFundsException") {} }; class Date { public: Date() : time_(std::time(0)) {} std::string toString() const { std::string result(std::ctime(&time_)); return result.substr(0, result.size() - 1); } private: std::time_t time_; }; std::ostream& operator << (std::ostream& out, const Date& date) { return out << date.toString(); } class Account { public: long availableBalance() const { return balance_; } void decreaseBalance(long amount) { if(amount < 0) { throw InsufficientFundsException(); } balance_ -= amount; } void increaseBalance(long amount) { balance_ += amount; } void updateLog(const std::string& msg, const Date& date, long amount) { std::cout << "Account: " << name() << ", " << msg << ", " << date << ", " << amount << std::endl; } virtual std::string name() const { return "Account"; } private: long balance_; }; class MoneySink { public: /* virtual void increaseBalance(long amount) = 0; virtual void updateLog(const std::string& msg, const Date& date, long amount) = 0; */ }; class MoneySource { public: /* virtual void transferTo(long amount, MoneySink& recipient) = 0; */ }; template <class Sink> class TransferMoneySink : public MoneySink { public: Sink* self() { return static_cast<Sink*>(this); } /* void increaseBalance(long amount) { Sink::increaseBalance(amount); } void updateLog(const std::string& msg, const Date& date, long amount) { Sink::updateLog(msg, date, amount); } */ void transferFrom(long amount, MoneySource& source) { self()->increaseBalance(amount); self()->updateLog("Transfer In", Date(), amount); } }; template <class Source> class TransferMoneySource : public MoneySource { public: Source* self() { return static_cast<Source*>(this); } template <class MoneySink> void transferTo(long amount, MoneySink& recipient) { // beginTransaction(); if(self()->availableBalance() < amount) { // endTransaction(); throw InsufficientFundsException(); } else { self()->decreaseBalance(amount); recipient.increaseBalance(amount); self()->updateLog("Transfer Out", Date(), amount); recipient.updateLog("Transfer In", Date(), amount); } // gui.displayScrean(SUCCESS_DEPOSIT_SCREEN); // endTransaction(); } }; class SavingsAccount : public Account, public TransferMoneySource<SavingsAccount> { public: std::string name() const { return "SavingsAccount"; } }; class CheckingAccount : public Account, public TransferMoneySink<CheckingAccount> { public: std::string name() const { return "CheckingAccount"; } }; int main(int argc, char* argv[]) { SavingsAccount source; CheckingAccount sink; source.increaseBalance(10000); source.transferTo(200, sink); std::cout << "source.availableBalance: " << source.availableBalance() << " / sink.availableBalance: " << sink.availableBalance() << std::endl; return 0; }
実行結果。
Account: SavingsAccount, Transfer Out, Wed Feb 3 21:22:51 2010, 200 Account: CheckingAccount, Transfer In, Wed Feb 3 21:22:51 2010, 200 source.availableBalance: 9800 / sink.availableBalance: 200