Comments 13
так получается книга ценная, её можно повторить и сделать - движок сущностей, прикрутить отрисовку. как я понял в каждой сущности еще может быть своя логика.
а если по стандарту идти то это сущности для магазина
use serde::Deserialize;
use chrono::NaiveDate;
#[derive(Deserialize)]
#[serde(tag = "type")]
pub enum Command {
CreateBatch {
#[serde(rename = "ref")]
ref_: String,
sku: String,
qty: usize,
#[serde(default)]
eta: Option<NaiveDate>,
},
Allocate {
orderid: String,
sku: String,
qty: usize,
},
ChangeBatchQuantity {
batchref: String,
qty: usize,
},
}
impl Command {
pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
serde_json::from_str(json)
}
pub fn validate(&self) -> Result<(), String> {
match self {
Command::CreateBatch { qty, ref_, sku, .. } => {
if ref_.is_empty() { return Err("ref is empty".into()); }
if sku.is_empty() { return Err("sku is empty".into()); }
if *qty == 0 { return Err("qty must be > 0".into()); }
}
Command::Allocate { orderid, sku, qty } => {
if orderid.is_empty() { return Err("orderid is empty".into()); }
if sku.is_empty() { return Err("sku is empty".into()); }
if *qty == 0 { return Err("qty must be > 0".into()); }
}
Command::ChangeBatchQuantity { batchref, qty } => {
if batchref.is_empty() { return Err("batchref is empty".into()); }
if *qty == 0 { return Err("qty must be > 0".into()); }
}
}
Ok(())
}
}Rust значительно компактней - это замена 6 файлов без большого ущерба для расширяемости и изолированности за счёт:
Enum с данными - заменяет иерархию классов
Serde - кодогенерация парсинга вместо ручного
getValue<T>()Pattern matching - вместо виртуальных методов
Нет header/cpp split - всё в одном файле
В C++ нужно:
IMessage.hpp(базовый интерфейс)CreateBatch.hpp/cpp,Allocate.hpp/cpp,ChangeBatchQuantity.hpp/cpp(3×2=6 файлов)FromJson.hpp(специализации шаблонов)Poco/nlohmann для парсинга
В Rust: 1 файл, ~50 строк.
Расширяемость не хуже - добавить вариант enum = добавить класс в C++. Компилятор форсит обработать все ветки в match.
Изоляция та же - можно вынести варианты в отдельные модули если разрастутся, но обычно не нужно.
Зря вы перенесли идиоматику Python на C++ - там своя эргономика и экономика кода
Конкретная реализация команд через иерархию классов - это питоновский стиль, где классы дешевые и нужны для типизации.
На плюсах достаточно
enum class CommandType { CreateBatch, Allocate, ChangeBatchQuantity };
struct Command {
CommandType type;
std::string ref, sku, orderid, batchref;
size_t qty;
std::optional<Date> eta;
};
Command parse_command(const json& j) {
Command cmd;
cmd.type = parse_type(j["type"]);
cmd.sku = j.value("sku", "");
// ... остальные поля
return cmd;
}Валидация - отдельная функция или в конструкторе.
Вместо 10+ файлов - 2 файла (hpp/cpp).
Иерархия классов с виртуальными методами нужна только если реально планируется runtime полиморфизм (например, плагины). Для простого парсинга команд - overkill.
у раста как будто всё приводится через байт код к флай обджектам ) это подкупает, но пофиг
Так (несколько if) можно забыть обработать одно из состояний и я сталкивался с логическими ошибками, которые очень трудно поймать и решены были полным переписыванием кода на честную реализацию конечных автоматов.
там суть в том что под капотом на уровнее ллвм тот же код может, быть, но это звучит очень оптимистично, ну тот код что я предложил не идеальный, да придётся подебажиться, но близок без кодогонерации к подходу раста, но на простейшем уровне как мне кажется
этоже только валидация, а будут последовательности, в С++ есть всё что надо для работы с памятью я думаю это точно, и в расте тоже
можно даже пару трейтов из раста перенести в С++ 26 и вы увидите приколдесы ) наверное, но суть не в переносе трейтов, суть в том что это возрождает какой-то другой подход к кодингу, где нету мега больших классов наверное, или если есть он делает свою работу наверное может так как-то
тоесть шейдер, и обьекты, обьекты могут быть флай обджекты, а шейдер 1 мега класс
или структура которая наполняется от последовательности, ну вообщем суть в том что раст как не крути по той же логике работает поидее
testcolBB1.cpp и всё сводится к последовательности указателей, поидее то что делает раст
тоесть раст это просто компилятор со своим языком, который исключает или автоматизирует эту работу наверное
Вы забыли картинку превью отмасштабировать. Вылазит за рамки
В указанной автором статьи книге авторов Гарри Персиваль, Боб Грегори в итоговой общей схеме архитектуры почему-то выпала связь между блоком обработчиков (handlers) и предметной областью (domain). Эта связь многократно указана в книге - например в рисунке 7.5. Но в итоговой схеме её нет. Если приложение содержит функционал домена (предметную область), то нельзя обойтись без связки handlers -> domain.
Благодарю за комментарий! Возможно, это было сделано намеренно - в итоговой схеме также отсутствуют сообщения, используемые в шине сообщений. С другой стороны, действительно можно было бы показать связь между сервисным слоем и доменом, чтобы подчеркнуть их взаимодействие. Однако я старался реализовать проект максимально близко к оригиналу, чтобы упростить освоение материала книги. Пока не могу однозначно сказать, стоит ли добавлять такую связь - буду признателен за мнение сообщества.
Исправлено, спасибо.
Реализация сервиса на C++: TDD, DDD и событийно-ориентированная архитектура