• 在Dart中如何实现以无侵入的方式扩展一个已有类?
  • 发布于 2个月前
  • 296 热度
    0 评论
  • 王晶
  • 0 粉丝 41 篇博客
  •   
前言
在做 iOS 开发的时候,Objective-C 语言有个特性,叫 Category,也就是可以在不修改原有类代码的基础上扩展一个类的方法或静态属性。Dart 语言同样借鉴了这种设计,只是换了个更贴切的说法,叫做 extension。通过 extension 可以以无侵入的方式扩展一个已有的类,从而不影响其他使用这个类的代码。本篇来介绍 extension 的使用以及典型的应用。

extension 介绍
要使用Dart 的 extension 扩展类很简单,只需要新增一个 Dart 文件,然后按下面的方式声明即可:
extension [扩展名称] on [已有类名] {
  static const [静态属性] = ...;

  [已有类名] get [get属性] = ...;

  [方法名](参数) {
    //方法体
  }
}
这里需要注意,extension不支持扩展非静态属性。

应用之扩展颜色
我们在 App 中经常会依据设计规范设置主题色、辅助色、标题字体颜色、分割线颜色等等。虽然可以通过 Theme 来实现,但是 Theme依赖于 Context。我们也可以定义一个主体颜色扩展,来定义我们所需要的颜色,当设计稿更改颜色时,只需要更改这个颜色扩展就可以完成所有颜色的修改。下面是示例代码:
extension ThemeColor on Color {
  static const primaryColor = Color(0xFF0990EC);
  static const lineColor = Color(0xFFEEEEEE);
  //堆代码 duidaima.com
  //反色
  Color get reverseColor {
    return Color.fromARGB(
      alpha,
      255 - red,
      255 - green,
      255 - blue,
    );
  }
 // 将颜色转换为16进制字符串表示,例如:#FFFF0000
  String toHex() {
    return '#${value.toRadixString(16).padLeft(6, 'F').toUpperCase()}';
  }
}

应用之屏幕适配
设计师提供的设计稿通常会按照某一个屏幕的宽度尺寸设计,例如375是设计师常用的一个设计宽度。但是实际屏幕的宽度却五花八门,这个时候就需要做屏幕适配。在 Flutter 屏幕适配插件中,screen_util 是应用最为广泛(中文文档:https://github.com/OpenFlutter/flutter_screenutil/blob/master/README_CN.md)。如果我们去看里面的源码,就会发现里面就用到了extension,其中典型的就是扩展了 num 类。
extension SizeExtension on num {
  ///[ScreenUtil.setWidth]
  double get w => ScreenUtil().setWidth(this);
  ///[ScreenUtil.setHeight]
  double get h => ScreenUtil().setHeight(this);
  ///[ScreenUtil.radius]
  double get r => ScreenUtil().radius(this);
  ///[ScreenUtil.diagonal]
  double get dg => ScreenUtil().diagonal(this);
  ///[ScreenUtil.diameter]
  double get dm => ScreenUtil().diameter(this);
  ///[ScreenUtil.setSp]
  double get sp => ScreenUtil().setSp(this);

  //...
}
原理上其实就是用实际屏幕尺寸与设计稿尺寸计算比例,然后再将这个比例乘以定义组件尺寸的数值。比如20.w,实际上就是20乘以换算的比例得到适配后的宽度。典型的例子就是一个固定宽度的 Container,屏幕越大应该宽度越宽,这样两侧的留白也不至于过宽。这里我们还可以发现,如果是在父类上进行扩展,那么这些扩展对子类也是可以生效的。比如上面扩展的是 num 类,那么对于 double、int 这样的子类也是可以用 num 类的扩展方法的。

应用之简化校验
第三类应用就是表单校验,实际上大部分都是字符串校验,那么我们可以编写一个字符串校验扩展类,将我们用到的字符串校验集中在一起,如下所示。
extension StringValidator on String {
  bool get isEmail {
    //邮箱校验逻辑
  }

  bool get isIdNumber {
    //身份证号校验
  }

  bool get isMobile {
    //手机号校验
  }

  //...
}
这样用起来会更方便,如下所示:
if (!email.isEmail) {
  //邮箱校验失败处理
}

if (!mobile.isMobile) {
  //手机号校验失败处理
}
相比单独定义一个校验类,编写静态方法来说,这样的方式的代码显然更加简洁。

总结
代码开发的一个重要原则是“对扩展开放,对修改封闭”。这是因为扩展的代码不会影响已有的业务代码,而修改则会影响所有用到修改部分的代码。使用 extension 就很好地遵循了这一原则,不更改原有的类代码,因此不影响类的原有使用方式。同时,又扩展了类的特性,使得扩展后的代码可以适用特定的场景。如果你在开发 Flutter 的应用中,发现经常要对某个类进行相同的处理,那么或许使用 extension 扩展会是一个更好的选择。
用户评论