一祝闻、概述
這個(gè)合約示例中占卧,我們有以下幾個(gè)文件:
Types.sol
IProcessor.sol
Processor.sol
Proxy.sol
Client.sol
分別論述下文件的作用:
Types.sol
定義了一些基本類型,與業(yè)務(wù)邏輯無(wú)關(guān)联喘;
IProcessor.sol
:關(guān)鍵合約屉栓,定義了Processor的接口;
Processor.sol
:關(guān)鍵合約耸袜,定義了實(shí)際對(duì)數(shù)據(jù)的邏輯操作;
Proxy.sol
:關(guān)鍵合約牲平,定義了數(shù)據(jù)以及邏輯操作合約Processor
的地址堤框;
Client.sol
定義了用戶操作Proxy
合約的方法,免去手動(dòng)將IProcessor
賦予Proxy
的過(guò)程纵柿。
1.Types.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
enum Gender {
Male,
Female
}
struct Student {
string name;
Gender gender;
uint256 age;
}
2.IProcessor.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import {Gender} from "./Types.sol";
interface IProcessor {
function setStudentName(string memory) external;
function setStudentGender(Gender) external;
function setStudentAge(uint256) external;
}
3.Processor.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "./IProcessor.sol";
import {Gender, Student} from "./Types.sol";
contract Processor is IProcessor {
Student public student;
address public processor;
function setStudentName(string calldata _name) override external {
student.name = _name;
}
function setStudentGender(Gender _gender) override external {
student.gender = _gender;
}
function setStudentAge(uint256 _age) override external {
student.age = _age;
}
}
4.Proxy.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "./IProcessor.sol";
import {Student} from "./Types.sol";
contract Proxy {
Student public student;
address public processor;
function setProcessor(address _processor) external {
processor = _processor;
}
fallback() external payable {
(bool success, bytes memory data) = processor.delegatecall(msg.data);
require(
success,
"error"
);
}
}
5.Client.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "./IProcessor.sol";
import {Gender, Student} from "./Types.sol";
contract Client {
IProcessor ip;
constructor(address _proxy) {
ip = IProcessor(_proxy);
}
function setName(string calldata _name) external {
ip.setStudentName(_name);
}
function setGender(Gender _gender) external {
ip.setStudentGender(_gender);
}
function setAge(uint256 _age) external {
ip.setStudentAge(_age);
}
}
我們用Client進(jìn)行操作蜈抓,比如說(shuō),執(zhí)行setName
函數(shù)昂儒,輸入的參數(shù)為字符串Tom
沟使,接下來(lái),就是觸發(fā)IProcessor
類型的代理合約Proxy
去執(zhí)行setStudentName
渊跋,但事實(shí)上腊嗡,代理合約中并沒(méi)有setStudentName
,因此會(huì)去執(zhí)行fallback
函數(shù)拾酝,fallback函數(shù)中有關(guān)鍵的delegatecall
函數(shù)燕少,它會(huì)委托Processor
合約去執(zhí)行setStudentName
的函數(shù)邏輯,但是改變的是Proxy
合約上的student
值蒿囤。這樣就實(shí)現(xiàn)了數(shù)據(jù)與邏輯的分離客们。下次如果我們需要升級(jí)邏輯處理合約就非常簡(jiǎn)單了——在Proxy
中,使用setProcessor
函數(shù)將舊的Processor合約替換成新的Processor合約即可材诽。
二底挫、缺陷
這個(gè)實(shí)現(xiàn)主要有兩個(gè)缺陷。
- 其一是
Proxy
中的fallback
函數(shù)執(zhí)行delegatecall
后脸侥,有兩個(gè)返回值建邓,一個(gè)是bool類型的success
,一個(gè)是bytes類型的data
湿痢。這個(gè)示例中我們的Processor
合約中的函數(shù)沒(méi)有返回值涝缝,倒無(wú)所謂扑庞,如果有返回值的話,我們其實(shí)是需要返回給調(diào)用者的拒逮。 - 其二是為了使得
delegatecall
的順利執(zhí)行罐氨,Proxy
和Processor
兩個(gè)合約保持了相同的變量存儲(chǔ)結(jié)構(gòu),但實(shí)際執(zhí)行時(shí)Processor
中的變量卻僅僅是擺設(shè)滩援,這樣的設(shè)計(jì)就顯得累贅栅隐,不夠優(yōu)雅。
這兩個(gè)問(wèn)題其實(shí)都有解決方案玩徊,請(qǐng)看下一篇:《Solidity合約代理模式的幾個(gè)技術(shù)技巧》租悄。