扣丁書屋

工廠模式家族 | Flutter 設計模式

在圍繞設計模式的話題中,工廠這個詞頻繁出現,從 簡單工廠 模式到 工廠方法 模式,再到 抽象工廠 模式。工廠名稱含義是制造產品的工業場所,應用在面向對象中,順理成章地成為了比較典型的創建型模式。

圖源:https://media2.giphy.com/media/3ohjUKYWSqORcgIIsE/giphy.gif

“從形式上講,工廠可以是一個返回我們想要對象的一個方法 / 函數,即可以作為構造函數的一種抽象。

本文將會帶領大家使用 Dart 理解它們的各自的實現和它們之間的關系。

簡單工廠 & factory 關鍵字

簡單工廠模式 不在 23 種 GOF 設計模式中,卻是我們最常使用的一種編程方式。其中主要涉及到一個特殊的方法,專門用來提供我們想要的實例對象 (對象工廠),我們可以將這個方法放到一個單獨的類 SimpleFactory 中,如下:

class SimpleFactory {

  /// 工廠方法
  static Product createProduct(int type) {
    if (type == 1) {
      return ConcreteProduct1();
    }
    if (type == 2) {
      return ConcreteProduct2();
    }
    return ConcreteProduct();
  }
}

我們認為該方法要創建的對象同屬一個 Product 類 (抽象類),并通過參數 type 指定要創建具體的對象類型。Dart 不支持 interface 關鍵詞,但我們可以使用 abstract 以抽象類的方式定義接口, 然后各個具體的類型繼承實現它即可:

/// 抽象類
abstract class Product {
  String? name;
}

/// 實現類
class ConcreteProduct implements Product {
  @override
  String? name = 'ConcreteProduct';
}

/// 實現類 1
class ConcreteProduct1 implements Product {
  @override
  String? name = 'ConcreteProduct1';
}

/// 實現類 2
class ConcreteProduct2 implements Product {
  @override
  String? name = 'ConcreteProduct2';
}

當我們想要在代碼中獲取對應的類型對象時,只需要通過這個方法傳入想要的類型值即可, 我們不必關心生產如何被生產以及哪個對象被選擇的具體邏輯:

void main() {
  final Product product = SimpleFactory.createProduct(1);
  print(product.name); // ConcreteProduct1
}

這就是 簡單工廠模式。說到這里,就不得不提到 Dart 中特有的 factory 關鍵詞了。

factory 關鍵詞 可以用來修飾 Dart 類的構造函數,意為 工廠構造函數,它能夠讓 的構造函數天然具有工廠的功能,使用方式如下:


class Product {
  /// 工廠構造函數(修飾 create 構造函數)
  factory Product.createFactory(int type) {
    if (type == 1) {
      return Product.product1;
    } else if (type == 2) {
      return Product._concrete2();
    }
    return Product._concrete();
  }

  /// 命名構造函數
  Product._concrete() : name = 'concrete';

  /// 命名構造函數 1
  Product._concrete1() : name = 'concrete1';

  /// 命名構造函數 2
  Product._concrete2() : name = 'concrete2';

  String name;
}

factory 修飾的構造函數需要返回一個當前類的對象實例, 我們可以根據參數調用對應的構造函數,返回對應的對象實例。

void main() {
  Product product = Product.createFactory(1);
  print(product.name); // concrete1
}

此外,工廠構造函數也并不要求我們每次都必須生成新的對象, 我們也可以在類中預先定義一些對象供工廠構造函數使用, 這樣每次在使用同樣的參數構建對象時,返回的會是同一個對象, 在 [單例模式] 的章節中我們已經介紹過:

class Product {
  /// 工廠構造函數
  factory Product.create(int type) {
    if (type == 1) {
      return product1;
    } else if (type == 2) {
      return product2();
    }
    return Product._concrete();
  }

  static final Product product1 = Product._concrete1();
  static final Product product2 = Product._concrete2();
}

factory 除了可以修飾命名構造函數外,也可以修飾默認的非命名構造函數,

class Product {
  factory Product(int type) {
    return Product._concrete();
  }

  String? name;
}

