封装与可见性
封装、继承与多态是面向对象编程(Object-oritented programming, OOP)的三大特性,而封装是其中最为基础与关键的特性。Go语言向我们证明了继承对于OOP是可选的,而多态通过接口来实现。这些基本概念将在后续章节陆续介绍,本章节重点说明封装。
封装
很多时候我们想要隐藏代码的实现细节,而对外暴露的只是API(应用编程接口),这便是针对接口(而非实现)编程,要实现这一点就要用到封装。接口用于定义API,而类就像一个容器主要用于封装。那么类究竟封装了什么?
// ch0401_1.dart
class Dog {
const Dog(this.name, this.age); // 3
final String name; // 1
final int age; // 1a
void say() { //2
print('$name: Woof woof');
}
void eat() { //2a
print("$name: I'd like to have some bones");
}
}
- 此例中
Dog
类封装了其属性(即字段name
、age
),以及 - 行为,即方法
say()
、eat()
;方法(method)即在类中定义的函数; - 这是
Dog
的构造函数。
类的实例化与成员
下面这行代码使用构造函数,创建(实例化)了Dog
类的一个实例(instance):
var dog = Dog('Spots', 6);
我们说dog
是Dog
类的一个实例或对象。
NOTE: 类是对象的模板,对象是类的实例。
类的成员包括实例变量、实例方法、静态变量、静态方法。
Dog
类中的name
和age
是实例变量,像这样引用:dog.name
。
Dog
类中的say()
和eat()
是实例方法,像下面这样调用:
dog.say();
实例变量与实例方法是与类的实例关联(绑定)的,而静态(static)变量与静态方法则与类关联,例如:
class Dog {
// .... (omit for brevity)
static const ponyo = Dog('Ponyo', 5);
static void ponyoSay() => ponyo.say();
}
可见性
Dart 没有使用 public
, protected
与 private
关键字来表达成员的可见性,而是使用特殊的标记即下划线(_
)来表达private
(私有)的概念。
class Dog {
// .... (omit for brevity)
final _secret = "I won't tell you this";
void _tellSecret() => print(_secret);
}
_secret
字段与_tellSecret()
方法对于外部文件是不可见的,换言之,它只能在当前文件(定义它的文件)中使用。
Libraries(库)
Dart里的每个.dart文件(加上它的子文件,即parts)都是一个libary。使用import
关键字导入libary中的代码。这种机制使得代码可以被模块化以及可以被复用。
file lib/hellodart.dart:
// lib/hellodart.dart
int calculate() { // 1
return 6 * 7;
}
file bin/hellodart.dart:
// bin/hellodart.dart
import 'package:hellodart/hellodart.dart' as hellodart; // 2
// ch0101
void main(List<String> arguments) {
print('Hello world: ${hellodart.calculate()}!'); // 3
}
- 在
lib/hellodart.dart
中定义函数calculate()
; - 这行代码使用关键字
import
导入lib/hellodart.dart
,并使用as
关键字给此library取名hellodart
,将其中的标识符置于hellodart
命名空间里; hellodart.calculate()
表示调用hellodart
library 中的calculate()
函数。
NOTE:
- 第2行
import
一句中的package:hellodart
表示hellodart
这个package(包),package的名称与pubspec.yaml
文件中name: hellodart
是一致的;回想一下,我们在第1章Hello Dart: 搭建开发环境一节中创建了helloword
这个package。- 如果第2行代码不使用 as 关键字给
hellodart.dart
取名,表示将此libary导入全局命名空间,就直接使用calculate()
来调用该函数。
我们在 lib/hellodart.dart
中定义另一个函数:
int _calculate() => 7 * 8;
然后在其它文件中(如 bin/hellodart.dart
)调用它,将引起编译错误:
bin/hellodart.dart:6:35: Error: Method not found: '_calculate'.
print('Hello world: ${hellodart._calculate()}!'); // 3
^^^^^^^^^^