Re: Про типы и логику
От: alex_public  
Дата: 03.03.15 10:27
Оценка: 21 (1)
Здравствуйте, 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'ов, все рантайм проверки можно полностью выкинуть из кода и тогда данный подход соответственно превращается из "необязательной дополнительной фичи" в наиболее оптимальное решение со всех точек зрения.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.