likes
comments
collection
share

APP架构和代码重构(Flutter)

作者站长头像
站长
· 阅读数 37

APP架构和代码重构(Flutter)

App 的质量属性

影响 App 质量和用户体验的各种特性。常见的质量属性如下:

  1. 功能性:App 应具备预期的功能,满足用户的需求。
  2. 可用性:界面友好,易于使用,用户能够轻松上手。
  3. 稳定性:很少出现崩溃、闪退等问题,能够稳定运行。
  4. 性能:响应迅速,加载速度快,不出现卡顿现象。
  5. 安全性:保护用户的隐私和数据安全。
  6. 兼容性:适配多种设备和操作系统。
  7. 可维护性:便于更新和修复问题。
  8. 可扩展性:能够方便地添加新功能。
  9. 用户体验:整体体验良好,让用户感到满意。
  10. 数据准确性:提供准确的信息。

App重构 (Refactoring)

不改变代码外在行为的前提下,对架构和代码做出修改, 以改进App内部的结构, 来提高App的质量属性

App重构分为如下两类

1. 架构重构(Re-Architecting)

对现有App从整体框架上进行更改,修正和更新, 相当于做一次大手术, 代价非常昂贵

所以需要慎重考虑, 下面是架构重构的原则

  1. 明确重构的目的和必要性 Hold the line
  2. 定义重构完成的界限 Define "Done"
  3. 持续渐进性重构 Incrementalism
  4. 熟悉当前的业务和架构状态 Find the start line
  5. 重视用户数据 Don't ignore the data
  6. 管理好技术债务 Manager tech debt better
  7. 远离虚华的东西, 使用成熟稳定的方案(例如使用"热门"的技术栈) Stay away from vanity stuff
  8. 团队一致,做好准备, Get the team ready

重构过程采用渐进式重构, 谨慎规划和逐步实施,避免对现有功能产生负面影响。

由底而上,及时进行测试和验证,确保重构后的 App 依然满足业务需求和质量标准

主要架构重构方式如下:

  1. 模块化
    1. 目录和资源整理,公共资源和工具类的抽离模块化, 然后按照功能进行分包分库
    2. 把App拆分成多个相互独立的模块,明确各模块的职责功能边界, 达到高内聚、低耦合目标。
  2. 组件化
    1. 在传统的 App 架构中,界面和逻辑往往相互交织,一旦某个页面需要进行调整或优化,就可能牵一发而动全身。而组件化更侧重于界面和交互逻辑的拆分。
    2. 将界面拆分成独立的组件,每个组件都具有自己的生命周期和状态管理,从而实现了界面逻辑的高度复用和解耦。独立开发、测试和部署
import 'package:flutter/material.dart';

// 定义一个按钮组件ButtonComponent, 该组件接受一个文本字符串和一个点击事件处理函数作为参数,并使用ElevatedButton来创建按钮的外观和行为。
class ButtonComponent extends StatelessWidget {
  const ButtonComponent({Key? key, String text, Function? onPressed}) : super(key: key) {
    final String _text;
    final Function _onPressed;

    // 初始化组件
    ButtonComponent(this._text, this._onPressed);

    // 构建组件的方法
    @override
    Widget build(BuildContext context) {
      return ElevatedButton(
        onPressed: _onPressed,
        child: Text(_text),
      );
    }
  }
}

// 在应用中使用按钮组件
class MyApp extends StatefulWidget {
  @override
  State createState() => MyAppState();
}

class MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ButtonComponent(
          text: '点击我',
          onPressed: () {
            // 按钮点击后的逻辑
            print('按钮被点击了');
          },
        ),
      ),
    );
  }
}
  1. 插件化

    App 根据不同用户的需求动态加载对应的插件,从而实现了业务逻辑的灵活配置和管理

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

// 定义一个FlutterCalcPlugin的计算器插件。该插件使用MethodChannel与平台进行交互,通过native端完成计算逻辑,并将结果返回给调用方。
class FlutterCalcPlugin {
  static const MethodChannel _channel = const MethodChannel('flutter_calc_plugin');

  static Future<int> getPlatformVersion() async {
    final String version = await _channel.invokeMethod('getPlatformVersion');
    return int.parse(version);
  }

  static Future<int> getResult(int a, int b) async {
    final String result = await _channel.invokeMethod('getResult', <String, int>{'a': a, 'b': b});
    return int.parse(result);
  }
}

