Flutter源码中的25种设计模式【不间断更新】

Flutter Java源码中的25种设计模式【不间断更新】

1.装饰器模式(结构型)【出场率:🌟🌟🌟🌟🌟】

装饰器模式(Decotator)能够在运行时动态地为原始对象添加一些额外的功能,使其变得强大。装饰器非常类似于“继承”,它们都是为了增强原始对象的功能。区别在于方式不同,后者是在编译时(compile-time)静态地通过对原始类的继承完成,而前者则是在程序运行时(run-time)通过对原始对象动态地“包装”完成。

常见场景:Flutter的Widget的嵌套
意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
主要功能:在不想增加很多子类的情况下扩展类。
使用方法:这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
关键词:
  • 给一个对象添加一些额外的职责【无需修改该类的源码】;
  • 避免生成过多的子类,但是会生成很多个类
  • 大多以Wrapper的形式存在,并把自身传入【摘抄自Medium技术博客:Flutter Design Patterns: 16 — Decorator

Decorator, also known as Wrapper, is a structural design pattern, which intention in the GoF book is described like this:

来自:Flutter Design Patterns: 16 — Decorator

案例1: 给子组件的文字设置如下样式:TextStyle(fontSize: 50,color: Colors.red)

//在ListView外层包裹一个Scrollbar,并且将自身作为参数传入Scrollbar
DefaultTextStyle(
        style: TextStyle(fontSize: 50,color: Colors.red),
        child: Column(
          children: [
            Text("data"),
            Text("data"),
            Text("data"),
          ],
        ),
      )

案例2:给ListView添加滚动指示器

//在ListView外层包裹一个Scrollbar,并且将自身作为参数传入Scrollbar
Scrollbar(
        child: ListView.builder(
          itemBuilder: (BuildContext context, int index) {
            return Text("index:$index");
          },
          itemCount: 200,
        ),
      )

案例3:UI与数据双向绑定【GetX状态管理框架的API】

Obx等于是粘合剂,将pointString和Text紧紧的粘在一起
数据跟UI紧紧的粘在一起
Obx(() {
//pointString用户的积分,一旦积分变化,UI会自动刷新并显示最新的积分,Obx就是完成这一工作的

        String pointString = memberGlobalLogic
            .points.value;
        return Text(
          pointString,
          style: TextStyle(
              color: Colors.white,
              fontSize: 17,
              fontWeight: FontWeight.w500),
        );
      })

案例4:

Flutter的Widget嵌套就是受此设计模式启发,
你需要添加A功能,
就往外面套一层A功能的Widget。
绝对是在Flutter中最常见的设计模式

2.组合模式(结构型)【出场率:🌟🌟🌟🌟🌟】

组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。

案例1: Flutter的Widget树🌲,借鉴了树形结构来组合模式。代码更显层次感【层级感】

Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: DefaultTextStyle(
        style: TextStyle(fontSize: 50,color: Colors.red),
        child: Column(
          children: [
            Text("data"),
            Text("data"),
            Text("data"),
          ],
        ),
      ),
    )

3.观察者模式(行为型)【出场率:🌟🌟🌟🌟】

Bloc、GetX都用到了Stream。Stream的listen方法便是观察者模式,当数据发生改变,自动通知你。

案例1:通过观察者模式实现计数器功能。

⚠️警告:Stream应该在合适的时候销毁,由于篇幅有限,省略此代码!
class _MyHomePageState extends State<MyHomePage> {
  int count = 0;

  StreamController dataStreamController = StreamController();

  @override
  void initState() {

    super.initState();

    dataStreamController.stream.listen((event) {
      print('你点击了第 ${event}次');
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            dataStreamController.add(++count);
          },
          child: const Icon(Icons.add),
        ));
  }
}

4.模板模式(行为型)【出场率:🌟🌟🌟】

意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
主要解决:一些方法通用,却在每一个子类都重新写了这一方法,减少核心库的大小
使用方法:创建抽象类A,只定义方法,实体类B实现A定义的方法
关键词:
  • 声明和实现分开
  • 公用方法抽成模版
  • 抽象类定义方法名,实体类实现(implement)或继承(extend)抽象类
  • 为核心库瘦身【本地核心库定义,第三方依赖】

