模板方法模式(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构建的基本框架。为不同的数据类型创建了具体的页面,这些页面继承了基本框架,并为其提供了具体的数据加载和渲染方法。
自定义动画
考虑到动画有很多共同的步骤,如初始化、启动和销毁,可以利用模板方法模式来简化这一过程。
创建了一个抽象的动画小部件,定义了动画的基本框架。为淡入淡出和缩放创建了具体的动画效果,这些效果继承了基本框架,并为其提供了具体的动画构建方法。
结论
模板方法模式是一种优雅的设计模式,它提供了一种简化复杂性的方法。它不仅帮助我们避免代码重复,而且提供了一个清晰、可维护的代码结构。
希望对您有所帮助谢谢!!!