模板方法模式(Template Method Pattern)
是一种行为设计模式,用于定义算法的框架,但将具体的步骤延迟到子类中。这样,算法的结构保持不变,而具体的步骤可以被重写。这使得代码具有更好的重用性和扩展性。
模板方法模式包括以下组件:
- 抽象类(Abstract Class):这个类定义了一个模板方法,它是一个算法的骨架。这个方法由一系列步骤组成,其中一些步骤是抽象的,需要子类来实现。
- 具体类(Concrete Class):这是抽象类的子类,实现了抽象类中定义的抽象方法。这些方法是模板方法的一部分。
优点:
- 代码重用:模板方法模式允许我们重用算法的结构,而不是复制和粘贴。
- 扩展性:如果需要更改算法的某些步骤,只需要扩展基类并覆盖所需的步骤即可。
缺点:
- 对于某些简单的算法,模板方法模式可能会使代码过于复杂。
- 如果不当地使用,可能会导致过多的类和代码重复。
简而言之,模板方法模式是一个定义在父类中的算法骨架,但将一些步骤的具体实现延迟到子类中。这样,不改变算法结构的情况下,子类可以重新定义算法中的某些步骤。
接下来让我们一起来看一下在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构建的基本骨架。具体的数据加载和列表构建由子类(如NumberListPage
和StringListPage
)来实现。可以为不同的内容类型定义不同的列表页面,同时保持加载和错误处理的逻辑不变。
场景二:自定义动画
一个常见的使用模板方法模式的场景是创建自定义的动画。动画经常有一些相同的步骤,如初始化、开始、结束等,但具体的动画效果可能会有所不同。
可以使用模板方法模式来定义动画的基本框架,然后通过子类来定义具体的动画效果。
使用模板方法模式来创建自定义的动画:
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
是一个抽象方法,由子类来实现。FadeAnimation
和ScaleAnimation
是两个具体的动画效果,分别实现了淡入淡出和缩放动画。
这种方式允许定义多种动画效果,同时保持动画控制器和生命周期管理的逻辑不变。
总结
当我们在软件开发中面临重复的结构或流程时,设计模式会作为一盏明灯,指引我们找到最佳的代码组织方式。模板方法模式
正是这样一个强大的工具,特别是当我们希望为某个过程定义一个固定的框架,但又想为其中的某些步骤提供灵活性时。
列表页面构建
构建一个应用,其中有多个页面,每个页面都需要加载数据、显示进度指示器、处理错误等。虽然每个页面的数据和渲染方式可能不同,但加载和错误处理的流程是相同的。
这正是模板方法模式大显身手的时刻!定义了一个抽象的列表页面,该页面包含了加载数据和UI构建的基本框架。为不同的数据类型创建了具体的页面,这些页面继承了基本框架,并为其提供了具体的数据加载和渲染方法。
自定义动画
考虑到动画有很多共同的步骤,如初始化、启动和销毁,可以利用模板方法模式来简化这一过程。
创建了一个抽象的动画小部件,定义了动画的基本框架。为淡入淡出和缩放创建了具体的动画效果,这些效果继承了基本框架,并为其提供了具体的动画构建方法。
结论
模板方法模式是一种优雅的设计模式,它提供了一种简化复杂性的方法。它不仅帮助我们避免代码重复,而且提供了一个清晰、可维护的代码结构。
希望对您有所帮助谢谢!!!