Flutter开发实战:模板方法模式(Template Method Pattern)

模板方法模式(Template Method Pattern)是一种行为设计模式,用于定义算法的框架,但将具体的步骤延迟到子类中。这样,算法的结构保持不变,而具体的步骤可以被重写。这使得代码具有更好的重用性和扩展性。

模板方法模式包括以下组件:

  1. 抽象类(Abstract Class):这个类定义了一个模板方法,它是一个算法的骨架。这个方法由一系列步骤组成,其中一些步骤是抽象的,需要子类来实现。
  2. 具体类(Concrete Class):这是抽象类的子类,实现了抽象类中定义的抽象方法。这些方法是模板方法的一部分。

优点:

  1. 代码重用:模板方法模式允许我们重用算法的结构,而不是复制和粘贴。
  2. 扩展性:如果需要更改算法的某些步骤,只需要扩展基类并覆盖所需的步骤即可。

缺点:

  1. 对于某些简单的算法,模板方法模式可能会使代码过于复杂。
  2. 如果不当地使用,可能会导致过多的类和代码重复。

简而言之,模板方法模式是一个定义在父类中的算法骨架,但将一些步骤的具体实现延迟到子类中。这样,不改变算法结构的情况下,子类可以重新定义算法中的某些步骤。

接下来让我们一起来看一下在Flutter开发中,如何使用模板方法模式来构建UI或处理业务逻辑。

场景一:页面多态

可以考虑使用下面这个场景,其中有多种类型的列表页面,它们都有相同的加载和错误处理,但它们的内容渲染方式有所不同。

使用模板方法模式的示例:

dart 复制代码
abstract class AbstractListPage extends StatefulWidget {
  @override
  _AbstractListPageState createState();
}

abstract class _AbstractListPageState extends State<AbstractListPage> {
  bool isLoading = true;
  bool hasError = false;

  @override
  void initState() {
    super.initState();
    fetchData();
  }

  fetchData() async {
    try {
      await loadItems();
      setState(() {
        isLoading = false;
      });
    } catch (e) {
      setState(() {
        isLoading = false;
        hasError = true;
      });
    }
  }

  @protected
  Future<void> loadItems();

  @override
  Widget build(BuildContext context) {
    if (isLoading) {
      return const Center(child: CircularProgressIndicator());
    }

    if (hasError) {
      return const Center(child: Text("Error loading items"));
    }

    return buildList();
  }

  @protected
  Widget buildList();
}

class NumberListPage extends AbstractListPage {
  @override
  _NumberListPageState createState() => _NumberListPageState();
}

class _NumberListPageState extends _AbstractListPageState {
  List<int>? numbers;

  @override
  Future<void> loadItems() async {
    await Future.delayed(const Duration(seconds: 2)); // 模拟网络加载
    numbers = List.generate(10, (index) => index);
  }

  @override
  Widget buildList() {
    return ListView.builder(
      itemCount: numbers?.length,
      itemBuilder: (context, index) {
        return ListTile(title: Text("Number: ${numbers?[index]}"));
      },
    );
  }
}

class StringListPage extends AbstractListPage {
  @override
  _StringListPageState createState() => _StringListPageState();
}

class _StringListPageState extends _AbstractListPageState {
  List<String>? strings;

  @override
  Future<void> loadItems() async {
    await Future.delayed(const Duration(seconds: 2)); // 模拟网络加载
    strings = List.generate(10, (index) => "Item $index");
  }

  @override
  Widget buildList() {
    return ListView.builder(
      itemCount: strings?.length,
      itemBuilder: (context, index) {
        return ListTile(title: Text("String: ${strings?[index]}"));
      },
    );
  }
}

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Template Method Pattern Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: const Text("Template Method Pattern Demo"),
        ),
        body: NumberListPage(),
        // body: StringListPage(),
      ),
    );
  }
}

AbstractListPage是一个抽象的列表页面,定义了数据加载和UI构建的基本骨架。具体的数据加载和列表构建由子类(如NumberListPageStringListPage)来实现。可以为不同的内容类型定义不同的列表页面,同时保持加载和错误处理的逻辑不变。

场景二:自定义动画

一个常见的使用模板方法模式的场景是创建自定义的动画。动画经常有一些相同的步骤,如初始化、开始、结束等,但具体的动画效果可能会有所不同。

可以使用模板方法模式来定义动画的基本框架,然后通过子类来定义具体的动画效果。

使用模板方法模式来创建自定义的动画:

dart 复制代码
///1.定义一个抽象的动画Widget和它的抽象State
abstract class CustomAnimationWidget extends StatefulWidget {
  final Widget child;

  CustomAnimationWidget({
    Key? key,
    required this.child,
  }) : super(key: key);

  @override
  _CustomAnimationWidgetState createState();
}

