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

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

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

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

优点:

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

缺点:

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

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

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

场景一:页面多态

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

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

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(),
      ),
    );
  }
}
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)来实现。可以为不同的内容类型定义不同的列表页面,同时保持加载和错误处理的逻辑不变。

场景二:自定义动画

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

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

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

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

自定义动画

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

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

结论

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

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

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MY0PFQHT' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片