案例1:我们写过无数次的StatefulWidget模版

StatefulWidget提供了两个个模版方法createElement、createState,我们需要重写createState方法

abstract class StatefulWidget extends Widget {

  const StatefulWidget({ Key? key }) : super(key: key);

  @override
  StatefulElement createElement() => StatefulElement(this);

  @protected
  @factory
  State createState(); // ignore: no_logic_in_create_state, this is the original sin
}

这句话啥意思,知道的告诉我一声 // ignore: no_logic_in_create_state, this is the original sin

class NewPage extends StatefulWidget {
  const NewPage({Key? key}) : super(key: key);

  @override
  _NewPageState createState() => _NewPageState();
}

class _NewPageState extends State<NewPage> {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

如果不重写createState 编译器会报错如下:

Missing concreate implementation of 'StatefulWidget.cteateState'. Try implementing the missing method,or make the class abstract.

案例2:我们重写过无数次的build方法

State提供了多个模版方法initState、didUpdateWidget、setState、deactivate、activate、dispose、build、didChangeDependencies、debugFillProperties,必须重写build方法

如果不重写build方法 编译器会报错如下:

Missing concreate implementation of 'State.build'. Try implementing the missing method,or make the class abstract.

abstract class States<T extends StateWidget> {

  T get widget => _widget;
  T _widget;

  BuildContext get context => _element;
  StateElement _element;
  
  @protected
  @mustCallSuper
  void initState() {}

  @protected
  @mustCallSuper
  void didUpdateWidget(covariant T oldWidget) {}

  @protected
  @mustCallSuper
  void reassemble() {}

  @protected
  @mustCallSuper
  void setState(VoidCallback fn) {}

  @protected
  @mustCallSuper
  void deactivate() {}

  @protected
  @mustCallSuper
  void dispose() {}

  @protected
  Widget build(BuildContext context);

  @protected
  @mustCallSuper
  void didChangeDependencies() { }

}

这句话啥意思,知道的告诉我一声 // ignore: no_logic_in_create_state, this is the original sin

class NewPage extends StatefulWidget {
  const NewPage({Key? key}) : super(key: key);

  @override
  _NewPageState createState() => _NewPageState();
}

class _NewPageState extends State<NewPage> {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

案例2【通过implements实现接口】: 抽象类Sink定义add、close方法,实体类DelegatingSink来实现。

⚠️文件位于Flutter的sky_engine核心库内:
/Users/jingkairen/flutter/bin/cache/pkg/sky_engine/lib/core/sink.dart

abstract class Sink<T> {

  void add(T data);

  void close();
}

⚠️文件在pub-cache【第三方依赖文件夹📁内】:
/Users/jingkairen/flutter/.pub-cache/hosted/pub.flutter-io.cn/async-2.8.1/lib/src/delegate/sink.dart

/// Simple delegating wrapper around a [Sink].
///
/// Subclasses can override individual methods, or use this to expose only the
/// [Sink] methods of a subclass.
class DelegatingSink<T> implements Sink<T> {
  final Sink _sink;

  DelegatingSink(Sink<T> sink) : _sink = sink;

  DelegatingSink._(this._sink);

  @Deprecated(
      'Use StreamController<T>(sync: true)..stream.cast<S>().pipe(sink)')
  static Sink<T> typed<T>(Sink sink) =>
      sink is Sink<T> ? sink : DelegatingSink._(sink);

  @override
  void add(T data) {
    _sink.add(data);
  }

  @override
  void close() {
    _sink.close();
  }
}
abstract class Sink<T> 这里的<T> 是啥,代表泛型,可以传入任何类型。(泛型语法糖下次再讲)
注意到警告⚠️标志了吗,抽象类放在Flutter包内,实现类需要三方依赖,这体现Flutter官方的瘦身方式吧😄

案例3【通过extends实现接口】: 你细品。

abstract class Sink<T> {

  void add(T data);

  void close();
}
abstract class ChunkedConversionSink<T> implements Sink<T> {
  ChunkedConversionSink();
  factory ChunkedConversionSink.withCallback(
      void callback(List<T> accumulated)) = _SimpleCallbackSink<T>;

  void add(T chunk);

