DDD领域驱动设计C++实现案例:订单管理系统

一、DDD核心概念简介

领域驱动设计(Domain-Driven Design)是一种软件开发方法论,强调将业务领域的概念和规则融入软件设计中。核心概念包括:

  • 值对象(Value Object): 无唯一标识,基于属性值判断相等性
  • 实体(Entity): 有唯一标识,其生命周期由聚合根管理
  • 聚合根(Aggregate Root): 聚合的根节点,维护聚合内的一致性
  • 领域服务(Domain Service): 处理跨实体的业务逻辑
  • 仓储(Repository): 提供数据持久化接口

二、订单管理系统领域模型设计

2.1 值对象定义

地址值对象
#include <string>
#include <cstdint>
#include <utility>// 地址值对象 - 无唯一标识,基于属性值相等
class Address {
private:std::string province_;  // 省份std::string city_;      // 城市std::string district_;  // 区/县std::string detail_;    // 详细地址public:// 构造函数 - 确保值对象不可变Address(std::string province, std::string city, std::string district, std::string detail): province_(std::move(province)), city_(std::move(city)), district_(std::move(district)), detail_(std::move(detail)) {}// 仅提供getter,不提供setter,保证不可变性[[nodiscard]] const std::string& GetProvince() const { return province_; }[[nodiscard]] const std::string& GetCity() const { return city_; }[[nodiscard]] const std::string& GetDistrict() const { return district_; }[[nodiscard]] const std::string& GetDetail() const { return detail_; }// 重写相等运算符 - 基于所有属性值判断相等bool operator==(const Address& other) const {return province_ == other.province_ &&city_ == other.city_ &&district_ == other.district_ &&detail_ == other.detail_;}
};
商品值对象
// 商品值对象 - 无唯一标识,代表商品信息
class Product {
private:std::string name_;      // 商品名称double price_;          // 商品单价std::string sku_;       // 商品唯一编码public:Product(std::string name, double price, std::string sku): name_(std::move(name)), price_(price), sku_(std::move(sku)) {}[[nodiscard]] const std::string& GetName() const { return name_; }[[nodiscard]] double GetPrice() const { return price_; }[[nodiscard]] const std::string& GetSku() const { return sku_; }// 基于SKU判断商品是否相同bool operator==(const Product& other) const {return sku_ == other.sku_;}
};

2.2 实体定义

订单项实体
#include <memory>// 订单项实体 - 有唯一标识,属于Order聚合
class OrderItem {
private:uint64_t id_;               // 订单项唯一标识std::shared_ptr<Product> product_;  // 商品信息int quantity_;              // 商品数量double unit_price_;         // 下单时的单价(快照)public:OrderItem(uint64_t id, std::shared_ptr<Product> product, int quantity): id_(id), product_(std::move(product)), quantity_(quantity) {if (quantity <= 0) {throw std::invalid_argument("订单项数量必须大于0");}unit_price_ = product_->GetPrice();  // 记录下单时的价格快照}[[nodiscard]] uint64_t GetId() const { return id_; }[[nodiscard]] const std::shared_ptr<Product>& GetProduct() const { return product_; }[[nodiscard]] int GetQuantity() const { return quantity_; }[[nodiscard]] double GetUnitPrice() const { return unit_price_; }// 计算订单项总价[[nodiscard]] double CalculateTotal() const {return unit_price_ * quantity_;}// 重写相等运算符 - 基于唯一标识判断bool operator==(const OrderItem& other) const {return id_ == other.id_;}
};

2.3 聚合根定义

