Pull to refresh

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 файлов без большого ущерба для расширяемости и изолированности за счёт:

  1. Enum с данными - заменяет иерархию классов

  2. Serde - кодогенерация парсинга вместо ручного getValue<T>()

  3. Pattern matching - вместо виртуальных методов

  4. Нет header/cpp split - всё в одном файле

В C++ нужно:

  • IMessage.hpp (базовый интерфейс)

  • CreateBatch.hpp/cppAllocate.hpp/cppChangeBatchQuantity.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.

у раста как будто всё приводится через байт код к флай обджектам ) это подкупает, но пофиг

h689fGfnG почти тоже самое вроде, ну с нюансами как я понял

Так (несколько if) можно забыть обработать одно из состояний и я сталкивался с логическими ошибками, которые очень трудно поймать и решены были полным переписыванием кода на честную реализацию конечных автоматов.

там суть в том что под капотом на уровнее ллвм тот же код может, быть, но это звучит очень оптимистично, ну тот код что я предложил не идеальный, да придётся подебажиться, но близок без кодогонерации к подходу раста, но на простейшем уровне как мне кажется

этоже только валидация, а будут последовательности, в С++ есть всё что надо для работы с памятью я думаю это точно, и в расте тоже

можно даже пару трейтов из раста перенести в С++ 26 и вы увидите приколдесы ) наверное, но суть не в переносе трейтов, суть в том что это возрождает какой-то другой подход к кодингу, где нету мега больших классов наверное, или если есть он делает свою работу наверное может так как-то

тоесть шейдер, и обьекты, обьекты могут быть флай обджекты, а шейдер 1 мега класс

или структура которая наполняется от последовательности, ну вообщем суть в том что раст как не крути по той же логике работает поидее

testcolBB1.cpp и всё сводится к последовательности указателей, поидее то что делает раст

тоесть раст это просто компилятор со своим языком, который исключает или автоматизирует эту работу наверное

3Gfc7rqrn как-то так можно сделать, но конечно уже нагрузно читать наверное

Вы забыли картинку превью отмасштабировать. Вылазит за рамки

В указанной автором статьи книге авторов Гарри Персиваль, Боб Грегори в итоговой общей схеме архитектуры почему-то выпала связь между блоком обработчиков (handlers) и предметной областью (domain). Эта связь многократно указана в книге - например в рисунке 7.5. Но в итоговой схеме её нет. Если приложение содержит функционал домена (предметную область), то нельзя обойтись без связки handlers -> domain.

Благодарю за комментарий! Возможно, это было сделано намеренно - в итоговой схеме также отсутствуют сообщения, используемые в шине сообщений. С другой стороны, действительно можно было бы показать связь между сервисным слоем и доменом, чтобы подчеркнуть их взаимодействие. Однако я старался реализовать проект максимально близко к оригиналу, чтобы упростить освоение материала книги. Пока не могу однозначно сказать, стоит ли добавлять такую связь - буду признателен за мнение сообщества.

Если приложение содержит доменную логику, то такая связь должна быть. Если этой связи нет, то встречный вопрос - кто будет запускать алгоритмы доменной логики?

Sign up to leave a comment.

Articles