// 在应用中,导入FlutterCalcPlugin插件,调用FlutterCalcPlugin.getResult方法来执行计算操作,并使用setState来更新计算结果。
class MyApp extends StatefulWidget {
  @override
  State createState() => MyAppState();
}

class MyAppState extends State<MyApp> {
  int _result = 0;

  void _calculate() async {
    final int a = 5;
    final int b = 3;
    final int result = await FlutterCalcPlugin.getResult(a, b);
    setState(() {
      _result = result;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('计算结果: $_result'),
            RaisedButton(
              onPressed: _calculate,
              child: Text('计算'),
            ),
          ],
        ),
      ),
    );
  }
}
  1. 分层架构

    分成表现层、业务逻辑层、数据访问层等,提高代码的复用性和可维护性。

  2. 数据模型优化:

    重新设计数据结构和数据库架构,以提高数据访问性能和存储效率。

  3. 异步处理

    利用异步编程技术,如线程、进程或异步任务,提高 App 的响应性和并发处理能力。

  4. 接口设计

    定义清晰的接口,便于模块之间的通信和集成。

  5. 代码抽象:

    提取公共功能和逻辑,创建抽象类或接口,提高代码的可复用性。

2.代码重构(Re-Coding)

在不改变代码外部行为的前提下, 有条不紊地改善代码,增加可读性或简化结构, 改进App内部质量

Bad Smells(味道) in Code

代码存在的一些问题,影响代码的质量、可维护性和可读性。以下是常见Bad Smells:

  • 重复代码:相同或相似的代码片段在多个地方出现。
  • 过长的函数:函数过于复杂,包含过多的逻辑。
  • 过大的类:类的职责过于宽泛,包含过多的功能。
  • 神秘代码:缺乏清晰的注释或文档,难以理解代码的目的和功能。
  • 过度耦合:不同模块或组件之间紧密耦合,修改一个部分可能会影响其他部分。
  • 数据泥团:多个类中使用了相同的一组数据。
  • 死代码:不再使用或无法到达的代码。
  • 条件复杂:复杂的条件逻辑,可能难以理解和维护。
  • 循环嵌套过深:深层的循环嵌套可能导致性能问题和可读性降低。

常见的代码重构方法

  1. 函数
    1. 提取重复代码到独立函数。
    2. 将大函数分解成几个小函数,单一职责
    3. 减少嵌套层级(使用if, guard提前返回)
    4. 用异常(exception)代替错误码
    5. 引入断言(assert),检查参数和结果
    6. 给函数起有意义的名字。
// 定义一个矩形类
class Rectangle {
  double length;
  double width;

  Rectangle(double length, double width) {
    this.length = length;
    this.width = width;
  }
}

// 原始的计算面积函数
double originalCalculateArea(Rectangle rectangle) {
  if (rectangle.length <= 0 || rectangle.width <= 0) {
    throw ArgumentError('长度和宽度必须大于 0');
  }
  return rectangle.length * rectangle.width;
}

// 重构后的代码
// 1. 提取重复代码到独立函数:将判断长度和宽度是否大于 0 的逻辑提取到一个单独的函数中
bool isValidRectangle(Rectangle rectangle) {
  return rectangle.length > 0 && rectangle.width > 0;
}

// 2. 将大函数分解成几个小函数,单一职责:将计算面积的逻辑拆分成两个小函数
double calculateArea(Rectangle rectangle) {
  if (!isValidRectangle(rectangle)) {
    throw ArgumentError('无效的矩形参数');
  }
  return rectangle.length * rectangle.width;
}

// 3. 减少嵌套层级 (使用 if, guard 提前返回):使用 guard 语句来提前返回,减少嵌套层级
double calculateLargerSide(Rectangle rectangle) {
  if (rectangle.length > rectangle.width) {
    return rectangle.length;
  } else {
    return rectangle.width;
  }
}

void main() {
  Rectangle rectangle = Rectangle(5.0, 3.0);

  // 4. 用异常 (exception) 取代错误码
  try {
    double area = calculateArea(rectangle);
    print('面积: $area');

    double side = calculateLargerSide(rectangle);
    print('较大边长: $side');

    double square = squareArea(side);
    print('边长为$side 的正方形面积: $square');
  } catch (ArgumentError e) {
    print(e.message);
  }
}
  1. 数据
    1. 减少全局数据的使用
    2. 合理使用数据结构和封装数据。
    3. 使用构造函数进行对象初始化。
    4. 常量取代magic number
    1. 单一职责: 如果类过于庞大和复杂,将其分解为更小、更专注的类, 并移除不必要的属性和方法。
    2. 提取类:如果多个类中有相似的功能,可以将这些功能提取到一个单独的类中,以减少代码重复。
    3. 使用接口和抽象类:使用接口来定义类的行为,提高代码的灵活性和可扩展性。
    4. 运用设计模式:合适的设计模式可以改善类的结构和代码的可维护性。