订单聚合根
#include <vector>
#include <unordered_map>
#include <stdexcept>
#include <algorithm>
#include <chrono>// 订单状态枚举
enum class OrderStatus {CREATED,    // 已创建PAID,       // 已支付SHIPPED,    // 已发货DELIVERED,  // 已送达CANCELLED   // 已取消
};// 订单聚合根 - 维护订单的一致性
class Order {
private:uint64_t id_;                     // 订单唯一标识std::string customer_id_;         // 客户IDstd::vector<OrderItem> items_;    // 订单项集合Address shipping_address_;        // 配送地址OrderStatus status_;              // 订单状态std::chrono::system_clock::time_point create_time_;  // 创建时间// 生成订单项ID的简单实现uint64_t GenerateItemId() const {return items_.empty() ? 1 : items_.back().GetId() + 1;}public:Order(uint64_t id, std::string customer_id, Address shipping_address): id_(id), customer_id_(std::move(customer_id)), shipping_address_(std::move(shipping_address)),status_(OrderStatus::CREATED),create_time_(std::chrono::system_clock::now()) {}// 添加商品到订单 - 领域行为void AddProduct(const std::shared_ptr<Product>& product, int quantity) {// 检查订单状态是否允许添加商品if (status_ != OrderStatus::CREATED) {throw std::logic_error("只有已创建状态的订单可以添加商品");}// 检查数量是否合法if (quantity <= 0) {throw std::invalid_argument("商品数量必须大于0");}// 检查商品是否已存在,存在则更新数量auto it = std::find_if(items_.begin(), items_.end(),[&product](const OrderItem& item) {return *item.GetProduct() == *product;});if (it != items_.end()) {// 在实际实现中,这里应该创建新的OrderItem或提供修改数量的方法throw std::logic_error("当前实现不支持修改订单项数量,请先移除再添加");} else {// 添加新订单项items_.emplace_back(GenerateItemId(), product, quantity);}}// 移除订单项void RemoveItem(uint64_t item_id) {if (status_ != OrderStatus::CREATED) {throw std::logic_error("只有已创建状态的订单可以移除商品");}auto it = std::remove_if(items_.begin(), items_.end(),[item_id](const OrderItem& item) {return item.GetId() == item_id;});if (it == items_.end()) {throw std::invalid_argument("订单项不存在");}items_.erase(it, items_.end());}// 计算订单总价 - 领域行为[[nodiscard]] double CalculateTotalAmount() const {double total = 0;for (const auto& item : items_) {total += item.CalculateTotal();}return total;}// 订单支付 - 状态转换行为void Pay() {if (status_ != OrderStatus::CREATED) {throw std::logic_error("只有已创建状态的订单可以支付");}if (items_.empty()) {throw std::logic_error("空订单不能支付");}status_ = OrderStatus::PAID;}// 订单发货 - 状态转换行为void Ship() {if (status_ != OrderStatus::PAID) {throw std::logic_error("只有已支付状态的订单可以发货");}status_ = OrderStatus::SHIPPED;}// 订单取消 - 状态转换行为void Cancel() {if (status_ != OrderStatus::CREATED && status_ != OrderStatus::PAID) {throw std::logic_error("只有已创建或已支付状态的订单可以取消");}status_ = OrderStatus::CANCELLED;}// 获取订单信息的getter方法[[nodiscard]] uint64_t GetId() const { return id_; }[[nodiscard]] const std::string& GetCustomerId() const { return customer_id_; }[[nodiscard]] const std::vector<OrderItem>& GetItems() const { return items_; }[[nodiscard]] const Address& GetShippingAddress() const { return shipping_address_; }[[nodiscard]] OrderStatus GetStatus() const { return status_; }[[nodiscard]] const std::chrono::system_clock::time_point& GetCreateTime() const { return create_time_; }
};

2.4 领域服务定义

