Здравствуйте, Mamut, Вы писали:
Эх, что-то я со своим новым проектом так ушёл в дела, что совсем забыл про rsdn. А тут такие забавные вещи обсуждаются... Ну попробую влить новую кровь в затухающую дискуссию. )))
Значит тебе хочется увидеть пример работы статической типизации в бизнес-логике. Такое встречается не так уж часто и на это есть вполне очевидная причина — подобные проверки в большинстве случаев будут идти не вместо рантайм проверок, а в дополнение к ним (проверяя условия на другом логическом уровне), так что получается увеличение кода без казалось бы существенного увеличения надёжности. Ну а программистская лень общеизвестна... ))) Однако для простенького форумного случая можно и написать демонстрацию. Причём твой "Шаг1" отлично для этого подходит (на следующие шаги естественно это легко экстраполируется, но уже лень). Вот полный компилируемый код примера:
#include <stdexcept>
#include <iostream>
using namespace std;
enum class OrderType {Unknown, OnlyDecrease, Constant};
template<OrderType Type>
struct Order{
const int amount;
const bool sent;
const bool risk;
};
template<OrderType Type> auto RiskOrder(const Order<Type>& o) {return Order<OrderType::Constant>{o.amount, o.sent, true};}
template<OrderType Type> auto SendOrder(const Order<Type>& o) {return Order<Type==OrderType::Unknown?OrderType::OnlyDecrease:Type>{o.amount, true, o.risk};}
template<OrderType Type> auto Increase(const Order<Type>& o, int v)
{
static_assert(Type==OrderType::Unknown, "You can not increase this order");
if(o.sent||o.risk) throw invalid_argument("You can not increase this order");
else return Order<Type>{o.amount+v, o.sent, o.risk};
}
template<OrderType Type> auto Decrease(const Order<Type>& o, int v)
{
static_assert(Type!=OrderType::Constant, "You can not decrease this order");
if(Type==OrderType::Unknown&&o.risk) throw invalid_argument("You can not decrease this order");
else return Order<Type>{o.amount-v, o.sent, o.risk};
}
template<OrderType Type, typename F> void ProcessOrder(const Order<Type>& o, F f)
{
cout<<"Process order:\t"<<o.amount<<'\t'<<o.sent<<'\t'<<o.risk<<endl;
try{
auto r=f(o);
cout<<"Result order:\t"<<r.amount<<'\t'<<r.sent<<'\t'<<r.risk<<endl;
}catch(const exception& e) {cout<<e.what()<<endl;}
}
int main()
{
ProcessOrder(Order<OrderType::Unknown>{100, false, false}, [](auto o){//нормально отрабатывает
auto o1=Increase(o, 10);
auto o2=SendOrder(o1);
return Decrease(o2, 20);
});
ProcessOrder(Order<OrderType::Unknown>{100, true, false}, [](auto o){//вылетает с исключением в рантайме - плохой входящий ордер для такого алгоритма
auto o1=Increase(o, 10);
auto o2=SendOrder(o1);
return Decrease(o2, 20);
});
ProcessOrder(Order<OrderType::Unknown>{100, false, false}, [](auto o){//некомпилируется - компилятор видит ошибку в бизнес логике алгоритма
auto o1=Decrease(o, 20);
auto o2=SendOrder(o1);
return Increase(o2, 10);
});
}
P.S. В случае использования только известных на момент компиляции (т.е. не загружаемых из БД, а скажем создаваемых в коде) order'ов, все рантайм проверки можно полностью выкинуть из кода и тогда данный подход соответственно превращается из "необязательной дополнительной фичи" в наиболее оптимальное решение со всех точек зрения.