到這里為止,工廠構造函數的一個缺點已經凸顯了出來,即使用者并不能直觀地感覺到自己正在使用的是工廠函數。工廠構造函數的使用方法和普通構造函數沒有區別,但這個構造函數生產的實例相當于是一種單例:

void main() {
  Product product = Product(1);
  print(product.name); // concrete1
}

這樣的用法很容易造成使用者的困擾,因此,我們應當盡量使用特定的命名構造函數 作為工廠構造函數(如上面示例中的 createFactory)。

工廠方法模式

工廠方法模式同樣也是我們編程中最常用到的一種手段。

抽象工廠 UML,圖源:refactoring.guru

在簡單工廠中,它主要服務的對象是客戶,而 工廠方法 的使用者與工廠本身的類并不相干, 工廠方法模式主要服務自身的父類,如下的 ProductFactory(類比 UML 中的 Creator):

/// 抽象工廠
abstract class ProductFactory {
  /// 抽象工廠方法
  Product factoryMethod();

  /// 業務代碼
  void dosomthing() {
    Product product = factoryMethod();
    print(product.name);
  }
}

ProductFactory 類中,工廠方法 factoryMethod 是抽象方法, 每個子類都必須重寫這個方法并返回對應不同的 Product 對象, 在 dosomthing() 方法被調用時,就可以根據返回的對象做出不同的響應。具體使用方法如下:

/// 具體工廠
class ProductFactory1 extends ProductFactory {

  /// 具體工廠方法1
  @override
  Product factoryMethod() {
    return ConcreteProduct1();
  }
}

class ProductFactory2 extends ProductFactory {
  /// 具體工廠方法2
  @override
  Product factoryMethod() {
    return ConcreteProduct2();
  }
}

/// 使用
main() {
  ProductFactory product = ProductFactory1();
  product.dosomthing(); // ConcreteProduct1
}

在 Flutter 中,抽象方法有一個非常實用的應用場景。我們在使用 Flutter 開發多端應用時通常需要考慮到多平臺的適配,即在多個平臺中,同樣的操作有時會產生不同的結果/樣式,我們可以將這些不同結果/樣式生成的邏輯放在工廠方法中。

如下,我們定義一個 DialogFacory,用作生成不同樣式 Dialog 的工廠:

abstract class DialogFacory {
  Widget createDialog(BuildContext context);

  Future<void> show(BuildContext context) async {
    final dialog = createDialog(context);
    return showDialog<void>(
      context: context,
      builder: (_) {
        return dialog;
      },
    );
  }
}

然后,針對 Android 和 iOS 兩個平臺,就可以創建兩個不同樣式的 Dialog 了:

/// Android 平臺
class AndroidAlertDialog extends DialogFactory {

  @override
  Widget createDialog(BuildContext context) {
    return AlertDialog(
      title: Text(getTitle()),
      content: const Text('This is the material-style alert dialog!'),
      actions: <Widget>[
        TextButton(
          onPressed: () {
            Navigator.of(context).pop();
          },
          child: const Text('Close'),
        ),
      ],
    );
  }
}
/// iOS 平臺
class IOSAlertDialog extends DialogFactory {

  @override
  Widget createDialog(BuildContext context) {
    return CupertinoAlertDialog(
      title: Text(getTitle()),
      content: const Text('This is the cupertino-style alert dialog!'),
      actions: <Widget>[
        CupertinoButton(
          onPressed: () {
            Navigator.of(context).pop();
          },
          child: const Text('Close'),
        ),
      ],
    );
  }
}

現在,我們就可以像這樣使用對應的 Dialog 了:

Future _showCustomDialog(BuildContext context) async {
  final dialog = AndroidAlertDialog();
  // final dialog = IOSAlertDialog();
  await selectedDialog.show(context);
}

抽象工廠

抽象工廠模式,相較于 簡單工廠工廠方法 最大的不同是:這兩種模式只生產一種對象,而抽象工廠生產的是一系列對象 (對象族),而且生成的這一系列對象一定存在某種聯系。比如 Apple 會生產 手機、平板 等多個產品,這些產品都屬于 Apple 這個品牌。

如下面這個抽象的工廠類:

abstract class ElectronicProductFactory {
  Product createComputer();

