模板方法模式(Template Method Pattern)
是一种行为设计模式,用于定义算法的框架,但将具体的步骤延迟到子类中。这样,算法的结构保持不变,而具体的步骤可以被重写。这使得代码具有更好的重用性和扩展性。
模板方法模式包括以下组件:
- 抽象类(Abstract Class):这个类定义了一个模板方法,它是一个算法的骨架。这个方法由一系列步骤组成,其中一些步骤是抽象的,需要子类来实现。
- 具体类(Concrete Class):这是抽象类的子类,实现了抽象类中定义的抽象方法。这些方法是模板方法的一部分。
优点:
- 代码重用:模板方法模式允许我们重用算法的结构,而不是复制和粘贴。
- 扩展性:如果需要更改算法的某些步骤,只需要扩展基类并覆盖所需的步骤即可。
缺点:
- 对于某些简单的算法,模板方法模式可能会使代码过于复杂。
- 如果不当地使用,可能会导致过多的类和代码重复。
简而言之,模板方法模式是一个定义在父类中的算法骨架,但将一些步骤的具体实现延迟到子类中。这样,不改变算法结构的情况下,子类可以重新定义算法中的某些步骤。
接下来让我们一起来看一下在Flutter开发中,如何使用模板方法模式
来构建UI或处理业务逻辑。
场景一:页面多态
可以考虑使用下面这个场景,其中有多种类型的列表页面,它们都有相同的加载和错误处理,但它们的内容渲染方式有所不同。
使用模板方法模式的示例:
abstract class AbstractListPage extends StatefulWidget {@override_AbstractListPageState createState();}abstract class _AbstractListPageState extends State<AbstractListPage> {bool isLoading = true;bool hasError = false;@overridevoid initState() {super.initState();fetchData();}fetchData() async {try {await loadItems();setState(() {isLoading = false;});} catch (e) {setState(() {isLoading = false;hasError = true;});}}@protectedFuture<void> loadItems();@overrideWidget build(BuildContext context) {if (isLoading) {return const Center(child: CircularProgressIndicator());}if (hasError) {return const Center(child: Text("Error loading items"));}return buildList();}@protectedWidget buildList();}class NumberListPage extends AbstractListPage {@override_NumberListPageState createState() => _NumberListPageState();}class _NumberListPageState extends _AbstractListPageState {List<int>? numbers;@overrideFuture<void> loadItems() async {await Future.delayed(const Duration(seconds: 2)); // 模拟网络加载numbers = List.generate(10, (index) => index);}@overrideWidget 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;@overrideFuture<void> loadItems() async {await Future.delayed(const Duration(seconds: 2)); // 模拟网络加载strings = List.generate(10, (index) => "Item $index");}@overrideWidget 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 {@overrideWidget 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(),),);}}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(), ), ); } }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构建的基本骨架。具体的数据加载和列表构建由子类(如NumberListPage
和StringListPage
)来实现。可以为不同的内容类型定义不同的列表页面,同时保持加载和错误处理的逻辑不变。
场景二:自定义动画
一个常见的使用模板方法模式的场景是创建自定义的动画。动画经常有一些相同的步骤,如初始化、开始、结束等,但具体的动画效果可能会有所不同。
可以使用模板方法模式来定义动画的基本框架,然后通过子类来定义具体的动画效果。
使用模板方法模式来创建自定义的动画:
///1.定义一个抽象的动画Widget和它的抽象Stateabstract 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;@overridevoid 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();}@overrideWidget build(BuildContext context) {return animatedBuilder(_animation!);}@protectedWidget animatedBuilder(Animation animation);@overridevoid dispose() {_animationController?.dispose();super.dispose();}}///2.实现两种具体的动画效果:///class FadeAnimation extends CustomAnimationWidget {@override// ignore: overridden_fieldsfinal Widget child;FadeAnimation({Key? key,required this.child,}) : super(key: key,child: child,);@override_FadeAnimationState createState() => _FadeAnimationState();}class _FadeAnimationState extends _CustomAnimationWidgetState {@overrideWidget animatedBuilder(Animation animation) {return Opacity(opacity: animation.value,child: widget.child,);}}class ScaleAnimation extends CustomAnimationWidget {@override// ignore: overridden_fieldsfinal Widget child;ScaleAnimation({Key? key,required this.child,}) : super(key: key,child: child,);@override_ScaleAnimationState createState() => _ScaleAnimationState();}class _ScaleAnimationState extends _CustomAnimationWidgetState {@overrideWidget animatedBuilder(Animation animation) {return Transform.scale(scale: animation.value,child: widget.child,);}}void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget 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),),),),);}}///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), ), ), ), ); } }///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
是一个抽象方法,由子类来实现。FadeAnimation
和ScaleAnimation
是两个具体的动画效果,分别实现了淡入淡出和缩放动画。
这种方式允许定义多种动画效果,同时保持动画控制器和生命周期管理的逻辑不变。
总结
当我们在软件开发中面临重复的结构或流程时,设计模式会作为一盏明灯,指引我们找到最佳的代码组织方式。模板方法模式
正是这样一个强大的工具,特别是当我们希望为某个过程定义一个固定的框架,但又想为其中的某些步骤提供灵活性时。
列表页面构建
构建一个应用,其中有多个页面,每个页面都需要加载数据、显示进度指示器、处理错误等。虽然每个页面的数据和渲染方式可能不同,但加载和错误处理的流程是相同的。
这正是模板方法模式大显身手的时刻!定义了一个抽象的列表页面,该页面包含了加载数据和UI构建的基本框架。为不同的数据类型创建了具体的页面,这些页面继承了基本框架,并为其提供了具体的数据加载和渲染方法。
自定义动画
考虑到动画有很多共同的步骤,如初始化、启动和销毁,可以利用模板方法模式来简化这一过程。
创建了一个抽象的动画小部件,定义了动画的基本框架。为淡入淡出和缩放创建了具体的动画效果,这些效果继承了基本框架,并为其提供了具体的动画构建方法。
结论
模板方法模式是一种优雅的设计模式,它提供了一种简化复杂性的方法。它不仅帮助我们避免代码重复,而且提供了一个清晰、可维护的代码结构。
希望对您有所帮助谢谢!!!