首页 > 解决方案 > Dart 中的通用约束

问题描述

我需要在 http 请求中抽象我的模型解析系统

我有一个 HttpClient 类

class HttpClient {
   Dio dio = new Dio();
   
   Future<T> get<T extends BaseModel>(String url) {
      Response response = await dio.get(url);
      T.fromJson(response.body);
   }
}

我的 BaseModel 类是这样的:

abstract class BaseModel {
 fromJson(Map<String, dynamic> json);
 toJson();
}

和一个模型的例子:

class Person extends BaseModel {
  String name;
  String surname;
  int age;

  Person({this.name, this.surname, this.age});

  Person.fromJson(Map<String, dynamic> json) {
    // parse ToModel;
  }

  Map<String, dynamic> toJson() {
    // parse toJson;
  }
}

我想像这样使用我的 HttpClient:

class RandomClassToConsumeHttp {
   
   HttpClient _httpClient;
   Person person;  

   RandomClassToConsumeHttp(this._httpClient);


   void _getPerson() async {
     _person = await _httpClient.get<Person>("api/getPerson");
   }
}

问题是:

我的 get 方法中有一个泛型,我需要泛型来扩展 BaseModel 类,所以我总是会有一个 fromJson() 的实现,并根据我发送给泛型的模型类型进行模型解析

我不知道是否可以在飞镖中做一些像这样的通用约束,我需要使用那种类型的 fromJson,有人知道它是否可能吗?或者如果有其他解决方案

   Future<T> get<T extends BaseModel>(String url) {
      Response response = await dio.get(url);
      T.fromJson(response.body);
   }

标签: jsonhttpdartmodel

解决方案


不幸的是,T.fromJson()这是不可能的。解决方法是维护工厂以创建实例。

一些警告是:

  1. 你不能撕掉命名的构造函数。
  2. 静态函数不是继承的,也没有办法要求子类来实现它们。
  3. 拥有非静态 virtual fromcreate函数会使不可变类和默认不可为空的代码复杂化。
  4. 每个适用的子类都需要实现createfrom模式并添加到工厂映射中。

总而言之,我认为将收到的 json 传递给模型的构造函数会更好,例如:

var model = SomeModel.fromJson(httpClient.get('resource'));

最后,这是一个工厂实现示例:

abstract class Base {
  static from(v) => SubA.from(v);
}

class SubA extends Base {
  static from(v) => SubA();
}

class SubB extends Base {
  static from(v) => SubB();
}

final _baseFromFactory = {
  Base: Base.from,
  SubA: SubA.from,
  SubB: SubB.from,
  // or you can do:
  // SubB: (v) => /* create instance */
};

class Client {
  get<T extends Base>(v) => _baseFromFactory[T](v);
  
  // Alternatively, you can switch on T.
  getAlt<T extends Base>(v) {
    switch (T) {
      case SubB: return SubB.from(v);
      default: return SubA.from(v);
    }
  }
}

main() {
  var client = Client();
  var subA = client.get<SubA>('a value');
  var subB = client.get<SubB>('a value');
  var altA = client.getAlt<SubA>('a value');
  var altB = client.getAlt<SubB>('a value');

  print(subA);
  print(subB);
  print(altA);
  print(altB);
}

推荐阅读