上一篇 Dart
语法的文章中介绍了对 Future
、Streams
的 unwrap
操作以及 List
、Map
的展开、合并和过滤等等,总觉得有点意犹未尽,还有很多有意思的并没有提到,本篇文章来介绍一下 Dart
语言中面向对象的内容——构造函数,在我们日常开发中,对象的初始化(构造函数)有哪些方式,它们之间的区别在哪里,不同的构造函数都适用于哪些场景呢,本篇文章来聊聊这些内容。
Named
构造函数如果没有定义构造函数,Dart
会默认创建一个不带参数的默认构造函数 。它用于创建类的简单实例,通常不带参数或仅初始化对象的基本属性。Dart
还允许在同一类中定义多个构造函数,每个构造函数通过名称区分。命名构造函数提供了更多灵活性,适用于不同初始化需求。命名构造函数使用类名加点(ClassName.constructorName
)的形式定义。
1 2 3 4 5 6 7 8 9 class Rectangle { double width, height; Rectangle(this .width, this .height); Rectangle.square(double size) : width = size, height = size; }
这里还可以使用重定向构造函数,简化代码逻辑,将多个构造函数重定向到同一个主构造函数,从而减少重复代码,修改 Rectangle.square
构造函数的代码:
1 2 Rectangle.square(double size) : this (size, size);
Flutter
源码中这种类似用法有很多,如 EdgeInsets
类,有EdgeInsets.fromLTRB、EdgeInsets.all、EdgeInsets.only
等等。
Factory
构造函数工厂构造函数使用 factory
关键字,可以在每次调用构造函数时返回相同的实例,也可以根据逻辑返回不同的实例。它适用于需要缓存或复杂实例创建逻辑的情况,比如我们常见的单例模式(Singleton Pattern
)。
1 2 3 4 5 6 7 8 9 10 11 class Logger { static final Logger _instance = Logger._internal(); Logger._internal(); factory Logger() { return _instance; } }
如下代码复杂一点的用法,利用 Factory
构造函数 来根据不同参数返回不同类型的形状对象(Circle
或 Square
),可实现灵活的创建对象,不同于标准构造函数,Factory
构造函数可以返回子类,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 enum ShapeType { circle, square }class Shape { final ShapeType type; const Shape(this .type); factory Shape.fromRadius(double radius) => Circle(radius); factory Shape.fromRect(Rect rect) => Square(rect); @override String toString() => 'I am a shape of type $type ' ; } class Circle extends Shape { final double radius; const Circle(this .radius) : super (ShapeType.circle); @override String toString() => super .toString() + ' and I am a circle' ; } class Square extends Shape { final Rect rect; const Square(this .rect) : super (ShapeType.square); @override String toString() => super .toString() + "and I am a square" ; }
Factory
构造函数在对象创建的过程中提供了统一且灵活的接口,特别适用于需要根据不同条件创建不同子类实例的场景。实际项目中 Factory
构造函数的应用场景也很多,尤其在处理复杂对象创建和资源优化方面。
以下是 Factory
构造函数和普通构造函数的区别。
Static
构造函数实际上在 Dart
中, Static
构造函数并不是像有的编程语言(如 C#)中那样的有内置特性。也就是说 Dart
中没有直接的静态构造函数概念,但可以通过静态方法来实现类似的功能。静态方法可以用于初始化类级别的共享资源、执行单例模式和延迟加载等场景。将上面的单例类(Logger
)改成 Static
构造函数来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Logger { static final Logger _instance = Logger._internal(); Logger._internal(); static Logger initialize() { print ("Logger initialized" ); return _instance; } void log(String message) { print ("Log: $message " ); } } void main() { final logger = Logger.initialize(); logger.log("This is a log message." ); }
静态构造函数结合 late 变量可以达到也可以延迟加载的机制,例如,设计一个依赖初始化数据库的类,来看看是怎么实现的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class DatabaseConnection { static late DatabaseConnection _instance; static bool _isInitialized = false ; DatabaseConnection._create(); static void initialize() { if (!_isInitialized) { _instance = DatabaseConnection._create(); print ("Database connection initialized." ); _isInitialized = true ; } } static DatabaseConnection get instance => _instance; void query(String sql) { print ("Executing query: $sql " ); } } void main() { DatabaseConnection.initialize(); DatabaseConnection.instance.query("SELECT * FROM users" ); }
上面的代码中,late
关键字保证变量在第一次使用时才初始化,从而实现延迟加载, _internal
私有构造函数防止外部直接实例化。 getInstance
静态工厂方法是单例实例的全局访问入口。延迟加载常用于当初始化逻辑较为复杂,需要耗费大量资源时,例如,加载配置文件、初始化数据库连接等,或者不希望类在加载时就立即初始化,而是按需使用时才初始化。
Factory
和 Static
构造函数区别上面分别介绍了 Factory
构造函数和 Static
构造函数,那么它们之间有什么区别呢?Factory
构造函数返回已有实例或子类实例,控制具体对象的创建过程,而不是初始化与类绑定的静态资源;而 Static
构造函数用于初始化与类本身相关的资源,如设置静态变量或执行静态初始化逻辑,而不创建具体实例。
还有需要补充的一点就是对泛型参数的支持,在工厂构造函数中可以直接使用该类泛型参数,而无需在其函数中重新定义它,而是静态构造函数,必须重新定义泛型参数,因为静态构造函数不能从其类中继承泛型参数,借助代码来理解一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @immutable class Person <P > { final String name; final P property; const factory Person(String name, P property) = Person.fromProperties; const Person.fromProperties(this .name, this .property); factory Person.fromPropertiesFactory(String name, P property) => Person.fromProperties(name, property); static Person<P> fromPropertiesStatic<P>(String name, P property) => Person.fromProperties(name, property); factory Person.fooBar(P property) => Person.fromProperties('Foo Bar' , property); static Person<P> bazQux<P>(P property) => Person.fromProperties('Baz Qux' , property); }
此外,工厂构造函数支持重定向等功能,而静态方法则无法提供这些功能。使用工厂构造函数创建对象有助于明确代码的目的和意图。
构造函数与抽象类 和其它语言中的抽象类一样,Dart
的抽象类也是不能被实例化的,但抽象类构造函数可以用于初始化被继承的字段或逻辑,子类可以在构造函数中使用 super
调用父类的构造函数,这样方便为子类提供模板化的构造流程。在设计模式中,抽象类的构造函数常用于定义公共初始化逻辑,确保子类继承时遵循一致性。还有当一个类定义了 final
修饰的字段时,可以通过抽象类的构造函数初始化这些字段。也把二者之间的区别做个对比。
结合代码来理解一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 enum Type { dog, cat }abstract class Animal { final Type type; const Animal({required this .type}); } class Cat extends Animal { const Cat() : super (type: Type .cat); } class Dog extends Animal { const Dog() : super (type: Type .dog); }
小结 文章中介绍了命名构造函数、Factory
和 Static
构造函数、构造函数与抽象类以及它们之间的区别,也提到它们各自的应用场景,怎么样,这些构造函数的打开方式你 Get 到了吗?如有疑问,欢迎留言。
关注公众号,第一时间看到更新内容: