likes
comments
collection
share

Flutter开发实战:组合模式(Composite Pattern)

作者站长头像
站长
· 阅读数 35

组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树状结构来表示“整体-部分”层次关系。该模式使得客户端可以统一对待单个对象和组合对象,从而使得组合对象和单个对象在使用时具有一致性。

在组合模式中,有两种主要类型的对象:叶子对象(Leaf)组合对象(Composite)

  • 叶子对象:是不能再包含其他对象的最小单位,它实现了组合中的基本功能。

  • 组合对象:是包含其他子对象的对象,可以是叶子对象,也可以是其他组合对象。

核心思想是:

无论是组合对象还是叶子对象,它们都实现相同的接口,从而使得客户端可以一致地对待它们。这使得客户端可以像操作单个对象一样来操作整个组合结构,而不需要关心当前操作的是单个对象还是组合对象。

主要是通过将对象组织成树结构,以递归方式处理对象和对象集合,从而实现对整个层次结构的统一操作。组合模式包含以下主要角色:

1. Component(组件):它是组合中所有对象的通用接口,可以是抽象类或接口。声明了组合对象和叶子对象的通用操作,包括添加、删除、获取子节点等。

  1. Leaf(叶子对象):是组合中的叶子节点,表示没有子节点的对象。它实现了 Component 接口的方法,但在这些方法中可能不包含具体实现。

  2. Composite(组合对象):是具有子节点的组合对象,它实现了 Component 接口。它可以包含 Leaf 对象和其他 Composite 对象,形成递归结构。

组合模式的工作原理是,当客户端对组合对象执行操作时,该操作会递归传递到组合对象的所有子节点,从而实现对整个树状结构的操作。这种递归特性使得组合模式非常适用于处理树状结构的场景,例如文件系统的目录结构、组织架构等。

使用组合模式的好处包括:

  • 透明性:客户端无需关心当前处理的是单个对象还是组合对象,统一处理,代码更简洁。
  • 灵活性:可以通过简单组合形成复杂结构,增加或删除组件更加灵活。
  • 可扩展性:容易添加新类型的组件,符合开闭原则。

组合模式一些限制和注意事项:

  • 可能造成性能损失:递归操作可能导致性能问题,特别是树状结构非常大时。
  • 不适合每个场景:并不是所有层次结构都适合使用组合模式,有时可能会引入过度一般化的设计。

在实现组合模式时,需要特别注意一些细节,例如确保组合对象和叶子对象在接口上保持一致,以及在组合对象中递归地调用子对象的操作等。

下面我们就一起来看两个在实际开发中遇到的场景,以此来学习了解组合模式(Composite Pattern)

场景一:电商应用

一个电商应用,它可能有一个产品目录,每个产品可以有多个子产品(如颜色或尺寸的变体)。在这种情况下,我们可以定义一个 Product Widget,该 Widget 可以包含一个或多个 ProductVariant Widget。

// Component
abstract class ProductComponent extends StatelessWidget {
  final String title;

  ProductComponent({
    Key? key,
    required this.title,
  }) : super(key: key);
}

// Leaf
class ProductVariant extends ProductComponent {
  final String variant;

  ProductVariant({required String title, required this.variant})
      : super(title: title);

  @override
  Widget build(BuildContext context) {
    return ListTile(
      title: Text(title),
      subtitle: Text(variant),
    );
  }
}

// Composite
class Product extends ProductComponent {
  final List<ProductVariant> variants;

  Product({required String title, required this.variants})
      : super(title: title);

