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构建的基本框架。为不同的数据类型创建了具体的页面,这些页面继承了基本框架,并为其提供了具体的数据加载和渲染方法。

自定义动画

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

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

结论

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

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

相关推荐
车载诊断技术3 小时前
电子电气架构 --- 什么是EPS?
网络·人工智能·安全·架构·汽车·需求分析
武子康3 小时前
大数据-258 离线数仓 - Griffin架构 配置安装 Livy 架构设计 解压配置 Hadoop Hive
java·大数据·数据仓库·hive·hadoop·架构
9527华安9 小时前
FPGA多路MIPI转FPD-Link视频缩放拼接显示,基于IMX327+FPD953架构,提供2套工程源码和技术支持
fpga开发·架构·音视频
jcLee9512 小时前
Flutter/Dart:使用日志模块Logger Easier
flutter·log4j·dart·logger
tmacfrank12 小时前
Flutter 异步编程简述
flutter
tmacfrank12 小时前
Flutter 基础知识总结
flutter
叫我菜菜就好13 小时前
【Flutter_Web】Flutter编译Web第三篇(网络请求篇):dio如何改造方法,变成web之后数据如何处理
前端·网络·flutter
lxyzcm13 小时前
深入理解C++23的Deducing this特性(上):基础概念与语法详解
开发语言·c++·spring boot·设计模式·c++23
越甲八千13 小时前
重温设计模式--单例模式
单例模式·设计模式
Vincent(朱志强)14 小时前
设计模式详解(十二):单例模式——Singleton
android·单例模式·设计模式