  void close();
}
abstract class ByteConversionSink extends ChunkedConversionSink<List<int>> {
  ByteConversionSink();
  factory ByteConversionSink.withCallback(
      void callback(List<int> accumulated)) = _ByteCallbackSink;
  factory ByteConversionSink.from(Sink<List<int>> sink) = _ByteAdapterSink;

  void addSlice(List<int> chunk, int start, int end, bool isLast);
}
class _BufferSink extends ByteConversionSink {
  final BytesBuilder builder = new BytesBuilder(copy: false);

  void add(List<int> chunk) {
    builder.add(chunk);
  }

  void addSlice(List<int> chunk, int start, int end, bool isLast) {
    if (chunk is Uint8List) {
      Uint8List list = chunk;
      builder.add(new Uint8List.view(
          list.buffer, list.offsetInBytes + start, end - start));
    } else {
      builder.add(chunk.sublist(start, end));
    }
  }

  void close() {}
}

5.享元模式(出场率:🌟🌟🌟🌟)

享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。

意图:减少创建对象的数量,以减少内存占用和提高性能。
实现原理:尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。用 HashMap 存储这些对象,用Key标识。
使用方法:Flutter特有的Diff算法基于此设计模式,必要时只需设置正确的Key即可。iOS的UICollectionViewCell、UITableViewCell正是用这种设计模式。
/// Whether the `newWidget` can be used to update an [Element] that currently
  /// has the `oldWidget` as its configuration.
  ///
  /// An element that uses a given widget as its configuration can be updated to
  /// use another widget as its configuration if, and only if, the two widgets
  /// have [runtimeType] and [key] properties that are [operator==].
  ///
  /// If the widgets have no key (their key is null), then they are considered a
  /// match if they have the same type, even if their children are completely
  /// different.
  static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
        && oldWidget.key == newWidget.key;
  }

6.原型模式(出场率:暂无)

意图:创建对象的代价比较大时,克隆对象
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
享元模式与原型模式的异同点
  • 都是通过HashMap来缓存对象,通过Key来标识
  • 原型模式通过克隆来创建对象,享元模式不通过克隆来创建对象

7.责任链模式(出场率:🌟)行为型

主要解决:降低对象之间的耦合性。
实现原理:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
案例1: NotificationListener的使用
NotificationListener<MyNotification>(
          onNotification: (notification) {
            print('检测到点击 ${notification.msg}');
            return true;
          },
          child: Builder(builder: (BuildContext context){
            return RaisedButton(
              onPressed: () {
                MyNotification("触发按钮").dispatch(context);
              },
              child: Text('RaisedButton'),
            );
          }),
        )
class MyNotification extends Notification {
  MyNotification(this.msg);

  final String msg;
}

A notification that can bubble up the widget tree.

实现原理如下:
  • 通过BuildContext bubble up the widget tree的方式依次传递
  • BuildContext即Element
  • Element有个属性 Element? _parent,保存它的父级Element(依次向上传递,寻找可以处理的Widget)
  • 第一步:Notification调用dispatch方法,并传入BuildContext;
  • 第二步:visitAncestorElements寻找可以处理的父控件;
  • 第三步:通过while循环调用visitAncestor找到可以处理NotificationListener来处理;
  bool visitAncestor(Element element) {
    if (element is StatelessElement) {
      final StatelessWidget widget = element.widget;
      if (widget is NotificationListener<Notification>) {
        if (widget._dispatch(this, element)) // that function checks the type dynamically
          return false;
      }
    }
    return true;
  }
  /// Start bubbling this notification at the given build context.
  ///
  /// The notification will be delivered to any [NotificationListener] widgets
  /// with the appropriate type parameters that are ancestors of the given
  /// [BuildContext]. If the [BuildContext] is null, the notification is not
  /// dispatched.
  void dispatch(BuildContext? target) {
    // The `target` may be null if the subtree the notification is supposed to be
    // dispatched in is in the process of being disposed.
    target?.visitAncestorElements(visitAncestor);
  }