订单领域服务
#include <unordered_map>// 订单领域服务 - 处理跨聚合的业务逻辑
class OrderService {
public:// 合并订单 - 跨聚合根操作std::shared_ptr<Order> MergeOrders(const std::shared_ptr<Order>& order1, const std::shared_ptr<Order>& order2) {// 检查两个订单是否属于同一客户if (order1->GetCustomerId() != order2->GetCustomerId()) {throw std::invalid_argument("只能合并同一客户的订单");}// 检查订单状态if (order1->GetStatus() != OrderStatus::CREATED || order2->GetStatus() != OrderStatus::CREATED) {throw std::invalid_argument("只能合并已创建状态的订单");}// 创建新订单(实际应用中ID通常由仓储生成)auto merged_order = std::make_shared<Order>(0,  // 临时ID,实际应用中由仓储生成order1->GetCustomerId(),order1->GetShippingAddress()  // 使用第一个订单的配送地址);// 添加第一个订单的商品for (const auto& item : order1->GetItems()) {merged_order->AddProduct(item.GetProduct(), item.GetQuantity());}// 添加第二个订单的商品for (const auto& item : order2->GetItems()) {merged_order->AddProduct(item.GetProduct(), item.GetQuantity());}return merged_order;}// 计算客户订单总金额double CalculateCustomerTotalOrders(const std::vector<std::shared_ptr<Order>>& orders) {double total = 0;for (const auto& order : orders) {// 只计算已支付和已发货的订单if (order->GetStatus() == OrderStatus::PAID || order->GetStatus() == OrderStatus::SHIPPED ||order->GetStatus() == OrderStatus::DELIVERED) {total += order->CalculateTotalAmount();}}return total;}
};

2.5 仓储接口定义

订单仓储接口
// 订单仓储接口 - 定义持久化操作
class OrderRepository {
public:virtual ~OrderRepository() = default;// 保存订单virtual void Save(const std::shared_ptr<Order>& order) = 0;// 根据ID获取订单virtual std::shared_ptr<Order> GetById(uint64_t id) = 0;// 获取客户的所有订单virtual std::vector<std::shared_ptr<Order>> GetByCustomerId(const std::string& customer_id) = 0;// 更新订单virtual void Update(const std::shared_ptr<Order>& order) = 0;// 删除订单virtual void Delete(uint64_t id) = 0;
};

2.6 仓储实现示例