abstract class _CustomAnimationWidgetState extends State<CustomAnimationWidget>
    with SingleTickerProviderStateMixin {
  AnimationController? _animationController;
  Animation? _animation;

  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(
      duration: const Duration(seconds: 1),
      vsync: this,
    );

    _animation = Tween(begin: 0.0, end: 1.0).animate(_animationController!)
      ..addListener(() {
        setState(() {});
      })
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          _animationController?.reverse();
        } else if (status == AnimationStatus.dismissed) {
          _animationController?.forward();
        }
      });

    _animationController?.forward();
  }

  @override
  Widget build(BuildContext context) {
    return animatedBuilder(_animation!);
  }

  @protected
  Widget animatedBuilder(Animation animation);

  @override
  void dispose() {
    _animationController?.dispose();
    super.dispose();
  }
}

///2.实现两种具体的动画效果:
///
class FadeAnimation extends CustomAnimationWidget {
  @override
  // ignore: overridden_fields
  final Widget child;

  FadeAnimation({
    Key? key,
    required this.child,
  }) : super(
          key: key,
          child: child,
        );

  @override
  _FadeAnimationState createState() => _FadeAnimationState();
}

class _FadeAnimationState extends _CustomAnimationWidgetState {
  @override
  Widget animatedBuilder(Animation animation) {
    return Opacity(
      opacity: animation.value,
      child: widget.child,
    );
  }
}

class ScaleAnimation extends CustomAnimationWidget {
  @override
  // ignore: overridden_fields
  final Widget child;

  ScaleAnimation({
    Key? key,
    required this.child,
  }) : super(
          key: key,
          child: child,
        );

  @override
  _ScaleAnimationState createState() => _ScaleAnimationState();
}

class _ScaleAnimationState extends _CustomAnimationWidgetState {
  @override
  Widget animatedBuilder(Animation animation) {
    return Transform.scale(
      scale: animation.value,
      child: widget.child,
    );
  }
}

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Animation Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(title: const Text("Animation Demo")),
        body: Center(
          child: FadeAnimation(
            child: const FlutterLogo(size: 100),
          ),
        ),
      ),
    );
  }
}

CustomAnimationWidget定义了动画的基本框架,其中animatedBuilder是一个抽象方法,由子类来实现。FadeAnimationScaleAnimation是两个具体的动画效果,分别实现了淡入淡出和缩放动画。

这种方式允许定义多种动画效果,同时保持动画控制器和生命周期管理的逻辑不变。

总结

当我们在软件开发中面临重复的结构或流程时,设计模式会作为一盏明灯,指引我们找到最佳的代码组织方式。模板方法模式正是这样一个强大的工具,特别是当我们希望为某个过程定义一个固定的框架,但又想为其中的某些步骤提供灵活性时。

列表页面构建

构建一个应用,其中有多个页面,每个页面都需要加载数据、显示进度指示器、处理错误等。虽然每个页面的数据和渲染方式可能不同,但加载和错误处理的流程是相同的。

这正是模板方法模式大显身手的时刻!定义了一个抽象的列表页面,该页面包含了加载数据和UI构建的基本框架。为不同的数据类型创建了具体的页面,这些页面继承了基本框架,并为其提供了具体的数据加载和渲染方法。

自定义动画

考虑到动画有很多共同的步骤,如初始化、启动和销毁,可以利用模板方法模式来简化这一过程。

创建了一个抽象的动画小部件,定义了动画的基本框架。为淡入淡出和缩放创建了具体的动画效果,这些效果继承了基本框架,并为其提供了具体的动画构建方法。

结论

模板方法模式是一种优雅的设计模式,它提供了一种简化复杂性的方法。它不仅帮助我们避免代码重复,而且提供了一个清晰、可维护的代码结构。

希望对您有所帮助谢谢!!!

相关推荐
BG20 小时前
Flutter 简仿Excel表格组件介绍
flutter
稻草人22221 天前
java Excel 导出 ,如何实现八倍效率优化,以及代码分层,方法封装
后端·架构
zhangmeng1 天前
FlutterBoost在iOS26真机运行崩溃问题
flutter·app·swift
数据智能老司机1 天前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
恋猫de小郭1 天前
对于普通程序员来说 AI 是什么?AI 究竟用的是什么?
前端·flutter·ai编程
卡尔特斯1 天前
Flutter A GlobalKey was used multipletimes inside one widget'schild list.The ...
flutter
数据智能老司机1 天前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
w_y_fan1 天前
Flutter 滚动组件总结
前端·flutter
烛阴1 天前
【TS 设计模式完全指南】懒加载、缓存与权限控制:代理模式在 TypeScript 中的三大妙用
javascript·设计模式·typescript
bobz9651 天前
k8s svc 实现的技术演化:iptables --> ipvs --> cilium
架构