  @override
  Widget build(BuildContext context) {
    return ExpansionTile(
      title: Text(title),
      children: variants,
    );
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Product Catalog'),
        ),
        body: ListView(
          children: [
            Product(
              title: 'T-Shirt',
              variants: [
                ProductVariant(title: 'T-Shirt', variant: 'Small'),
                ProductVariant(title: 'T-Shirt', variant: 'Medium'),
                ProductVariant(title: 'T-Shirt', variant: 'Large'),
              ],
            ),
            Product(
              title: 'Shoes',
              variants: [
                ProductVariant(title: 'Shoes', variant: '8'),
                ProductVariant(title: 'Shoes', variant: '9'),
                ProductVariant(title: 'Shoes', variant: '10'),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

void main() {
  runApp(MyApp());
}

ProductComponent 组件,是所有产品和产品变体的通用接口。Product 是组合对象,它包含了多个 ProductVariant,它们是叶子对象,代表了没有子节点的对象。

组合模式的优点: 你可以在不关心对象是组合对象还是叶子对象的情况下,通过一致的方式处理对象。无论它是单个产品变体,还是包含多个产品变体的产品。

场景二:文件系统

模拟文件系统的场景。在这个场景中,我们有文件(File)和文件夹(Directory)。每个文件夹都可以包含多个文件或其他文件夹,形成一个层次结构。

创建一个抽象类 FileSystemEntityWidget,它继承自 Widget。然后我们的 FileWidgetDirectoryWidget 可以继承自这个抽象类。在 DirectoryWidget 中,可以接收一个 FileSystemEntityWidget 的列表作为子节点。

// Component
abstract class FileSystemEntityWidget extends StatelessWidget {
  final String name;

  FileSystemEntityWidget({
    Key? key,
    required this.name,
  }) : super(key: key);
}

// Leaf
class FileWidget extends FileSystemEntityWidget {
  FileWidget({required String name}) : super(name: name);

  @override
  Widget build(BuildContext context) {
    return ListTile(
      leading: Icon(Icons.insert_drive_file),
      title: Text(name),
    );
  }
}

// Composite
class DirectoryWidget extends FileSystemEntityWidget {
  final List<FileSystemEntityWidget> children;

  DirectoryWidget({required String name, required this.children})
      : super(name: name);

  @override
  Widget build(BuildContext context) {
    return ExpansionTile(
      leading: const Icon(Icons.folder),
      title: Text(name),
      children: children,
    );
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('File Explorer'),
        ),
        body: ListView(
          children: [
            DirectoryWidget(
              name: 'Documents',
              children: [
                FileWidget(name: 'Resume.pdf'),
                FileWidget(name: 'CoverLetter.pdf'),
              ],
            ),
            DirectoryWidget(
              name: 'Music',
              children: [
                DirectoryWidget(
                  name: 'Rock',
                  children: [
                    FileWidget(name: 'song1.mp3'),
                    FileWidget(name: 'song2.mp3'),
                  ],
                ),
                DirectoryWidget(
                  name: 'Classical',
                  children: [
                    FileWidget(name: 'song3.mp3'),
                    FileWidget(name: 'song4.mp3'),
                  ],
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

void main() {
  runApp(MyApp());
}

FileSystemEntityWidget 是我们的组件,是所有文件和文件夹的共通接口。FileWidget 是叶子对象,代表没有子节点的对象。DirectoryWidget 是组合对象,它包含了多个 FileSystemEntityWidget 对象,形成了递归结构。

组合模式的优点:你可以在不关心对象是组合对象还是叶子对象的情况下,通过一致的方式处理对象。

总结

组合模式是一种结构型设计模式,它允许你以树形方式组合对象,然后像处理单个对象一样处理这些对象。在本文中,我们将使用两个具体的例子来演示如何在 Flutter 中使用组合模式:一个电商应用程序和一个模拟文件系统。

电商应用

电商应用程序,有产品(Product),每个产品又可以有多个变体(Variant),例如不同的颜色或尺寸。在这种情况下,可以将产品看作是组合对象(Composite),因为它可以包含多个变体,而变体则是叶子对象(Leaf),因为它们不能包含其他对象。

首先定义了一个 ProductComponent 的抽象类,它是所有产品和变体的基类。然后,定义了 ProductProductVariant 类,它们都继承自 ProductComponentProduct 类包含了一个 ProductVariant 的列表,表示它可以包含多个变体。

文件系统

模拟文件系统,有文件(File)和文件夹(Directory)。每个文件夹都可以包含多个文件或其他文件夹,形成一个层次结构。

在这种情况下,可以将文件夹看作是组合对象(Composite),因为它可以包含多个文件或其他文件夹,而文件则是叶子对象(Leaf),因为它们不能包含其他对象。

首先定义了一个 FileSystemEntityWidget 的抽象类,它是所有文件和文件夹的基类。然后,定义了 FileWidgetDirectoryWidget 类,它们都继承自 FileSystemEntityWidgetDirectoryWidget 类包含了一个 FileSystemEntityWidget 的列表,表示它可以包含多个文件或其他文件夹。

结论

组合模式是一种强大的设计模式,它可以帮助我们设计出灵活和可复用的代码。通过使用组合模式,可以将复杂的问题分解为更小的部分,然后以一种统一和一致的方式处理这些部分。在 Flutter 中,可以利用组合模式来创建复杂的 UI,提高代码的可读性和可维护性。

统一处理对象和组合对象、增加新类型的组件容易等。但是,它也有局限性,例如可能难以限制组合对象的层次结构或使系统过于一般化。因此,在具体的应用场景中,需要根据具体需求权衡使用组合模式的利弊。

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

转载自:https://juejin.cn/post/7263000483345367095
评论
请登录