  void visitAncestorElements(bool Function(Element element) visitor) {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    Element? ancestor = _parent;
    while (ancestor != null && visitor(ancestor))
      ancestor = ancestor._parent;
  }

8.状态模式

在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。Flutter就是数据驱动、状态驱动就是借鉴这种模式。

9.建造者模式

建造者模式(Builder Pattern)创建对象时必须传入很多参数,使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。

例如:麦当劳小程序点汉堡时,需要进行多种馅料选择!
// Listview的建造者方法,创建列表需要知道列表长度(必选)、UI建造者函数(必选)、监听控制器、碰撞物理样式等参数
ListView.builder(
      itemBuilder: (context, index) {
      //  return Text(index.toString());
      },
      physics: const BouncingScrollPhysics(),
      itemCount: 100,
      controller: scrollController,
    );
/// Builder to accumulate properties and configuration used to assemble a
/// [DiagnosticsNode] from a [Diagnosticable] object.
class DiagnosticPropertiesBuilder {
  /// Creates a [DiagnosticPropertiesBuilder] with [properties] initialize to
  /// an empty array.
  DiagnosticPropertiesBuilder() : properties = <DiagnosticsNode>[];

  /// Creates a [DiagnosticPropertiesBuilder] with a given [properties].
  DiagnosticPropertiesBuilder.fromProperties(this.properties);

  /// Add a property to the list of properties.
  void add(DiagnosticsNode property) {
    assert(() {
      properties.add(property);
      return true;
    }());
  }

  /// List of properties accumulated so far.
  final List<DiagnosticsNode> properties;

  /// Default style to use for the [DiagnosticsNode] if no style is specified.
  DiagnosticsTreeStyle defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.sparse;

  /// Description to show if the node has no displayed properties or children.
  String? emptyBodyDescription;
}
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(EnumProperty<MaterialType>('type', type));
    properties.add(DoubleProperty('elevation', elevation, defaultValue: 0.0));
    properties.add(ColorProperty('color', color, defaultValue: null));
    properties.add(ColorProperty('shadowColor', shadowColor, defaultValue: null));
    textStyle?.debugFillProperties(properties, prefix: 'textStyle.');
    properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null));
    properties.add(DiagnosticsProperty<bool>('borderOnForeground', borderOnForeground, defaultValue: true));
    properties.add(DiagnosticsProperty<BorderRadiusGeometry>('borderRadius', borderRadius, defaultValue: null));
  }

10.代理模式【结构型】

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。

在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。

案例1:GridView网格布局,其布局通过SliverGridDelegateWithFixedCrossAxisCount、SliverGridDelegateWithMaxCrossAxisExtent。实现布局管理

GridView(
            gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
                maxCrossAxisExtent: 40
            ),
            // gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
            //     crossAxisCount: 4
            // ),
            children: <Widget>[
              Container(
                color: Colors.blue,
                child: Icon(Icons.ac_unit),
              ),
              Container(
                color: Colors.yellow,
                child: Icon(Icons.airport_shuttle),
              ),
              Container(
                color: Colors.red,
                child: Icon(Icons.all_inclusive),
              ),
              Container(
                color: Colors.grey,
                child: Icon(Icons.beach_access),
              ),
              Container(
                color: Colors.blueAccent,
                child: Icon(Icons.cake),
              ),
              Container(
                color: Colors.deepOrange,
                child: Icon(Icons.free_breakfast),
              ),
            ],
          )

11.单例模式【创建型】

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
使用场景

  • 线程安全(写文件操作)
  • 在内存里只有一个实例,减少了内存的开销
  • 例如一个App只需要一个网络请求类

12.工厂模式【简单工厂】

违反开闭原则,只有一个工厂,生产所有产品,增加和删除产品都要修改工厂代码。(Factory Pattern)模式属于创建型模式,在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

12B.工厂方法

遵守开闭原则,一个工厂只生产一种产品,在添加新产品时,无需修改现有代码,只需增加新的具体工厂和产品子类,符合开闭原则。

13.抽象工厂模式

抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。

例如,比亚迪就是超级工厂,创造王朝工厂和海洋工厂。王朝工厂生产王朝车系列:唐、宋、元等车,海洋工厂生产海洋车系列:海豹、海豚、海鸥。

14.外观模式【新增类,新增访问接口】

外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个统一的接口(上层接口)。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。简化多个类的API,例如多个不相关的类需要协作。例如家庭影院系统,看电影需要多个家电配合运行:打开投影仪、拉下投影幕布、关灯、打开音响。那么家庭影院的类提供看电影的方法就行。