#include <unordered_map>
#include <mutex>// 内存订单仓储实现 - 实际应用中可能是数据库实现
class InMemoryOrderRepository : public OrderRepository {
private:std::unordered_map<uint64_t, std::shared_ptr<Order>> orders_;uint64_t next_id_ = 1;std::mutex mutex_;public:void Save(const std::shared_ptr<Order>& order) override {std::lock_guard<std::mutex> lock(mutex_);// 如果是新订单,分配IDif (order->GetId() == 0) {// 在实际应用中,这里应该通过反射或工厂方法创建新订单对象// 为简化示例,我们假设可以修改订单ID// 注意:在纯正的DDD中,聚合根的ID通常在创建时确定auto new_order = std::make_shared<Order>(next_id_++,order->GetCustomerId(),order->GetShippingAddress());for (const auto& item : order->GetItems()) {new_order->AddProduct(item.GetProduct(), item.GetQuantity());}orders_[new_order->GetId()] = new_order;} else {orders_[order->GetId()] = order;}}std::shared_ptr<Order> GetById(uint64_t id) override {std::lock_guard<std::mutex> lock(mutex_);auto it = orders_.find(id);if (it != orders_.end()) {return it->second;}return nullptr;}std::vector<std::shared_ptr<Order>> GetByCustomerId(const std::string& customer_id) override {std::lock_guard<std::mutex> lock(mutex_);std::vector<std::shared_ptr<Order>> result;for (const auto& pair : orders_) {if (pair.second->GetCustomerId() == customer_id) {result.push_back(pair.second);}}return result;}void Update(const std::shared_ptr<Order>& order) override {Save(order);  // 对于内存仓储,Save和Update可以实现为同一操作}void Delete(uint64_t id) override {std::lock_guard<std::mutex> lock(mutex_);orders_.erase(id);}
};

2.7 应用服务定义

// 订单应用服务 - 协调领域对象完成业务功能
class OrderAppService {
private:std::unique_ptr<OrderRepository> order_repository_;OrderService order_service_;public:explicit OrderAppService(std::unique_ptr<OrderRepository> repository): order_repository_(std::move(repository)) {}// 创建订单uint64_t CreateOrder(const std::string& customer_id, const Address& shipping_address) {auto order = std::make_shared<Order>(0, customer_id, shipping_address);order_repository_->Save(order);return order->GetId();}// 添加商品到订单void AddProductToOrder(uint64_t order_id, const std::shared_ptr<Product>& product, int quantity) {auto order = order_repository_->GetById(order_id);if (!order) {throw std::invalid_argument("订单不存在");}order->AddProduct(product, quantity);order_repository_->Update(order);}// 提交订单(支付)void PayOrder(uint64_t order_id) {auto order = order_repository_->GetById(order_id);if (!order) {throw std::invalid_argument("订单不存在");}order->Pay();order_repository_->Update(order);}// 合并客户的两个订单uint64_t MergeCustomerOrders(uint64_t order_id1, uint64_t order_id2) {auto order1 = order_repository_->GetById(order_id1);auto order2 = order_repository_->GetById(order_id2);if (!order1 || !order2) {throw std::invalid_argument("订单不存在");}auto merged_order = order_service_.MergeOrders(order1, order2);order_repository_->Save(merged_order);// 删除原订单order_repository_->Delete(order_id1);order_repository_->Delete(order_id2);return merged_order->GetId();}// 获取客户订单总金额double GetCustomerTotalSpent(const std::string& customer_id) {auto orders = order_repository_->GetByCustomerId(customer_id);return order_service_.CalculateCustomerTotalOrders(orders);}
};

三、客户端使用示例

#include <iostream>void RunExample() {// 创建仓储auto repository = std::make_unique<InMemoryOrderRepository>();OrderAppService app_service(std::move(repository));// 创建地址Address shipping_address("广东省", "深圳市", "南山区", "科技园路100号");// 创建订单uint64_t order_id = app_service.CreateOrder("customer_123", shipping_address);std::cout << "创建订单成功,订单ID: " << order_id << std::endl;// 创建商品auto product1 = std::make_shared<Product>("C++编程思想", 89.0, "book_001");auto product2 = std::make_shared<Product>("DDD实战", 79.0, "book_002");// 添加商品到订单app_service.AddProductToOrder(order_id, product1, 1);app_service.AddProductToOrder(order_id, product2, 2);std::cout << "添加商品到订单成功" << std::endl;// 支付订单app_service.PayOrder(order_id);std::cout << "订单支付成功" << std::endl;// 查询客户总消费double total = app_service.GetCustomerTotalSpent("customer_123");std::cout << "客户总消费金额: " << total << "元" << std::endl;
}int main() {try {RunExample();} catch (const std::exception& e) {std::cerr << "发生错误: " << e.what() << std::endl;return 1;}return 0;
}

四、DDD概念在案例中的体现

  1. 限界上下文(Bounded Context): 整个订单管理系统构成一个限界上下文,包含了订单相关的所有领域对象

  2. 值对象(Value Object):

    • Address: 表示地址信息,无唯一标识,基于内容判断相等
    • Product: 表示商品信息,基于SKU判断相等性,是不可变对象
  3. 实体(Entity):

    • OrderItem: 有唯一ID,生命周期由Order聚合根管理
    • 实体的标识在整个系统中唯一,不受属性变化影响
  4. 聚合根(Aggregate Root):

    • Order: 作为聚合根,维护订单项集合的一致性
    • 提供了添加/移除商品、支付、发货等领域行为
    • 确保订单状态转换的业务规则得到遵守
  5. 领域服务(Domain Service):

    • OrderService: 处理跨聚合的业务逻辑(订单合并)
    • 包含领域知识,但不适合放在单个聚合根中的操作
  6. 仓储(Repository):

    • OrderRepository: 抽象订单的持久化操作
    • InMemoryOrderRepository: 具体实现,隔离领域层与数据访问层
  7. 领域事件(Domain Event): 案例中未直接实现,但可以扩展添加,如订单支付后发布OrderPaid事件

五、总结

本案例通过一个简单的订单管理系统展示了DDD的核心概念和设计思想:

1.** 以领域为中心 : 设计围绕订单业务领域的概念和规则展开
2.
面向对象设计 : 将业务行为封装在领域对象中,而非过程式的服务中
3.
关注业务规则 : 通过聚合根确保业务规则(如订单状态转换)得到遵守
4.
分层架构 : 领域层、应用层、基础设施层清晰分离
5.
隔离技术细节 **: 通过仓储接口隔离领域层与数据访问技术

在实际项目中,DDD的应用往往更加复杂,可能还会涉及领域事件、事件溯源、CQRS等模式,但本案例展示的核心概念是DDD的基础。通过将业务领域模型化,团队可以更好地与领域专家沟通,构建出更符合业务需求、更易维护的软件系统。## 六、DDD架构设计

6.1 DDD分层架构

DDD推荐采用分层架构,将系统划分为不同职责的层次,各层之间通过明确的接口交互。以下是典型的DDD四层架构:

DDD四层架构图

各层职责:

  • 表示层(Presentation Layer):处理用户交互和请求响应,如API接口、UI界面
  • 应用层(Application Layer):协调领域对象执行业务操作,不包含业务逻辑
  • 领域层(Domain Layer):核心层,包含业务模型、规则和逻辑
  • 基础设施层(Infrastructure Layer):提供技术支持,如数据库访问、消息队列等

在我们的订单管理系统中:

  • 表示层:未直接实现,可对应API控制器或UI界面
  • 应用层:OrderAppService类,协调领域对象完成业务功能
  • 领域层:OrderOrderItemProduct等领域对象和OrderService领域服务
  • 基础设施层:OrderRepository接口及InMemoryOrderRepository实现

6.2 领域事件

领域事件是DDD中一个重要概念,用于捕获领域中发生的重要事件并通知其他组件。例如:

// 订单支付事件示例
class OrderPaidEvent {
private:uint64_t order_id_;std::string customer_id_;double amount_;std::chrono::system_clock::time_point paid_time_;public:OrderPaidEvent(uint64_t order_id, std::string customer_id, double amount): order_id_(order_id), customer_id_(std::move(customer_id)), amount_(amount), paid_time_(std::chrono::system_clock::now()) {}// Getter方法...
};// 在Order类的Pay方法中发布事件
void Pay() {// ... 现有支付逻辑 ...// 发布订单支付事件DomainEventPublisher::Publish(std::make_shared<OrderPaidEvent>(id_, customer_id_, CalculateTotalAmount()));
}

领域事件可用于实现跨聚合通信、集成其他系统(如支付系统、物流系统)等,提高系统的解耦度。

七、使用DDD的优化点

7.1 业务与技术分离

DDD将业务逻辑集中在领域层,与技术实现分离。在订单管理系统中,订单的状态转换、价格计算等核心业务规则封装在Order类中,不依赖于数据库访问、UI框架等技术细节。

优势:

  • 业务逻辑更清晰,易于理解和维护
  • 技术变更(如更换数据库)不影响领域模型
  • 领域模型可独立测试,无需依赖外部系统

7.2 领域知识显性化

DDD鼓励创建与业务语言一致的模型,使领域知识直接体现在代码中。例如:

  • Order::Pay()方法对应"订单支付"业务动作
  • OrderStatus枚举反映业务中的订单状态流转
  • OrderService::MergeOrders()实现"合并订单"业务场景

这种显性化使开发人员和领域专家能使用共同语言沟通,减少理解偏差。

7.3 维护数据一致性

