Flutter开发实战:解释器模式(Interpreter Pattern)

解释器模式(Interpreter Pattern)是一种设计模式,用于为特定的问题定义一个语言,并提供该语言的解释器。这种模式通常用于为特定类型的问题实现一种简单的语言或脚本。例如,正则表达式、SQL查询语句和一些编程语言都有自己的解释器。

解释器模式的关键组成部分包括:

  1. 抽象表达式 (Abstract Expression):定义解释操作的接口。
  2. 终结符表达式 (Terminal Expression):实现与语法中的终结符相关的解释操作。一个特定的符号对应于一个终结符表达式。
  3. 非终结符表达式 (Nonterminal Expression):为文法中的非终结符实现解释操作。它可以有一个或多个子表达式。
  4. 上下文 (Context):包含解释器之外的一些全局信息。
  5. 客户端 (Client):构建(或解析)抽象语法树,然后使用解释器解释该树。

解释器.png

适用场景:

  1. 当有一个语言需要解释执行,并且可以将该语言表示为简单的句子时,可以使用解释器模式。
  2. 文法简单。对于复杂的文法,使用解释器模式可能会产生大量的类。为每个规则至少有一个类,这使得文法的管理变得复杂。

优点:

  1. 易于实现简单文法。
  2. 提供了修改和扩展文法的灵活性。

缺点:

  1. 对于复杂的文法,可能产生大量的类文件。
  2. 低效。解释执行代码通常比直接执行的机器码慢。

解释器模式常用于解释某种自定义语言或标记。下面我们就一起来看一下在Flutter中如何使用此设计模式。

场景一:动态生成UI

假设我们正在构建一个应用,允许用户自定义某些 UI 组件的布局和颜色。用户可以使用一个简单的描述语言为每个组件指定属性。

例如 “Button:width=100;height=50;color=red”。
这样,用户可以动态地修改 UI,而不需要重新编译应用。

/// 抽象表达式 (Abstract Expression)

abstract class Expression {

  dynamic interpret(Context context);

}



///终结符表达式 (Terminal Expression)

class ValueExpression implements Expression {

  final String value;



  ValueExpression(this.value);



  @override

  dynamic interpret(Context context) {

    return value;

  }

}



///非终结符表达式 (Nonterminal Expression)

class UIComponentExpression implements Expression {

  final Expression type;

  final List<Expression> properties;



  UIComponentExpression(this.type, this.properties);



  @override

  dynamic interpret(Context context) {

    Map<String, dynamic> result = {};

    result['type'] = type.interpret(context);

    for (Expression prop in properties) {

      result.addAll(prop.interpret(context));

    }

    return result;

  }

}



class PropertyExpression implements Expression {

  final String key;

  final Expression value;



  PropertyExpression(this.key, this.value);



  @override

  dynamic interpret(Context context) {

    return {key: value.interpret(context)};

  }

}



///上下文 (Context)

class Context {

  final String input;



  Context(this.input);

}



void main() => runApp(MyApp());



class MyApp extends StatelessWidget {

  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      title: 'Interpreter Pattern in Flutter',

      theme: ThemeData(

        primarySwatch: Colors.blue,

      ),

      home: MyHomePage(),

    );

  }

}



class MyHomePage extends StatefulWidget {

  @override

  _MyHomePageState createState() => _MyHomePageState();

}



class _MyHomePageState extends State<MyHomePage> {

  final TextEditingController _controller = TextEditingController();

  Map<String, dynamic> _uiProperties = {};

  Expression? _parseDescription(String description) {
    List<String> parts = description.split(';');
    if (parts.isEmpty) return null;

    List<String> typePart = parts[0].split(':');
    if (typePart.length != 2) return null;


    Expression type = ValueExpression(typePart[0]);
    List<Expression> properties = [];

    for (int i = 1; i < parts.length; i++) {
      List<String> prop = parts[i].split('=');
      if (prop.length == 2) {
        properties.add(PropertyExpression(prop[0], ValueExpression(prop[1])));
      }
    }


    return UIComponentExpression(type, properties);
  }

