短谈Flutter中 BlocProvider 和 参数递传

634 阅读2分钟

在刚开始接触flutter中的BLoC 状态管理机制时,可能很多新人会对使用BlocProvider传参和使用构造函数参数传参两者有什么差异感到不解。以flutter_bloc 包中的代码举例,比如向PageB传入的Model实例,可以有如下两种方式:

  1. 使用BlocProvider:
 class PageAState extends State{
   Model modelX;
   ...
   Widget build(BuildContext context) {
     return BlocProvider.value(
       value: modelX,
       child: PageB(),
    );
  }
 }
 ​
 class PageB extends StatelessWidget {
   Widget build(BuildContext context){
     //获取Model 实例
     var modelX = context.read<Model>();
    ...
  }
 }
  1. 使用构造函数参数:
 class PageAState extends State{
   Model modelX;
   
   Widget build(BuildContext context) {
     return PageB(model: modelX);
  }
 }
 ​
 class PageB extends StatelessWidget {
   PageB({required this.modelX});
   final Model modeX;
   
   Widget build(BuildContext context){
    ...
  }
 }

两种方式确实都能达到相同目的,但为何我们通常都使用第一种而不是第二种方式传递Model实例呢?再举一个例子来说明,假如要在另外一个UI中使用PageB, 该UI的层级结构如下:

 class PageC extends StatelessWidget {
     Widget build(BuildContext context){
         return PageD();
     }
 }
 ​
 class PageD extends StatelessWidget {
     Widget bulid(BuildContext context){
         return PageE();
     }
 }
 ​
 class PageE extends StatelessWidget {
     Widget build(BuildContext context){
         return PageB();
     }
 }

在使用PageB 之前嵌套了多层Widget,这个时候如果使用参数递传的方式,那么PageCPageDPageE 都要增加Model的参数,但实际上它们根本用不到这个参数,只是为了向下传递而已,这就造成了一个很严重的问题:耦合,如果Model发生了变化,比如某一天PageB重构不需要Model了或Model 类型定义改变了,那么C、D、E也要跟着改,很容易造成代码的混乱。在软件设计六原则SOLID 中第一个原则:单一职责 中强调对象应专注提供单一的功能,引入无关的参数显然破坏了这一原则。所以,即使两种方式都能达到相同的目的,但从软件工程角度看,BlocProvider 方式明显优于参数递传的方式。

最近面试遇到很多新人对软件设计SOLID原则知之甚少,很多时候不理解一段看似简单的功能为什么要写那么多的代码,就没有看到背后所蕴含的架构思想,由此引出这一个小问题。很多人习惯了写功能性代码,反正功能实现了就行,主要可能因为很多项目生命周期比较短,没有暴露出相关问题。但是如果参与到了大型项目,项目周期5年以上的代码,就会切身体会到,在需求不断变化迭代过程中,代码的系统架构有多重要。当今时代很多时候实现一个功能没什么技术含量,而如何优雅的实现一个功能,永远追求一个最优方案,才是我们开发者应秉持专业素养,愿共勉。