聚合根模式确保了数据的一致性。Order作为聚合根,控制着订单项的添加、移除和订单状态的转换,防止出现无效状态(如空订单支付、已发货订单添加商品等)。

7.4 提高代码复用性

领域模型抽象了业务概念,可在不同场景中复用。例如Product值对象可用于订单系统、库存系统、购物车系统等多个模块。

7.5 更好的可扩展性

DDD的分层架构和领域模型设计使系统更易于扩展:

  • 新增业务功能时,只需扩展领域模型或添加新的领域服务
  • 通过限界上下文隔离不同业务模块,减少相互影响
  • 领域事件机制支持松耦合的扩展

八、不使用DDD的问题

8.1 业务逻辑分散

传统开发中,业务逻辑常分散在服务层、控制器甚至UI层,形成"贫血模型"。例如:

// 传统方式:业务逻辑分散在服务中
class OrderService {
public:double CalculateTotal(OrderDTO order) {double total = 0;for (auto& item : order.items) {total += item.price * item.quantity;}return total;}void PayOrder(uint64_t order_id) {// 查询订单、更新状态等逻辑}// 更多业务方法...
};

这种方式导致业务规则难以追踪和维护,需求变更时需修改多个地方。

8.2 技术驱动设计

缺乏领域模型时,开发往往以数据库表结构为中心(数据库驱动设计),导致代码与业务脱节。例如,为了适应数据库关系而设计的类结构,可能无法反映真实业务概念。

8.3 难以应对复杂业务

对于复杂业务规则,没有领域模型的支撑会导致代码变得混乱:

  • 条件判断嵌套复杂
  • 业务规则隐藏在过程式代码中
  • 难以进行单元测试
  • 团队协作困难,代码冲突频繁

8.4 系统刚性

传统架构缺乏明确边界,模块间依赖紧密,一处修改可能引发多处问题。例如,修改订单状态字段可能影响到订单查询、统计、报表等多个功能。

九、DDD适用场景与注意事项

9.1 适合DDD的场景

  • 复杂业务领域:如金融、电商、物流等业务规则复杂的系统
  • 长期演进系统:需要持续迭代、扩展的大型应用
  • 团队协作开发:多团队、多角色参与的项目
  • 业务价值优先:业务逻辑对系统成功至关重要的项目

9.2 DDD的局限性

  • 学习曲线陡峭:需要团队理解领域驱动设计的概念和原则
  • 前期投入大:领域建模需要与领域专家深入沟通
  • 不适合简单系统:对于CRUD为主的简单应用,可能增加不必要的复杂度
  • 需要持续重构:领域模型需要随业务发展不断优化

9.3 实施建议

  1. 从小处着手:选择核心业务模块先行实施DDD
  2. 领域专家参与:确保开发人员与领域专家密切合作
  3. 持续建模:通过事件风暴、示例驱动等方法不断完善模型
  4. 避免过度设计:根据业务复杂度调整DDD实践的深度
  5. 自动化测试:为领域模型编写全面的单元测试

十、总结与扩展

本案例通过订单管理系统展示了DDD的核心概念和实践方法。从值对象、实体到聚合根,从领域服务到仓储,每个组件都有其明确的职责和设计意图。DDD不是银弹,但在复杂业务系统中,它提供了一套有效的方法论,帮助团队构建出更符合业务需求、更易维护的软件。

扩展方向

  1. 事件溯源(Event Sourcing):通过记录领域事件来重建对象状态,适合审计、溯源需求
  2. CQRS:命令查询职责分离,优化读写性能
  3. 微服务与DDD:结合限界上下文设计微服务边界
  4. 领域驱动设计工具:使用事件风暴工具、领域模型可视化工具等提升建模效率

要真正掌握DDD,需要在实践中不断学习和调整。建议结合具体业务场景,逐步应用DDD原则,而非教条式地套用所有模式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/90137.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/90137.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

神经网络和机器学习的一些基本概念

记录一些基本概念,不涉及公式推导,因为数学不好,记了也没啥用,但是知道一些基本术语以及其中的关系,对神经网络训练有很大帮助。 可能有些概念不会讲得很详细,但是当你有了这个概念,你就知道往这个方向去获取更详细的信息,不至于连往哪走都不知道。 下面以多元线性回归…

MySQL(146) 如何迁移数据库到新服务器?

数据库迁移到新服务器是一项复杂而重要的任务&#xff0c;确保数据完整性和最小化停机时间至关重要。以下是一个详细的步骤指导&#xff0c;包括准备工作、数据备份、数据传输、数据恢复和验证的全过程。 一、准备工作 1. 确认服务器环境 源服务器&#xff1a;当前运行数据库的…

图论的整合

图 有若干个节点&#xff0c;有若干条边连接节点。&#xff08;两个点之间不是必须相连&#xff09; 比如&#xff1a; 有向图 可以理解为边上面有箭头的图&#xff0c;比如下面这张图&#xff1a; 在这张图中&#xff0c;点 111 可以通过这条有向边到达点 222&#xff0c…

电子设计大赛【C语言核心知识点】讲解

目录 前言 1. 基础语法 2. 流程控制 3. 函数 4. 数组与字符串 5. 指针&#xff08;核心重点&#xff09; 6. 内存管理 7. 结构体与联合体 8. 文件操作 9. 预处理器 10. 高级特性 内存布局图解 前言 在进行程序代码开发之前&#xff0c;需要掌握好C语言各个模块之间…

Numpy 库 矩阵数学运算,点积,文件读取和保存等

目录 1.数组&#xff08;矩阵&#xff09;的组合 2.数组&#xff08;矩阵&#xff09;的切割 3.数组的数学运算 4.数组的深拷贝和浅拷贝 5.随机模块 6.矩阵统计运算 7.矩阵的特有运算点积&#xff0c;求逆 8.文件读取和保存 1.数组&#xff08;矩阵&#xff09;的组合 水…

STL学习(?函数对象,谓词,内建函数对象)

目录 一、函数对象 1.函数对象的概念 2.函数对象的使用 &#xff08;1&#xff09;函数对象在使用的时候&#xff0c;可以像普通函数那样调用&#xff0c;可以有参数&#xff0c;也可以有返回值。 &#xff08;2&#xff09;函数对象超出普通函数的概念&#xff0c;函数对象…

【爬虫】05 - 爬虫攻防

爬虫05 - 爬虫攻防 文章目录爬虫05 - 爬虫攻防一&#xff1a;随机User-Agent爬虫1&#xff1a;fake-useragent2&#xff1a;高级反反爬策略3&#xff1a;生产环境建议二&#xff1a;代理IP爬虫1&#xff1a;获取代理IP2&#xff1a;高阶攻防3&#xff1a;企业级的代理实战三&am…

FPGA自学——存储器模型

FPGA自学——存储器模型 文章目录FPGA自学——存储器模型一、IP核二、ROM&#xff08;read only memory&#xff09;三、ROM的IP核调用四、RAM&#xff08;random access memory&#xff09;五、RAM的IP核调用总结1.不同波形的使用的存储器2.块与分布式的选择3.FPGA与模块的容量…

【C++】stack和queue拓展学习

目录 1.反向迭代器思路及实现 1.1. 源码及框架分析 1.2. 实现反向迭代器 2.stack和queue练习拓展-计算器实现 2.1. 后缀表达式概念 2.2. 后缀表达式运算规则 2.3. 中缀表达式转后缀表达式 2.3.1 转换思路 2.3.2 代码实现 2.4. 计算器实现 1.反向迭代器思路及实现 1.1…

Web3与区块链如何革新网络安全——走在前沿

随着互联网技术的飞速发展&#xff0c;网络安全问题日益成为全球关注的焦点。Web3和区块链技术作为新兴的技术力量&#xff0c;正在逐步改变网络安全的格局。本文将探讨Web3和区块链技术如何革新网络安全&#xff0c;走在技术前沿。 1. Web3技术概述 Web3&#xff0c;即第三代互…

网络初级安全第三次作业

<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>用户登录</title><style>* {margin:…

CSS中用display实现元素的显示/隐藏切换

** 通过display中的none和block ** 在前端开发中&#xff0c;display: none 和 display: block 是两种常用的 CSS 显示模式&#xff0c;核心区别在于&#xff1a;是否在页面中保留元素的占位空间 1. 核心区别属性display: nonedisplay: block占位空间元素完全从渲染树中移除&am…

因果图方法设计测试用例的价值与使用范围

一、因果图方法的核心原理 因果图方法通过分析软件规格说明中的输入条件&#xff08;因&#xff09;和输出结果&#xff08;果&#xff09;之间的逻辑关系&#xff0c;利用图形化方式将这些关系清晰展现。它使用特定的符号表示因果关系&#xff08;如恒等、非、或、与&#xff…

智慧农服数字化平台-数字科技赋能农业,开启智慧三农新篇章

智慧农服数字化平台数字科技赋能农业&#xff0c;开启智慧三农新篇章平台概览在乡村振兴和农业现代化的时代背景下&#xff0c;我们推出了创新的农业服务数字化平台——一个专为农业生产者打造的综合性SaaS服务平台。平台以"科技助农、数据兴农"为使命&#xff0c;通…

在线教育培训课程视频如何防下载、防盗录?

在数字化学习日益普及的今天&#xff0c;高质量的在线课程已成为教育机构、知识付费平台和讲师的核心竞争力。如何在不影响学员正常学习体验的前提下&#xff0c;有效防止课程视频被恶意盗取&#xff1f;今天介绍在线教育课程防下载、防盗录的10种视频加密方法&#xff0c;看看…

图像分析学习笔记(2):图像处理基础

图像分析学习笔记&#xff1a;图像处理基础图像增强方法图像复原方法图像分割方法形态学处理图像增强方法 目的&#xff1a;改善视觉效果&#xff0c;例如增强对比度定义&#xff1a;为了改善视觉效果、便于人或计算机对图像的分析理解&#xff0c;针对图像的特点或存在的问题…

生存分析机器学习问题

研究目标&#xff1a; 开发一个机器学习模型&#xff0c;用于个性化预测XXX的总体生存期。 模型输入&#xff1a;结合生存时间、治疗方案、人口统计学特征和实验室测试结果等多种特征。 模型输出&#xff1a;预测二元结果&#xff08;活着 vs. 死亡&#xff09;。 应用场景&…

【华为机试】547. 省份数量

文章目录547. 省份数量描述示例 1示例 2提示解题思路核心分析问题转化算法选择策略1. 深度优先搜索 (DFS)2. 广度优先搜索 (BFS)3. 并查集 (Union-Find)算法实现详解方法一&#xff1a;深度优先搜索 (DFS)方法二&#xff1a;广度优先搜索 (BFS)方法三&#xff1a;并查集 (Union…

09_Spring Boot 整合 Freemarker 模板引擎的坑

09_Spring Boot 整合 Freemarker 模板引擎的坑 1.背景&#xff1a; springboot 版本&#xff1a;3.0.2 2. 引入依赖 在 pom.xml 中添加&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web<…

十七、【Linux系统yum仓库管理】替换阿里源、搭建本地yum源

替换阿里源、搭建本地yum源本章学习目标内容简介阿里外网源核心功能本地yum核心功能操作演示替换阿里外网源备份原有yum源清理冲突配置下载阿里源配置文件添加EPEL扩展源清理缓存重建索引验证源状态测试安装软件使用镜像搭建本地仓库准备ISO镜像创建挂载点目录挂载iso文件验证挂…