APP架构和代码重构(Flutter)
App 的质量属性
影响 App 质量和用户体验的各种特性。常见的质量属性如下:
- 功能性:App 应具备预期的功能,满足用户的需求。
- 可用性:界面友好,易于使用,用户能够轻松上手。
- 稳定性:很少出现崩溃、闪退等问题,能够稳定运行。
- 性能:响应迅速,加载速度快,不出现卡顿现象。
- 安全性:保护用户的隐私和数据安全。
- 兼容性:适配多种设备和操作系统。
- 可维护性:便于更新和修复问题。
- 可扩展性:能够方便地添加新功能。
- 用户体验:整体体验良好,让用户感到满意。
- 数据准确性:提供准确的信息。
App重构 (Refactoring)
在不改变
代码外在行为
的前提下,对架构和代码做出修改, 以改进App内部的结构
, 来提高App的质量属性
App重构分为如下两类
1. 架构重构(Re-Architecting)
对现有App从整体框架上进行更改,修正和更新, 相当于做一次大手术, 代价非常昂贵
所以需要慎重考虑
, 下面是架构重构的原则
- 明确重构的目的和
必要性
Hold the line - 定义
重构完成
的界限 Define "Done" - 持续
渐进性
重构 Incrementalism - 熟悉当前的业务和架构状态 Find the start line
- 重视用户数据 Don't ignore the data
- 管理好技术债务 Manager tech debt better
- 远离虚华的东西, 使用成熟稳定的方案(例如使用"热门"的技术栈) Stay away from vanity stuff
- 团队一致,做好准备, Get the team ready
重构过程采用渐进式
重构, 谨慎规划和逐步实施,避免对现有功能产生负面影响。
由底而上
,及时进行测试和验证,确保重构后的 App 依然满足业务需求和质量标准
主要架构重构方式如下:
- 模块化
- 目录和资源整理,公共资源和工具类的抽离模块化, 然后按照功能进行分包分库
- 把App拆分成多个相互独立的模块,明确各模块的
职责
和功能边界
, 达到高内聚、低耦合
目标。
- 组件化
- 在传统的 App 架构中,界面和逻辑往往相互交织,一旦某个页面需要进行调整或优化,就可能牵一发而动全身。而组件化更侧重于
界面和交互
逻辑的拆分。 - 将界面拆分成独立的组件,每个组件都具有自己的生命周期和状态管理,从而实现了界面逻辑的高度复用和解耦。
独立开发、测试和部署
。
- 在传统的 App 架构中,界面和逻辑往往相互交织,一旦某个页面需要进行调整或优化,就可能牵一发而动全身。而组件化更侧重于
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('按钮被点击了');
},
),
),
);
}
}
-
插件化
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('计算'),
),
],
),
),
);
}
}
-
分层架构
分成表现层、业务逻辑层、数据访问层等,提高代码的复用性和可维护性。
-
数据模型优化:
重新设计数据结构和数据库架构,以提高数据访问性能和存储效率。
-
异步处理
利用异步编程技术,如线程、进程或异步任务,提高 App 的响应性和并发处理能力。
-
接口设计
定义清晰的接口,便于模块之间的通信和集成。
-
代码抽象:
提取公共功能和逻辑,创建抽象类或接口,提高代码的可复用性。
2.代码重构(Re-Coding)
在不改变代码外部行为的前提下, 有条不紊地改善代码,增加可读性或简化结构, 改进App内部质量
Bad Smells(味道) in Code
代码存在的一些问题,影响代码的质量、可维护性和可读性。以下是常见Bad Smells:
- 重复代码:相同或相似的代码片段在多个地方出现。
- 过长的函数:函数过于复杂,包含过多的逻辑。
- 过大的类:类的职责过于宽泛,包含过多的功能。
- 神秘代码:缺乏清晰的注释或文档,难以理解代码的目的和功能。
- 过度耦合:不同模块或组件之间紧密耦合,修改一个部分可能会影响其他部分。
- 数据泥团:多个类中使用了相同的一组数据。
- 死代码:不再使用或无法到达的代码。
- 条件复杂:复杂的条件逻辑,可能难以理解和维护。
- 循环嵌套过深:深层的循环嵌套可能导致性能问题和可读性降低。
常见的代码重构方法
- 函数
- 提取重复代码到独立函数。
- 将大函数分解成几个小函数,单一职责
- 减少嵌套层级(使用if, guard
提前返回
) - 用异常(exception)代替错误码
- 引入断言(assert),检查参数和结果
- 给函数起有意义的名字。
// 定义一个矩形类
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);
}
}
- 数据
- 减少全局数据的使用
- 合理使用数据结构和封装数据。
- 使用构造函数进行对象初始化。
- 常量取代magic number
- 类
- 单一职责: 如果类过于庞大和复杂,将其分解为更小、更专注的类, 并移除不必要的属性和方法。
- 提取类:如果多个类中有相似的功能,可以将这些功能提取到一个单独的类中,以减少代码重复。
- 使用接口和抽象类:使用接口来定义类的行为,提高代码的灵活性和可扩展性。
- 运用设计模式:合适的设计模式可以改善类的结构和代码的可维护性。
// 定义 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