Solidity入门(11)-智能合约设计模式2

文章目录

  • [5. 代理模式](#5. 代理模式)
    • [5.1 为什么需要代理模式](#5.1 为什么需要代理模式)
    • [5.2 代理模式架构](#5.2 代理模式架构)
    • [5.3 存储布局兼容性](#5.3 存储布局兼容性)
  • [6. 工厂模式](#6. 工厂模式)
    • [6.1 为什么需要工厂模式](#6.1 为什么需要工厂模式)
    • [6.2 基础工厂实现](#6.2 基础工厂实现)
    • [6.3 Clone工厂模式(EIP-1167)](#6.3 Clone工厂模式(EIP-1167))
  • [7. 紧急停止模式](#7. 紧急停止模式)
    • [7.1 为什么需要紧急停止](#7.1 为什么需要紧急停止)
    • [7.2 OpenZeppelin的Pausable实现](#7.2 OpenZeppelin的Pausable实现)
    • [7.3 最佳实践](#7.3 最佳实践)
  • [8. 模式对比与选择指南](#8. 模式对比与选择指南)
    • [8.1 模式对比表](#8.1 模式对比表)
    • [8.2 选择指南](#8.2 选择指南)
    • [8.3 模式组合建议](#8.3 模式组合建议)
  • [9. 模式组合应用案例](#9. 模式组合应用案例)
    • [9.1 DeFi借贷协议](#9.1 DeFi借贷协议)
  • [9.2 NFT交易市场](#9.2 NFT交易市场)
    • [9.3 DAO治理系统](#9.3 DAO治理系统)

5. 代理模式

代理模式是实现合约升级的核心方案。通过分离数据存储和业务逻辑,我们可以在不改变合约地址的情况下升级业务逻辑。

5.1 为什么需要代理模式

我们都知道,智能合约部署后代码是不可修改的。但在实际项目中,我们经常需要修复Bug或者添加新功能。这就产生了一个矛盾:合约的不可变性与升级需求之间的矛盾。

传统方式的局限性:

bash 复制代码
// 传统方式:合约不可升级
contract Token {
    mapping(address => uint256) public balances;
    
    function transfer(address to, uint256 amount) public {
        // 如果这里发现Bug,无法修复!
        balances[msg.sender] -= amount;
        balances[to] += amount;
    }
}

问题:

  • 发现Bug无法修复
  • 无法添加新功能
  • 只能部署新合约,但地址会改变
  • 用户需要迁移到新合约

5.2 代理模式架构

代理模式通过分离数据存储和业务逻辑来解决这个问题。

架构组成:

  • 代理合约(Proxy):

地址保持不变,用户始终与这个地址交互

只负责存储数据和管理升级

不包含业务逻辑

  • 逻辑合约(Implementation):

包含所有的业务逻辑

可以部署多个版本(V1、V2、V3...)

通过升级切换版本

工作原理:

bash 复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

// 简单的代理合约
contract SimpleProxy {
    // 逻辑合约地址
    address public implementation;
    
    // 管理员地址
    address public admin;
    
    // 数据存储(与逻辑合约的存储布局必须一致)
    uint256 public value;
    
    /**
     * @notice 构造函数:初始化逻辑合约地址
     * @param _implementation 逻辑合约地址
     */
    constructor(address _implementation) {
        admin = msg.sender;
        implementation = _implementation;
    }
    
    /**
     * @notice onlyAdmin修饰符
     */
    modifier onlyAdmin() {
        require(msg.sender == admin, "Not admin");
        _;
    }
    
    /**
     * @notice 升级函数:更换逻辑合约
     * @param newImplementation 新的逻辑合约地址
     * @dev 只有admin可以调用
     */
    function upgrade(address newImplementation) external onlyAdmin {
        implementation = newImplementation;
    }
    
    /**
     * @notice fallback函数:将所有调用转发到逻辑合约
     * @dev 使用delegatecall调用逻辑合约
     */
    fallback() external payable {
        address impl = implementation;
        require(impl != address(0), "Implementation not set");
        
        // 使用delegatecall调用逻辑合约
        // delegatecall的特性:
        // 1. 代码在Implementation中执行
        // 2. 但使用的storage是Proxy的
        // 3. msg.sender保持不变(是原始调用者)
        assembly {
            calldatacopy(0, 0, calldatasize())
            let result := delegatecall(gas(), impl, 0, calldatasize(), 0, 0)
            returndatacopy(0, 0, returndatasize())
            switch result
            case 0 { revert(0, returndatasize()) }
            default { return(0, returndatasize()) }
        }
    }
    
    // 接收以太币
    receive() external payable {}
}

V1逻辑合约:

bash 复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

// V1逻辑合约:初始版本
contract ImplementationV1 {
    // 注意:存储布局必须与Proxy完全一致!
    address public implementation;  // 对应Proxy的implementation
    address public admin;            // 对应Proxy的admin
    uint256 public value;            // 对应Proxy的value
    
    /**
     * @notice 设置值
     * @param _value 要设置的值
     * @dev 这个函数会修改Proxy的storage,不是本合约的
     */
    function setValue(uint256 _value) public {
        value = _value;
    }
    
    /**
     * @notice 获取值
     */
    function getValue() public view returns (uint256) {
        return value;
    }
}

V2逻辑合约(升级版本):

bash 复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

// V2逻辑合约:升级版本(新增功能)
contract ImplementationV2 {
    // 存储布局必须与V1和Proxy完全一致!
    address public implementation;
    address public admin;
    uint256 public value;
    
    // 新增变量只能在末尾添加
    uint256 public multiplier;
    
    /**
     * @notice 设置值(新逻辑:值翻倍)
     * @param _value 要设置的值
     * @dev 新逻辑:值会自动翻倍
     */
    function setValue(uint256 _value) public {
        value = _value * (multiplier == 0 ? 1 : multiplier);
    }
    
    /**
     * @notice 获取值
     */
    function getValue() public view returns (uint256) {
        return value;
    }
    
    /**
     * @notice 设置倍数(V2新增功能)
     * @param _multiplier 倍数
     * @dev V1没有这个函数,升级后可以使用
     */
    function setMultiplier(uint256 _multiplier) public {
        multiplier = _multiplier;
    }
}

执行流程:

bash 复制代码
用户调用 Proxy.setValue(50)
    ↓
Proxy的fallback函数被触发(因为Proxy没有setValue函数)
    ↓
fallback函数使用delegatecall调用 Implementation.setValue(50)
    ↓
Implementation的代码在Proxy的上下文中执行
    ↓
修改的是Proxy的value(不是Implementation的)
    ↓
msg.sender仍然是原始用户(不是Proxy)

升级流程:

bash 复制代码
V1时期:
- Proxy.value = 0
- 调用setValue(50) → Proxy.value = 50(V1逻辑:直接赋值)

升级到V2:
- upgrade(V2地址) → 逻辑切换,但Proxy.value保持50

V2时期:
- 调用setValue(50) → Proxy.value = 100(V2逻辑:50*2=100)
- 调用setMultiplier(3) → multiplier = 3(V2新功能)

5.3 存储布局兼容性

使用代理模式有一个关键的要求:存储布局必须保持兼容。

存储布局规则:

  • 不能改变已有变量的类型和顺序:
bash 复制代码
// V1
uint256 public value;
address public owner;

// V2(错误!)
address public value;  // 类型改变,会导致数据错乱
uint256 public owner;
  • 只能在末尾添加新变量:
bash 复制代码
// V1
uint256 public value;

// V2(正确)
uint256 public value;
uint256 public multiplier;  // 在末尾添加
  • 不能删除变量:
bash 复制代码
// V1
uint256 public value;
uint256 public oldValue;

// V2(错误!)
uint256 public value;
// oldValue被删除,会导致存储槽错乱

存储布局冲突的后果:

如果存储布局不兼容,会导致:

  • 数据错乱
  • 变量值被覆盖
  • 合约功能异常
  • 用户资金损失

最佳实践:

  • 使用存储槽编号注释
  • 充分测试升级过程
  • 使用OpenZeppelin的升级代理(UUPS或Transparent)

6. 工厂模式

工厂模式用于批量部署相同类型的合约。这个模式在需要创建多个合约实例的场景中非常有用,还能大幅降低部署成本。

6.1 为什么需要工厂模式

我们来看几个实际场景:

Uniswap:

  • 需要为每个交易对创建一个Pair合约
  • ETH/USDT一个合约,ETH/DAI又是一个合约
  • 需要统一管理和批量创建

NFT市场:

  • 需要为每个创作者的集合创建独立的合约
  • 每个NFT项目都有自己的合约
  • 需要统一管理

多签钱包:

  • 每个团队需要自己的多签钱包
  • 需要批量创建和管理

传统方式的问题:

bash 复制代码
// 传统方式:每次都要单独部署
contract Token {
    // 部署一个Token合约需要20-50万Gas
}

// 如果需要创建100个Token,需要2000-5000万Gas!

6.2 基础工厂实现

基础的工厂实现很简单:

bash 复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

// 简单的代币合约
contract SimpleToken {
    string public name;
    string public symbol;
    address public creator;
    uint256 public totalSupply;
    
    mapping(address => uint256) public balances;
    
    /**
     * @notice 构造函数:初始化代币
     * @param _name 代币名称
     * @param _symbol 代币符号
     * @param _supply 初始供应量
     */
    constructor(string memory _name, string memory _symbol, uint256 _supply) {
        name = _name;
        symbol = _symbol;
        creator = msg.sender;
        totalSupply = _supply;
        balances[msg.sender] = _supply;
    }
    
    /**
     * @notice 转账函数
     */
    function transfer(address to, uint256 amount) public {
        require(balances[msg.sender] >= amount, "Insufficient balance");
        balances[msg.sender] -= amount;
        balances[to] += amount;
    }
}

// 代币工厂合约
contract TokenFactory {
    // 记录所有创建的代币地址
    SimpleToken[] public tokens;
    
    // 记录每个用户创建的代币
    mapping(address => address[]) public userTokens;
    
    // 事件:记录代币创建
    event TokenCreated(
        address indexed tokenAddress,
        string name,
        string symbol,
        address indexed creator
    );
    
    /**
     * @notice 创建新代币
     * @param name 代币名称
     * @param symbol 代币符号
     * @param initialSupply 初始供应量
     * @return 新代币的地址
     * @dev 使用new关键字创建新合约实例
     */
    function createToken(
        string memory name,
        string memory symbol,
        uint256 initialSupply
    ) public returns (address) {
        // 使用new关键字创建新的代币合约
        SimpleToken newToken = new SimpleToken(name, symbol, initialSupply);
        
        // 记录新代币地址
        tokens.push(newToken);
        userTokens[msg.sender].push(address(newToken));
        
        // 发出事件
        emit TokenCreated(address(newToken), name, symbol, msg.sender);
        
        return address(newToken);
    }
    
    /**
     * @notice 查询创建的代币数量
     */
    function getTokenCount() public view returns (uint256) {
        return tokens.length;
    }
    
    /**
     * @notice 查询用户创建的所有代币
     * @param user 用户地址
     * @return 代币地址数组
     */
    function getUserTokens(address user) public view returns (address[] memory) {
        return userTokens[user];
    }
}

基础工厂的特点:

  • 实现简单
  • 每个合约完整部署
  • Gas成本:20-50万Gas/合约

6.3 Clone工厂模式(EIP-1167)

传统的部署方式Gas成本很高。Clone工厂模式(EIP-1167最小代理标准)可以大幅降低Gas成本。

Clone工厂的核心思想:

  • 先部署一个模板合约(Implementation)
  • 后续的合约不是完整部署,而是创建一个极简的代理
  • 代理通过delegatecall调用模板合约
  • 每个克隆只需要4.5万Gas左右,节省80%到90%!

Clone工厂实现:

bash 复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

// 模板合约(只部署一次)
contract TokenImplementation {
    string public name;
    string public symbol;
    address public creator;
    uint256 public totalSupply;
    
    mapping(address => uint256) public balances;
    
    /**
     * @notice 初始化函数(替代构造函数)
     * @dev Clone不能使用构造函数,所以用初始化函数
     */
    function initialize(
        string memory _name,
        string memory _symbol,
        uint256 _supply
    ) public {
        require(creator == address(0), "Already initialized");
        name = _name;
        symbol = _symbol;
        creator = msg.sender;
        totalSupply = _supply;
        balances[msg.sender] = _supply;
    }
    
    function transfer(address to, uint256 amount) public {
        require(balances[msg.sender] >= amount, "Insufficient balance");
        balances[msg.sender] -= amount;
        balances[to] += amount;
    }
}

// Clone工厂合约
contract CloneFactory {
    // 模板合约地址
    address public implementation;
    
    // 记录所有克隆的地址
    address[] public clones;
    
    // 记录每个用户创建的克隆
    mapping(address => address[]) public userClones;
    
    event CloneCreated(address indexed cloneAddress, address indexed creator);
    
    /**
     * @notice 构造函数:部署模板合约
     * @dev 模板合约只部署一次
     */
    constructor() {
        implementation = address(new TokenImplementation());
    }
    
    /**
     * @notice 创建克隆
     * @param name 代币名称
     * @param symbol 代币符号
     * @param initialSupply 初始供应量
     * @return 克隆合约地址
     * @dev 使用create2创建确定性地址的克隆
     */
    function createClone(
        string memory name,
        string memory symbol,
        uint256 initialSupply
    ) public returns (address) {
        // 使用create2创建克隆(需要实现最小代理合约)
        // 这里简化示例,实际需要使用EIP-1167标准
        bytes memory bytecode = getCloneBytecode();
        bytes32 salt = keccak256(abi.encodePacked(msg.sender, clones.length));
        
        address clone;
        assembly {
            clone := create2(0, add(bytecode, 0x20), mload(bytecode), salt)
        }
        
        // 初始化克隆
        TokenImplementation(clone).initialize(name, symbol, initialSupply);
        
        // 记录克隆地址
        clones.push(clone);
        userClones[msg.sender].push(clone);
        
        emit CloneCreated(clone, msg.sender);
        return clone;
    }
    
    /**
     * @notice 获取克隆字节码(EIP-1167最小代理)
     * @dev 这是EIP-1167标准的最小代理合约字节码
     */
    function getCloneBytecode() internal view returns (bytes memory) {
        // EIP-1167最小代理合约字节码
        // 实际实现需要使用OpenZeppelin的Clones库
        return abi.encodePacked(
            hex"3d602d80600a3d3981f3363d3d373d3d3d363d73",
            implementation,
            hex"5af43d82803e903d91602b57fd5bf3"
        );
    }
}

Clone工厂的优势:

  • 大幅降低Gas成本
  • 适合批量部署
  • 统一管理

推荐使用OpenZeppelin的Clones库:

bash 复制代码
import "@openzeppelin/contracts/proxy/Clones.sol";

contract MyFactory {
    using Clones for address;
    
    address public implementation;
    
    function createClone() external returns (address) {
        address clone = implementation.clone();
        // 初始化克隆...
        return clone;
    }
}

7. 紧急停止模式

紧急停止模式,也叫断路器模式(Circuit Breaker),用于风险控制。在紧急情况下,能够快速暂停合约的功能,防止损失扩大。

7.1 为什么需要紧急停止

什么时候需要紧急停止呢?

  • 发现安全漏洞:

合约存在严重的安全漏洞

正在遭受攻击

需要暂停功能防止损失扩大

  • 预言机数据异常:

价格预言机返回异常数据

可能导致错误的交易

需要暂停等待修复

  • 系统维护:

需要进行系统升级

需要修复Bug

需要暂停服务

  • 市场异常:

市场出现极端波动

需要暂停交易保护用户

没有紧急停止的风险:

如果合约没有紧急停止机制,一旦发现问题,只能眼睁睁看着资金被攻击或损失,无法及时止损。

7.2 OpenZeppelin的Pausable实现

OpenZeppelin提供了Pausable合约,实现起来非常简单:

bash 复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

// 使用OpenZeppelin Pausable的保险库合约
contract VaultWithPause {
    mapping(address => uint256) public balances;
    
    // 暂停状态
    bool public paused;
    
    // 管理员地址
    address public admin;
    
    // 事件:记录暂停和恢复操作
    event Paused(address admin);
    event Unpaused(address admin);
    event Deposited(address indexed user, uint256 amount);
    event Withdrawn(address indexed user, uint256 amount);
    event EmergencyWithdrawal(address indexed user, uint256 amount);
    
    /**
     * @notice 构造函数:初始化管理员
     */
    constructor() {
        admin = msg.sender;
        paused = false;
    }
    
    /**
     * @notice whenNotPaused修饰符:要求合约未暂停
     * @dev 大部分业务函数应该使用此修饰符
     */
    modifier whenNotPaused() {
        require(!paused, "Contract is paused");
        _;
    }
    
    /**
     * @notice whenPaused修饰符:要求合约已暂停
     * @dev 紧急函数应该使用此修饰符
     */
    modifier whenPaused() {
        require(paused, "Contract is not paused");
        _;
    }
    
    /**
     * @notice onlyAdmin修饰符:只有管理员可以调用
     */
    modifier onlyAdmin() {
        require(msg.sender == admin, "Not admin");
        _;
    }
    
    /**
     * @notice 存款函数(暂停时无法调用)
     * @dev 使用whenNotPaused确保暂停时无法存款
     */
    function deposit() public payable whenNotPaused {
        require(msg.value > 0, "Must deposit something");
        balances[msg.sender] += msg.value;
        emit Deposited(msg.sender, msg.value);
    }
    
    /**
     * @notice 提现函数(暂停时无法调用)
     * @param amount 提现金额
     * @dev 使用whenNotPaused确保暂停时无法提现
     */
    function withdraw(uint256 amount) public whenNotPaused {
        require(balances[msg.sender] >= amount, "Insufficient balance");
        balances[msg.sender] -= amount;
        payable(msg.sender).transfer(amount);
        emit Withdrawn(msg.sender, amount);
    }
    
    /**
     * @notice 紧急提现(只能在暂停时调用)
     * @dev 当合约暂停时,用户可以通过此函数提取资金
     * @dev 使用whenPaused确保只能在暂停时调用
     */
    function emergencyWithdraw() public whenPaused {
        uint256 amount = balances[msg.sender];
        require(amount > 0, "No balance");
        
        // 清零余额(防止重复提取)
        balances[msg.sender] = 0;
        
        // 转账给用户
        payable(msg.sender).transfer(amount);
        emit EmergencyWithdrawal(msg.sender, amount);
    }
    
    /**
     * @notice 暂停合约(只有管理员可以调用)
     * @dev 暂停后,deposit和withdraw等函数将无法调用
     */
    function pause() public onlyAdmin whenNotPaused {
        paused = true;
        emit Paused(admin);
    }
    
    /**
     * @notice 恢复合约(只有管理员可以调用)
     * @dev 恢复后,合约功能恢复正常
     */
    function unpause() public onlyAdmin whenPaused {
        paused = false;
        emit Unpaused(admin);
    }
}

紧急停止模式的关键点:

  • 两个修饰符:

whenNotPaused:大部分业务函数使用

whenPaused:紧急函数使用

紧急函数:

emergencyWithdraw:允许用户在暂停时提取资金

确保用户资金安全

  • 权限控制:

只有管理员可以暂停/恢复

建议使用多签钱包

7.3 最佳实践

推荐做法:

  • 结合多签钱包:

紧急停止的权限最好结合多签钱包

避免单点故障

提高安全性

  • 设置时间锁:

防止权限滥用

给社区反应时间

记录暂停原因:

记录每次暂停的原因和时间

便于审计和追溯

  • 定期演练:

定期进行应急演练

确保在真正的紧急情况下能够快速响应

要避免的做法:

  • 不要过度中心化:

Circuit Breaker是保护机制,但不应过度使用

避免滥用暂停功能

  • 不要忽视用户体验:

暂停时要及时通知用户

提供紧急提现功能

  • 不要忽视恢复流程:

确保有清晰的恢复流程

测试恢复功能

使用OpenZeppelin的Pausable:

bash 复制代码
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MyContract is Pausable, Ownable {
    function deposit() public whenNotPaused {
        // 存款逻辑...
    }
    
    function pause() public onlyOwner {
        _pause();
    }
    
    function unpause() public onlyOwner {
        _unpause();
    }
}

8. 模式对比与选择指南

现在我们已经学习了6种设计模式,让我们做一个对比,帮助大家在实际项目中选择合适的模式。

8.1 模式对比表

8.2 选择指南

对于基础合约:

访问控制和紧急停止几乎是必备的:

  • 任何合约都需要权限管理
  • 金融类合约更需要紧急停止机制

如果合约涉及资金操作:

提现模式和CEI原则是必须遵守的:

  • 这关系到资金安全
  • 不能有任何侥幸心理
  • 必须遵循CEI原则

如果合约有明确的生命周期:

状态机模式是首选:

  • 众筹、拍卖等场景
  • 规范流程,减少错误

需要升级能力的合约:

可以考虑代理模式,但要注意:

  • 这是一个高复杂度的方案
  • 需要非常谨慎
  • 确保存储布局的兼容性
  • 充分测试升级过程

如果需要批量部署相同类型的合约:

工厂模式是首选:

  • 特别是Clone工厂能大幅降低成本
  • 统一管理合约实例

8.3 模式组合建议

在实际项目中,通常会组合使用多个模式:

基础组合:

  • 访问控制 + 紧急停止

资金相关:

  • 访问控制 + 提现模式 + 紧急停止

复杂系统:

-访问控制 + 状态机 + 提现模式 + 紧急停止 + 代理模式

9. 模式组合应用案例

在实际项目中,我们通常会组合使用多个设计模式来构建复杂的系统。让我们看几个真实项目的例子。

9.1 DeFi借贷协议

像Compound和AAVE这样的DeFi借贷协议,通常会组合使用多个设计模式:

使用的模式:

访问控制:

  • 管理管理员权限
  • 使用多签钱包增加安全性

紧急停止:

  • 快速应对市场风险或安全事件
  • 保护用户资金

提现模式:

  • 确保用户资金的安全取款
  • 遵循CEI原则

代理模式:

  • 让协议能够不断迭代升级
  • 修复问题和添加新功能

示例代码结构:

bash 复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

// DeFi借贷协议示例(组合多个模式)
contract LendingProtocol is Ownable, Pausable, ReentrancyGuard {
    mapping(address => uint256) public deposits;
    mapping(address => uint256) public borrows;
    
    // 访问控制:只有owner可以设置参数
    function setInterestRate(uint256 rate) external onlyOwner whenNotPaused {
        // 设置利率...
    }
    
    // 提现模式:遵循CEI原则
    function withdraw(uint256 amount) external nonReentrant whenNotPaused {
        // Checks
        require(deposits[msg.sender] >= amount, "Insufficient balance");
        
        // Effects
        deposits[msg.sender] -= amount;
        
        // Interactions
        payable(msg.sender).transfer(amount);
    }
    
    // 紧急停止:只有owner可以暂停
    function pause() external onlyOwner {
        _pause();
    }
    
    function unpause() external onlyOwner {
        _unpause();
    }
}

9.2 NFT交易市场

像OpenSea和Blur这样的NFT交易市场,也组合使用了多个设计模式:

使用的模式:

  • 访问控制:

管理平台权限

控制平台费用

  • 工厂模式:

创建不同的NFT集合

每个艺术家或项目可以有自己的合约

  • 状态机:

管理拍卖流程

从开始竞拍到结束到资金结算

  • 提现模式:

安全的资金结算

防止重入攻击

9.3 DAO治理系统

像Compound Governance这样的DAO治理系统,也组合使用了多个设计模式:

使用的模式:

  • 访问控制:

管理投票权

只有代币持有者才能投票

  • 状态机:

管理提案的完整流程

从创建、投票、排队到执行

  • 代理模式:

DAO本身可以升级

通过治理投票来决定升级方案

  • 紧急停止:

在出现问题时暂停治理流程

保护系统安全

  • 关键要点:

从这些例子可以看出,真正的工程实践中,设计模式不是孤立使用的,而是根据业务需求组合使用,每个模式解决特定的问题。

相关推荐
__万波__2 小时前
二十三种设计模式(十四)--命令模式
java·设计模式·命令模式
程序员zgh2 小时前
C++常用设计模式
c语言·数据结构·c++·设计模式
MicroTech20252 小时前
微算法科技(NASDAQ MLGO)使用多线程技术提升区块链共识算法的性能
科技·区块链·共识算法
爬点儿啥3 小时前
[Ai Agent] 12 Swarm 与 Agents SDK —— 去中心化的多智能体协作
去中心化·区块链·swarm·langgraph·agents sdk·handoff
山风wind3 小时前
设计模式-模板方法模式详解
python·设计模式·模板方法模式
Biteagle3 小时前
P2SH:比特币的「脚本保险箱」与比特鹰的技术解析
区块链·智能合约
郝学胜-神的一滴3 小时前
Linux线程的共享资源与非共享资源详解
linux·服务器·开发语言·c++·程序人生·设计模式
syt_10133 小时前
设计模式之-单例模式
单例模式·设计模式
KD8 小时前
设计模式——责任链模式实战,优雅处理Kafka消息
后端·设计模式·kafka