// 定义 UserRepository 接口
interface UserRepository {
  Future<User> getUserInfo(String userId);
}

//UserInfoManager 类实现 UserRepository 接口
class UserInfoManager implements UserRepository {
  @override
  Future<User> getUserInfo(String userId) {
    // 实现具体的用户信息获取逻辑
    return Future<User>.value(user);
  }
}

// 登录管理器类
class LoginManager {
  final Authenticator authenticator;
  LoginManager(this.authenticator);

  Future<void> login(String username, String password) {
    // 调用 authenticator 进行验证
    if (authenticator.validateCredentials(username, password)) {
      // 登录成功的逻辑
    } else {
      // 登录失败的逻辑
    }
  }
}

// 注册管理器类
class RegistrationManager {
  final UserRepository repository;
  RegistrationManager(this.repository);

  Future<void> register(String username, String password) {
    // 注册用户的逻辑
  }
}

// 在主函数中使用重构后的类
void main() {
  var authenticator = Authenticator();
  var loginManager = LoginManager(authenticator);
  var registrationManager = RegistrationManager(UserInfoManager());

  registrationManager.register('user1', 'pass1');
  loginManager.login('user1', 'pass1');
}

常用设计模式

  • 单例模式(Singleton Pattern):确保类只有一个对象被创建。
  • 工厂模式(Factory Pattern):可以使用工厂模式来封装类创建逻辑。
  • 策略模式(Strategy Pattern):将算法封装为独立的策略类来完成同一任务,在运行时动态选择。
  • 装饰器模式(Decorator Pattern):不修改原始类的情况下为类添加额外的功能或行为时。
  • 观察者模式(Observer Pattern):当一个对象的状态改变需要通知其他相关对象时, 实现对象之间的解耦。
  • 模板方法模式(Template Method Pattern):基本步骤固定,但子类具体实现可以变化,定义固定的框架,让子类填充具体的步骤。
// 创建三个用于渲染组件的抽象类
abstract class IActivityIndicator {
  Widget render();
}

abstract class ISlider {
  Widget render(double value, ValueSetter<double> onChanged);
}

abstract class ISwitch {
  Widget render(bool value, ValueSetter<bool> onChanged);
}

// 整体页面的抽象类
abstract class IWidgetFactory {
  String getTitle();
  IActivityIndicator createActivityIndicator();
  ISlider createSlider();
  ISwitch createSwitch();
}

// 创建 iOS 风格和 Android 风格的 Widget 实例对象
class MaterialIWidgetsFactory implements IWidgetFactory {
  @override
  IActivityIndicator createActivityIndicator() {
    return AndroidActivityIndicator();
  }

  @override
  ISlider createSlider() {
    return AndroidSlider();
  }

  @override
  ISwitch createSwitch() {
    return AndroidSwitch();
  }

  @override
  String getTitle() {
    return "Android Widgets";
  }
}

class CupertinoWidgetsFactory implements IWidgetFactory {
  @override
  IActivityIndicator createActivityIndicator() {
    return IOSActivityIndicator();
  }

  @override
  ISlider createSlider() {
    return IOSSlider();
  }

  @override
  ISwitch createSwitch() {
    return IOSSwitch();
  }

  @override
  String getTitle() {
    return "iOS Widgets";
  }
}

// 实现 IActivityIndicator 接口
class AndroidActivityIndicator implements IActivityIndicator {
  @override
  Widget render() {
    return CircularProgressIndicator(
      backgroundColor: const Color(0xFFECECEC),
      valueColor: AlwaysStoppedAnimation<Color>(Colors.black.withOpacity(0.65)),
    );
  }
}

// 实现 IOSActivityIndicator 接口
class IOSActivityIndicator implements IActivityIndicator {
  @override
  Widget render() {
    return CupertinoActivityIndicator();
  }
}

定义抽象类,然后实现这些抽象类来创建具体的Widget实例对象。优势在于可以方便地扩展和替换不同风格的Widget,同时保持代码的简洁和可维护性。

转载自:https://juejin.cn/post/7342481068171771931
评论
请登录