15.适配器模式(结构型)【】

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。

意图:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

主要解决:主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的。

案例1: SliverToBoxAdapter

CustomScrollView的子控件要求一定是Sliver,如果是普通Widget必须通过适配器SliverToBoxAdapter转化成Sliver才可以显示

CustomScrollView(
          slivers: [
            SliverAppBar(
              title: Text('Sliver Demo'),
              centerTitle: true,
              // 展开的高度
              expandedHeight: 300.0,
              // 强制显示阴影
              forceElevated: true,
              flexibleSpace: FlexibleSpaceBar(
                // 背景折叠动画
                collapseMode: CollapseMode.parallax,
                background: Icon(Icons.add_to_home_screen),
              ),
            ),
            SliverToBoxAdapter(child: Text("data")),
            // 这个部件一般用于最后填充用的,会占有一个屏幕的高度,
            // 可以在 child 属性加入需要展示的部件
            SliverFillRemaining(
              child: Center(child: Text('FillRemaining', style: TextStyle(fontSize: 30.0))),
            ),
          ],
        )

16.桥接模式 (结构型)

意图:解决类层级爆炸的问题,新增新功能增加新类即可。

如何解决:把这种多角度分类分离出来,抽成不同的类,让它们独立变化,减少它们之间耦合。

例如画图案时需要选定颜色、形状、边框类最后画出图形

适配器模式和桥接模式的异同:

共同点
桥接和适配器都是让两个东西配合工作

不同点
出发点不同。
         1)适配器:改变已有的两个接口,让他们相容。
         2)桥接模式:分离抽象化和实现,使两者的接口可以不同,目的是分离。

        所以说,如果你拿到两个已有模块,想让他们同时工作,那么你使用的适配器。
        如果你还什么都没有,但是想分开实现,那么桥接是一个选择。

        桥接是先有桥,才有两端的东西
        适配是先有两边的东西,才有适配器

17.空对象模式()

在空对象模式(Null Object Pattern)中,一个空对象取代 NULL 对象实例的检查。Null 对象不是检查空值,而是反应一个不做任何动作的关系。这样的 Null 对象也可以在数据不可用的时候提供默认的行为。

在空对象模式中,我们创建一个指定各种要执行的操作的抽象类和扩展该类的实体类,还创建一个未对该类做任何实现的空对象类,该空对象类将无缝地使用在需要检查空值的地方。

18.解释器模式(行为型)

解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。

意图:给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。

主要解决:对于一些固定文法构建一个解释句子的解释器。

何时使用:如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。

如何解决:构建语法树,定义终结符与非终结符。

关键代码:构建环境类,包含解释器之外的一些全局信息,一般是 HashMap。

应用实例:SQL查询语句、正则表达式、编译器、运算表达式计算。

19.访问者模式

在访问者模式(Visitor Pattern)中,分离数据结构和数据操作,访问者类就是包装执行算法的类。我们使用了一个访问者类,代表着元素类要执行的易变算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。

意图:主要将数据结构与数据操作分离。

主要解决:稳定的数据结构和易变的操作耦合问题。

何时使用:需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,使用访问者模式将这些封装到类中。

如何解决:在被访问的类里面加一个对外提供接待访问者的接口。

关键代码:在数据基础类里面有一个方法接受访问者,将自身引用传入访问者。

应用实例:您在朋友家做客,您是访问者,朋友接受您的访问,您通过朋友的描述,然后对朋友的描述做出一个判断,这就是访问者模式。

优点: 1、符合单一职责原则。 2、优秀的扩展性。 3、灵活性。

缺点: 1、具体元素对访问者公布细节,违反了迪米特原则。 2、具体元素变更比较困难。 3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象。

20.在策略模式(行为型)

一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。

在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。

何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。

如何解决:将这些算法封装成一个一个的类,任意地替换。

关键代码:实现同一个接口。

21.备忘录模式

备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。

意图:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。

主要解决:所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。

何时使用:很多时候我们总是需要记录一个对象的内部状态,这样做的目的就是为了允许用户取消不确定或者错误的操作,能够恢复到他原先的状态,使得他有"后悔药"可吃。

如何解决:通过一个备忘录类专门存储对象状态。

关键代码:客户不与备忘录类耦合,与备忘录管理类耦合。

