其他
一文学会三种 Rust 设计模式
The following article is from 知行之录 Author 知行之录
洋芋,一块链习《Substrate快速入门与开发实战》课程助教,区块链工程师
我们都知道如何在 Rust 中初始化一个简单的结构体,但面对复杂结构体时,我们应该选择怎样的方式去初始化它呢?在分析 Substrate 代码的过程中,学习到一些复杂结构体初始化的设计模式,本文整理总结如下:
new
模式builder
模式Default
模式
new
模式new
函数。pub fn new(param1, param2 : T) -> Self {
Self {
// 初始化
}
}
primitives/runtime/src/offchain/http.rs
中有如下代码:pub struct Header {
name: Vec<u8>,
value: Vec<u8>,
}
impl Header {
/// Creates new header given it's name and value.
pub fn new(name: &str, value: &str) -> Self {
Header {
name: name.as_bytes().to_vec(),
value: value.as_bytes().to_vec(),
}
}
}
impl<B, E, Block, RA> Client<B, E, Block, RA> where
B: backend::Backend<Block>,
E: CallExecutor<Block>,
Block: BlockT,
{
/// Creates new Substrate Client with given blockchain and code executor.
pub fn new(
backend: Arc<B>,
executor: E,
build_genesis_storage: &dyn BuildStorage,
fork_blocks: ForkBlocks<Block>,
bad_blocks: BadBlocks<Block>,
execution_extensions: ExecutionExtensions<Block>,
_prometheus_registry: Option<Registry>,
) -> sp_blockchain::Result<Self> {
}
Client
,除非记住 new
函数的签名或借助IDE的提示,否则可能会不记得参数列表。而且new
模式还有个问题,就是不适合用在供外部使用的API实现上,因为如果结构体增加或减少一个字段,所有调用该new
函数的地方都要做相应的修改。builder
模式impl Struct {
pub fn build(self) -> Struct {
// 初始化
Struct {
}
}
}
client/service/src/builder.rs
中:pub struct ServiceBuilder<TBl, TRtApi, TCl, TFchr, TSc, TImpQu, TFprb, TFpp,
TExPool, TRpc, Backend>
{
config: Configuration,
pub (crate) client: Arc<TCl>,
backend: Arc<Backend>,
tasks_builder: TaskManagerBuilder,
keystore: Arc<RwLock<Keystore>>,
fetcher: Option<TFchr>,
select_chain: Option<TSc>,
pub (crate) import_queue: TImpQu,
finality_proof_request_builder: Option<TFprb>,
finality_proof_provider: Option<TFpp>,
transaction_pool: Arc<TExPool>,
rpc_extensions: TRpc,
remote_backend: Option<Arc<dyn RemoteBlockchain<TBl>>>,
marker: PhantomData<(TBl, TRtApi)>,
background_tasks: Vec<(&'static str, BackgroundTask)>,
}
impl ServiceBuilder<(), (), (), (), (), (), (), (), (), (), ()> {
/// Start the service builder with a configuration.
pub fn new_light<TBl: BlockT, TRtApi, TExecDisp: NativeExecutionDispatch + 'static>(
config: Configuration,
) -> Result<ServiceBuilder<
...
>, Error> {
...
Ok(ServiceBuilder {
...
})
}
}
impl<TBl, TRtApi, TBackend, TExec, TSc, TImpQu, TExPool, TRpc>
ServiceBuilder<
...
> where
...
{
/// Builds the service.
pub fn build(self) -> Result<Service<
...
>, Error>
where TExec: CallExecutor<TBl, Backend = TBackend>,
{
...
Ok(Service {
...
})
}
}
build
函数的签名可以看出builder
模式不需要指定所有内容来构建结构体ServiceBuilder
。我们看如何使用它,代码在bin/node/cli/src/service.rs
中:/// Builds a new service for a light client.
pub fn new_light(config: Configuration)
-> Result<impl AbstractService, ServiceError> {
type RpcExtension = jsonrpc_core::IoHandler<sc_rpc::Metadata>;
let inherent_data_providers = InherentDataProviders::new();
let service = ServiceBuilder::new_light::<Block, RuntimeApi, node_executor::Executor>(config)?
.with_select_chain(|_config, backend| {
Ok(LongestChain::new(backend.clone()))
})?
...
.build()?;
Ok(service)
}
build
函数有503行。这说明了builder
模式的一个大缺点:非常长。行数是new
模式的几倍。Default
模式Default
,实现default
函数,然后再为其实现一个类似build
的函数。impl Default for Struct {
fn default() -> Self {
// 初始化部分
}
}
impl Struct {
pub fn build(self) -> Struct {
// 初始化
Struct {
}
}
}
VotingRulesBuilder
,它使用一组规则来逐步约束投票。代码在client/finality-grandpa/src/voting_rule.rs
中:pub struct VotingRulesBuilder<Block, B> {
rules: Vec<Box<dyn VotingRule<Block, B>>>,
}
impl<Block, B> Default for VotingRulesBuilder<Block, B> where
Block: BlockT,
B: HeaderBackend<Block>,
{
fn default() -> Self {
VotingRulesBuilder::new()
.add(BeforeBestBlockBy(2.into()))
.add(ThreeQuartersOfTheUnfinalizedChain)
}
}
impl<Block, B> VotingRulesBuilder<Block, B> where
Block: BlockT,
B: HeaderBackend<Block>,
{
/// Return a new `VotingRule` that applies all of the previously added
/// voting rules in-order.
pub fn build(self) -> impl VotingRule<Block, B> + Clone {
VotingRules {
rules: Arc::new(self.rules),
}
}
}
builder
模式,但是与其相比,我们大大降低了build
代码的长度。如果需要进行一些默认的操作,则可以在default()
函数中进行。关于使用,我们可以在bin/node/cli/src/service.rs
中看到如下的代码:voting_rule: grandpa::VotingRulesBuilder::default().build(),
.04结语
扫码关注公众号,回复“1”加入开发者社群