  void _updateUI() {
    // _controller.text = "Button:width=100;height=50;color=red";
    Expression? expression = _parseDescription(_controller.text);
    Context context = Context("");
    _uiProperties = expression?.interpret(context);
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    double width = double.tryParse(_uiProperties['width'] ?? '') ?? 50.0;
    double height = double.tryParse(_uiProperties['height'] ?? '') ?? 30.0;
    Color color = {
          'red': Colors.red,
          'blue': Colors.blue,
          'green': Colors.green,
          'yellow': Colors.yellow,
        }[_uiProperties['color']] ??
        Colors.grey;

    return Scaffold(
      appBar: AppBar(
        title: const Text('Interpreter Pattern in Flutter'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: TextField(
              controller: _controller,
              decoration: const InputDecoration(
                labelText: 'Enter UI description',
                hintText: 'e.g. Button:width=100;height=50;color=red',
              ),
            ),
          ),
          ElevatedButton(
            onPressed: _updateUI,
            child: const Text('Generate UI'),
          ),
          const SizedBox(height: 20),
          if (_uiProperties['type'] == 'Button')
            Container(
              width: width,
              height: height,
              child: ElevatedButton(
                onPressed: () {},
                style: ElevatedButton.styleFrom(
                  backgroundColor: color,
                ),
                child: Text('Button'),
              ),
            ),
        ],
      ),
    );
  }
}

这个例子比较相对简单,但它展示了如何使用解释器模式解析一个自定义语言的基本结构。在实际开发中,可能需要处理更多的组件类型、属性和复杂性。为了真正实现这个功能,还需要一个词法分析器和语法分析器,它们可以将用户的输入转化为上述的表达式树。

场景二:内容过滤

开发一个内容管理系统,允许用户定义自己的过滤规则,以决定哪些内容应该显示或隐藏。为此,我们可以创建一个简单的描述语言,允许用户定义基于关键词的规则。

用户可以输入以下描述字符串来定义规则:

  • “Show:topic=sports;age=above18″,表示仅显示与运动相关的内容,并且只适合18岁及以上的人士。
  • “Hide:topic=politics”,表示隐藏所有与政治相关的内容。
/// 抽象表达式 (Abstract Expression)

abstract class Expression {

  dynamic interpret(Context context);

}



///终结符表达式 (Terminal Expression)

class ValueExpression implements Expression {

  final String value;



  ValueExpression(this.value);



  @override

  dynamic interpret(Context context) {

    return value;

  }

}



///非终结符表达式 (Nonterminal Expression)

class UIComponentExpression implements Expression {

  final Expression type;

  final List<Expression> properties;



  UIComponentExpression(this.type, this.properties);



  @override

  dynamic interpret(Context context) {

    Map<String, dynamic> result = {};

    result['type'] = type.interpret(context);

    for (Expression prop in properties) {

      result.addAll(prop.interpret(context));

    }

    return result;

  }

}



class PropertyExpression implements Expression {

  final String key;

  final Expression value;



  PropertyExpression(this.key, this.value);



  @override

  dynamic interpret(Context context) {

    return {key: value.interpret(context)};

  }

}



///上下文 (Context)

class Context {

  final String input;



  Context(this.input);

}



void main() => runApp(MyApp());



class MyApp extends StatelessWidget {

  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      title: 'Interpreter Pattern in Flutter',

      theme: ThemeData(

        primarySwatch: Colors.blue,

      ),

      home: MyHomePage(),

    );

  }

}



class MyHomePage extends StatefulWidget {

  @override

  _MyHomePageState createState() => _MyHomePageState();

}



class _MyHomePageState extends State<MyHomePage> {

  final TextEditingController _controller = TextEditingController();

