[英]How to enforce that a parameter is of the same static concrete type as the object?
I have a Flutter app connected to a Firestore database.我有一个连接到 Firestore 数据库的 Flutter 应用程序。 I have a hierarchy of objects, let's say for example with abstract base class
Vehicle
, with subclasses such as Car
, Truck
, etc. Objects of these types are stored in the database.我有一个对象层次结构,例如抽象基类 class
Vehicle
,以及Car
、 Truck
等子类。这些类型的对象存储在数据库中。 Due to how Firestore works, it is better to make these classes immutable, ie to make them essentially represent a snapshot of the conceptual object instead of the mutable object itself.由于 Firestore 的工作方式,最好让这些类不可变,即让它们本质上代表概念 object 的快照,而不是可变的 object 本身。
When I want to update a Vehicle
object in the database, I don't want users of Vehicle
(ie other parts of my code) to have to specify the update as a dynamic map, eg vehicle.update({"speed": 42, "color": "red"})
, because this disables static checking of field names and types.当我想更新数据库中的
Vehicle
object 时,我不希望Vehicle
的用户(即我代码的其他部分)必须将更新指定为动态 map,例如 vehicle.update vehicle.update({"speed": 42, "color": "red"})
,因为这会禁用字段名称和类型的 static 检查。 Instead, I thought that the best way would be to make the users create the new snapshot they want, eg car.setInDB(updatedCar)
.相反,我认为最好的方法是让用户创建他们想要的新快照,例如
car.setInDB(updatedCar)
。 Behind the scenes this just calls .set()
on the corresponding document reference in Firestore.在幕后,这只是在 Firestore 中的相应文档引用上调用
.set()
。 Since the implementation would be the same on all subclasses of Vehicle
, one could think of adding the method to Vehicle
:由于实现在
Vehicle
的所有子类上都是相同的,因此可以考虑将方法添加到Vehicle
:
abstract class Vehicle {
Future<void> setInDB(Vehicle updated)
=> database.set(this.dbLocation, updated)
}
However, users of this method shouldn't be setting a vehicle of some concrete type with a vehicle of some other concrete type.但是,此方法的用户不应将某些具体类型的车辆与其他具体类型的车辆设置在一起。 Ie, a truck can only be updated with another truck snapshot, not a car snaphot.
即,一辆卡车只能用另一辆卡车快照更新,而不是汽车快照。 This is already enforced at runtime through Firestore rules, but I'd rather have this checked statically.
这已经通过 Firestore 规则在运行时强制执行,但我宁愿静态检查它。
These options occur to me:我想到了这些选项:
Use the covariant
keyword and make subclasses override the setInDB
method with the corresponding type.使用
covariant
关键字并使子类覆盖具有相应类型的setInDB
方法。
abstract class Vehicle { Future<void> setInDB(covariant Vehicle updated) => database.set(this.dbLocation, updated) }
class Car extends Vehicle { @override Future<void> setInDB(Car updated) => super.setInDB(updated) }
The problem with this is I might forget to override the method if I add a new subclass.这样做的问题是,如果我添加一个新的子类,我可能会忘记重写该方法。 Also, users could do
vehicle.setInDB(otherVehicle)
, where vehicle
has static type Vehicle
, and the correctness of otherVehicle
would be checked at runtime again.此外,用户可以执行
vehicle.setInDB(otherVehicle)
,其中vehicle
具有 static 类型Vehicle
,并且将在运行时再次检查otherVehicle
的正确性。
Only add the setInDB
method to concrete subclasses of Vehicle
, with the correct type:仅将
setInDB
方法添加到具有正确类型的Vehicle
的具体子类:
abstract class Vehicle { }
class Car extends Vehicle { Future<void> setInDB(Car updated) => database.set(this.dbLocation, updated) }
class Truck extends Vehicle { Future<void> setInDB(Truck updated) => database.set(this.dbLocation, updated) }
Etc. The problem with this approach is the code duplication, since the implementation is the same everywhere.等等 这种方法的问题是代码重复,因为实现在任何地方都是一样的。
Are there other options?还有其他选择吗?
Dart 2.19 adds analyzer support for a @mustBeOverridden
annotation that you can add to a base class method. Dart 2.19 添加了对
@mustBeOverridden
注释的分析器支持,您可以将其添加到基本 class 方法中。 The analyzer then will emit a warning if a derived class neglects to override it.如果派生的 class 忽略覆盖它,那么分析器将发出警告。 (However, as of writing, a new version of
package:meta
has not been published yet to add that annotation, but presumably it will be published soon.) For more details, see https://github.com/dart-lang/sdk/issues/30175 . (然而,在撰写本文时,
package:meta
的新版本尚未发布以添加该注释,但据推测它很快就会发布。)有关更多详细信息,请参阅https://github.com/dart-lang/ SDK/问题/30175 。
Borrow the curiously recurring template pattern from C++ and apply it to Dart generics by making your derived classes extend (or mixin) a generic base class that is parameterized on the derived class. For example, I think that you could do:从 C++ 中借用奇怪的重复模板模式,并将其应用到 Dart generics,方法是使派生类扩展(或混入)在派生 class 上参数化的通用基类 class。例如,我认为您可以这样做:
abstract class Vehicle<DerivedVehicle extends Vehicle<DerivedVehicle>> { Future<void> setInDB(DerivedVehicle updated) => database.set(this.dbLocation, updated) } class Car extends Vehicle<Car> { } class Truck extends Vehicle<Truck> { }
and now Car().setInDB()
should require a Car
argument, and Truck().setInDB()
should expect a Truck
.现在
Car().setInDB()
应该需要一个Car
参数,而Truck().setInDB()
应该需要一个Truck
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.