应用实例: 1、后悔药。 2、打游戏时的存档。 3、Windows 里的 ctrl + z。 4、IE 中的后退。 5、数据库的事务管理。

优点: 1、给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。 2、实现了信息的封装,使得用户不需要关心状态的保存细节。

缺点:消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。

使用场景: 1、需要保存/恢复数据的相关状态场景。 2、提供一个可回滚的操作。

注意事项: 1、为了符合迪米特原则,还要增加一个管理备忘录的类。 2、为了节约内存,可使用原型模式+备忘录模式。

22.中介者模式

中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式。

意图:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

主要解决:对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,我们也需要跟踪与之相关联的对象,同时做出相应的处理。

何时使用:多个类相互耦合,形成了网状结构。

如何解决:将上述网状结构分离为星型结构。

关键代码:对象 Colleague 之间的通信封装到一个类中单独处理。

应用实例: 1、中国加入 WTO 之前是各个国家相互贸易,结构复杂,现在是各个国家通过 WTO 来互相贸易。 2、机场调度系统。 3、MVC 框架,其中C(控制器)就是 M(模型)和 V(视图)的中介者。

优点: 1、降低了类的复杂度,将一对多转化成了一对一。 2、各个类之间的解耦。 3、符合迪米特原则。

缺点:中介者会庞大,变得复杂难以维护。

使用场景: 1、系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象。 2、想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。

注意事项:不应当在职责混乱的时候使用。

23.迭代器模式

意图:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。

主要解决:不同的方式来遍历整个整合对象。

何时使用:遍历一个聚合对象。

如何解决:把在元素之间游走的责任交给迭代器,而不是聚合对象。

关键代码:定义接口:hasNext, next。

案例1:Reative语法【高阶函数等,RxJava、RxSwift、ReativeCocoa、Rxdart等】

List list = [1,2,3,4,5];
    list.forEach((element) {
      print('_MyHomePageState.initState element:$element');
    });

24. 命令模式

命令模式(Command Pattern)强调命令的通用性,是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。

意图:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。

主要解决:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。

何时使用:在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。

如何解决:通过调用者调用接受者执行命令,顺序:调用者→命令→接受者。

关键代码:定义三个角色:1、received 真正的命令执行对象 2、Command 3、invoker 使用命令对象的入口

命令模式主要将"行为请求者"与"行为实现者"解耦,Flutter主要通过装饰器模式+组合模式进行解耦。此设计模式在iOS中使用较多

案例1:为iOS添加日历📅事件

  • 命令对象: EKEvent
  • 接受者: EventStore
  • 接受方法: -(BOOL)saveEvent:(EKEvent *)event span:(EKSpan)span error:(NSError **)error
    // 创建事件
    EKEvent *event  = [EKEvent eventWithEventStore:eventStore];

    // 这里拿当前时间 (自己写的话格式最好用 yyyy/MM/dd hh:mmaaa)
    NSDate *date = [NSDate date];
    event.title  = @"title" ;              //  事件标题
    event.location = @"location" ;          //  事件地点
    event.notes = @"notes";               //  事件备注
    event.startDate = [date dateByAddingTimeInterval:60 * 2];   // 开始时间
    event.endDate   = [date dateByAddingTimeInterval:60 * 30];  // 结束时间
    
    //第一次提醒  (几分钟后)
    [event addAlarm:[EKAlarm alarmWithRelativeOffset:60.0f * -5.0f]];
    
    [event setCalendar:[eventStore defaultCalendarForNewEvents]];
    NSError *err;
    [eventStore saveEvent:event span:EKSpanThisEvent error:&err];

25.过滤器模式

过滤器模式(Filter Pattern)或标准模式(Criteria Pattern)是一种设计模式,这种模式允许开发人员使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来。这种类型的设计模式属于结构型模式,它结合多个标准来获得单一标准。

案例1:Reative语法【高阶函数等,RxJava、RxSwift、ReativeCocoa、Rxdart等】

List list = [1,2,3,4,5,6];
    List filtedList = list.where((element) {
      return element >= 2;
    }).toList();
    print('_MyHomePageState.initStatefiltedList ${filtedList}');

控制台:flutter: _MyHomePageState.initStatefiltedList [2, 3, 4, 5, 6]

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容