  Map<String, dynamic> _filterProperties = {};
  final List<String> _contents = [
    'A sports article suitable for all ages.',
    'A sports article for above 18.',
    'A political news article.',
    'A general news article.'
  ];
  List<String> _filteredContents = [];


  Expression? _parseDescription(String description) {
    List<String> parts = description.split(';');
    if (parts.isEmpty) return null;

    List<String> typePart = parts[0].split(':');
    if (typePart.length != 2) return null;

    Expression type = ValueExpression(typePart[0]);
    List<Expression> properties = [];


    for (int i = 1; i < parts.length; i++) {
      List<String> prop = parts[i].split('=');
      if (prop.length == 2) {
        properties.add(PropertyExpression(prop[0], ValueExpression(prop[1])));
      }
    }

    return UIComponentExpression(type, properties);
  }

  void _applyFilter() {
    // _controller.text = "Show:topic=sports;age=above18";
    Expression? expression = _parseDescription(_controller.text);
    Context context = Context("");
    _filterProperties = expression?.interpret(context);

    _filteredContents = _contents;

    if (_filterProperties['type'] == 'Hide' &&
        _filterProperties.containsKey('topic')) {
      _filteredContents = _filteredContents.where((content) {
        return !content.contains(_filterProperties['topic']);
      }).toList();
    } else if (_filterProperties['type'] == 'Show') {
      if (_filterProperties.containsKey('topic')) {
        _filteredContents = _filteredContents.where((content) {
          return content.contains(_filterProperties['topic']);
        }).toList();
      }
      if (_filterProperties.containsKey('age') &&
          _filterProperties['age'] == 'above18') {
        _filteredContents = _filteredContents.where((content) {
          return content.contains('above 18');
        }).toList();
      }
    }

    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Interpreter Pattern in Flutter'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: TextField(
              controller: _controller,
              decoration: const InputDecoration(
                labelText: 'Enter Filter Rule',
                hintText: 'e.g. Show:topic=sports;age=above18',
              ),
            ),
          ),
          ElevatedButton(
            onPressed: _applyFilter,
            child: const Text('Apply Filter'),
          ),
          const SizedBox(height: 20),
          Expanded(
            child: ListView.builder(
              itemCount: _filteredContents.length,
              itemBuilder: (context, index) {
                return ListTile(
                  title: Text(_filteredContents[index]),
                );
              },
            ),
          )
        ],
      ),
    );
  }
}

在这个示例中,用户可以输入一个描述字符串来定义显示或隐藏内容的规则。使用了解释器模式来解析这个描述字符串,并据此过滤内容列表。

总结

在构建应用程序时,特别是那些涉及到自定义配置或用户定义行为的应用,我们经常需要解释一些特定格式的数据或字符串。解释器模式是一个设计模式,它提供了评估语言的语法或表达式的方式。在这篇文章中,我们探讨了如何在 Flutter 中使用解释器模式构建两个不同的应用示例:动态 UI 构建和内容过滤。

动态 UI 构建

允许用户通过描述字符串动态创建UI。描述字符串可以像 “Button:width=100;height=50;color=red” 这样,来指定要创建的UI的宽度和颜色。通过解释器模式,可以将这个描述字符串转化为实际的 UI 组件,让用户看到所描述的实际UI。

内容过滤

让用户通过描述字符串定义内容过滤规则。例如,可以定义 “Show:topic=sports;age=above18” 或 “Hide:topic=politics” 这样的规则。这意味着根据用户的规则,可以显示与运动相关且只适合18岁及以上人士的内容,或者隐藏所有与政治相关的内容。再次使用解释器模式,可以解析用户的规则并据此过滤内容。

结论

通过上面两个示例,可以看到了解释器模式在 Flutter 中的实际应用。这种模式在需要解析特定格式的数据或字符串时非常有用,尤其是当这些数据或字符串需要转化为实际的操作或对象时。解释器模式为我们提供了一个清晰、结构化的方法来处理这些转化,使代码更加模块化和可维护。

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

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

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

昵称

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