  Product createMobile();

  Product createPad();

  // ...
}

對于 Apple 來說,我就是生產這類電子產品的工廠,于是可以繼承這個類,實現其中的方法生產各類產品:

class Apple extends ElectronicProductFactory {

  @override
  Product createComputer() {
    return Mac();
  }

  @override
  Product createMobile() {
    return IPhone();
  }

  @override
  Product createPad() {
    return IPad();
  }

  // ...
}

同樣地,對于華為、小米等電子產品廠商也可以使用相同的方式表示,這就是抽象工廠模式。

在開發 Flutter 應用中,我們也可以充分利用抽象工廠模式做切合應用的適配,我們可以定義如下這個抽象工廠,用于生產 widget:

abstract class IWidgetsFactory {

  Widget createButton(BuildContext context);

  Widget createDialog(BuildContext context);

  // ...
}

我們的應用通常需要針對各個平臺展示不同風格的 widget。因此針對每一個平臺,我們都可以實現對應的實現工廠,如下:

/// Material 風格組件工廠
class MaterialWidgetsFactory extends IWidgetsFactory {
  @override
  Widget createButton(
      BuildContext context, VoidCallback? onPressed, String text) {
    return ElevatedButton(
      child: Text(text),
      onPressed: onPressed,
    );
  }

  @override
  Widget createDialog(BuildContext context, String title, String content) {
    return AlertDialog(title: Text(title), content: Text(content));
  }

  /// ...
}

/// Cupertino 風格組件工廠
class CupertinoWidgetsFactory extends IWidgetsFactory {
  @override
  Widget createButton(
    BuildContext context,
    VoidCallback? onPressed,
    String text,
  ) {
    return CupertinoButton(
      child: Text(text),
      onPressed: onPressed,
    );
  }

  @override
  Widget createDialog(BuildContext context, String title, String content) {
    return CupertinoAlertDialog(
      title: Text(title),
      content: Text(content),
    );
  }

  // ...
}

這樣,在 Android 平臺上我們使用 MaterialWidgetsFactory,在 iOS 平臺上使用 CupertinoWidgetsFactory,就能使用對應平臺的 widget,想要適配更多平臺只需要再繼承 IWidgetsFactory 實現對應平臺的工廠類即可。

至此,我們可以發現,作為創建型模式,這三類工廠模式主要工作就是以不同的方式創建對象,但他們各有特點:簡單工廠模式抽象的是 生產對象,工廠方法模式抽象的是 類方法,工廠方法模式抽象的則是 生產對象的工廠,如何使用就見仁見智了。


https://mp.weixin.qq.com/s/e7bYAN8nU8apGyEkMX4RaA

最多閱讀

如何有效定位Flutter內存問題? 1年以前  |  11725次閱讀
Flutter的手勢GestureDetector分析詳解 3年以前  |  7688次閱讀
Flutter插件詳解及其發布插件 3年以前  |  6371次閱讀
在Flutter中添加資源和圖片 3年以前  |  5201次閱讀
Flutter 狀態管理指南之 Provider 3年以前  |  4388次閱讀
發布Flutter開發的iOS程序 3年以前  |  4365次閱讀
Flutter for Web詳細介紹 3年以前  |  4229次閱讀
在Flutter中發起HTTP網絡請求 3年以前  |  4005次閱讀
使用Inspector檢查用戶界面 3年以前  |  3923次閱讀
Flutter Widget框架概述 3年以前  |  3301次閱讀
Flutter路由詳解 3年以前  |  3141次閱讀
JSON和序列化 3年以前  |  3060次閱讀
Flutter框架概覽 3年以前  |  3025次閱讀
推薦5個Flutter重磅開源項目! 1年以前  |  2983次閱讀
為Flutter應用程序添加交互 3年以前  |  2982次閱讀
使用自定義字體 3年以前  |  2879次閱讀
處理文本輸入 3年以前  |  2870次閱讀
編寫國際化Flutter App 3年以前  |  2868次閱讀

手機掃碼閱讀
18禁止午夜福利体验区,人与动人物xxxx毛片人与狍,色男人窝网站聚色窝
<蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>