文章目录
Dart也是一门面向对象的开发语言,面向对象中非常重要的概念就是类,通过类的初始化创建一个对象
Dart 是支持基于 mixin 继承机制的面向对象语言,所有对象都是一个类的实例,而除了 Null 以外的所有的类都继承自 Object 类。 基于 mixin 的继承 意味着尽管每个类(top class Object? 除外)都只有一个超类,一个类的代码可以在其它多个类继承中重复使用。 扩展方法 是一种在不更改类或创建子类的情况下向类添加功能的方式。
Dart中,定义类用class关键字Object的, 定义类的伪代码如下:class 类名 {
类型 成员名;
返回值类型 方法名(参数列表) {
方法体
}
}
Dart语言中, 在类中使用属性(成员/实例变量)时, 有必要时是通过this获取的getsize方法中并没有加thisDart的开发风格中,在方法中通常使用属性时,会省略this,但是有命名冲突时,this不能省略// 创建类
class Point {
// 定义变量
int x;
void getsize() {
print('x = $x');
}
}
// 类的初始化
main(List<String> args) {
// 从Dart2开始,new关键字可以省略
var point = new Point();
point.x = 1;
point.getsize();
}
对象的 成员 由函数和数据(即 方法 和 实例变量)组成。方法的 调用 要通过对象来完成,这种方式可以访问对象的函数和数据。
使用(.)来访问对象的实例变量或方法:
var p = Point(2, 2);
// Get the value of y.
assert(p.y == 2);
// Invoke distanceTo() on p.
double distance = p.distanceTo(Point(4, 4));
使用 ?. 代替 . 可以避免因为左边表达式为 null 而导致的问题:
// If p is non-null, set a variable equal to its y value.
var a = p?.y;
Dart语言中,如果类中没有明确指定构造方法时,将默认拥有一个无参的构造方法()point对象调用的就是默认的无参构造方法Dart本身不支持函数的重载, 所以如果我们明确的写一个默认的构造方法,就会和我们自定义的构造方法冲突class Student {
String name;
int age;
Student(String name, int age) {
this.name = name;
this.age = age;
}
}
Dart提供了一种更加简洁的语法糖形式class Student1 {
String name;
int age;
// 这里和上面的Studeng的构造方法等价
Student1(this.name, this.age);
}
Dart又不支持函数的重载, 不能创建爱你相同名称不同参数的构造方法class Model {
String name;
int age;
Model(this.name, this.age);
// 命名构造方法
Model.withNameAndAge(String name, int age) {
this.name = name;
this.age = age;
}
// 命名构造方法
Model.initJson(Map<String, Object> map) {
this.name = map['name'];
this.age = map['age'];
}
}
// 初始化对象
main() {
// 普通构造方法
var model0 = Model('name', 12);
// 命名构造方法
var model1 = Model.withNameAndAge('titan', 12);
var model2 = Model.initJson({'name': 'jun', 'age': 18});
}
几种方式定义的属性都是可变的, 如果定义的属性是final不可重新赋值的又该如何实现
class Teacher {
final String name;
final int age;
// 1. 这里会有一个错误提示: All final variables must be initialized, but 'age' and 'name' are not
Teacher(String name, int age) {
//2. 这里也会有一个错误提示: 'name' can't be used as a setter because it's final
this.name = name;
this.age = age;
}
}
Dart中在执行下面{ }中的代码的时候, 表示Teacher对象已经初始化完毕了{ }之前, 必须保证name和age被初始化了final修饰的属性是不可被重新赋值的, 所以才会报错class Size {
final double width;
final double height;
final double area;
// 命名可选参数
Size(this.width, this.height, { this.area = 10 });
}
area只能设置具体的数值, 不能设置表达式class Size {
final double width;
final double height;
final double area;
// 多个属性使用逗号分隔
Size(double width, double height):
this.width = width,
this.height = height,
this.area = width * height;
}
name变量来获取一个对象class Point {
String name;
int age;
Point(this.name, this.age);
// 重定向的构造方法
Point.fromName(String name): this(name, 0);
}
// 使用方法
var point = Point.fromName("name");
print(point.age); // 输出: 0
Dart中判断两个对象是否是同一个的方法是通过函数identical判断, 返回值是一个布尔值// 普通构造函数
class Person {
String name;
int age;
Person(this.name, this.age);
}
// 初始化列表
class Size {
final double width;
final double height;
final double area;
// 多个属性使用逗号分隔
Size(double width, double height):
this.width = width,
this.height = height,
this.area = width * height;
}
main(List<String> args) {
var p1 = Person("name", 10);
var p2 = Person("name", 10);
// 判断两个对象是不是同一个
print(identical(p1, p2)); /// false
var s1 = Size(10, 20);
var s2 = Size(10, 20);
// 判断两个对象是不是同一个
print(identical(s1, s2)); /// false
}
Dart中如果将构造方法前加const进行修饰,那么可以保证相同的参数,创建出来的对象是相同的// 常量构造方法
class Teacher {
final String name;
const Teacher(this.name);
}
main(List<String> args) {
// 常量构造方法
// 这里的const不可以省略
var t1 = const Teacher("name");
var t2 = const Teacher("name");
print(identical(t1, t2)); /// true
// 这里的const可以省略
const t3 = Teacher("name");
const t4 = Teacher("name");
print(identical(t3, t4)); /// true
print(identical(t1, t4)); /// true
}
常量构造方法有一些注意点:
final修饰的.new关键字,而是使用const关键字const修饰的标识符时,const可以省略.Dart提供了factory关键字, 用于通过工厂去获取对象returnmain(List<String> args) {
var p1 = Person.fromName("titan");
var p2 = Person.fromName("titan");
print(identical(p1, p2)); // true
}
class Person {
String name;
// 用于缓存创建的对象, 避免大量的创建和销毁对象
static final Map<String, Person> _cache = <String, Person>{};
factory Person.fromName(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final p = Person(name);
_cache[name] = p;
return p;
}
}
Person(this.name);
}
Dart中类定义的属性默认是可以直接被外界访问的Dart中也存在setter和getter方法, 用于监听累的属性被访问的过程main() {
var people = People('top');
people.setName = 'top';
print(people.getName);
print(people.name);
var person = Person('titan');
person.setName = 'jun';
print(person.getName);
}
class People {
String name;
// setter
set setName(String value) {
this.name = value;
}
// getter
String get getName {
return 'titanjun';
}
People(this.name);
}
setName和getName是自定义的, 你也可以命名为setterName和getterName等class Person {
String name;
// setter
set setName(String value) => this.name = value;
// getter
String get getName => 'titanjun';
Person(this.name);
}
Dart中同样支持类的继承, 继承使用extends关键字,子类中使用super来访问父类super显式调用父类的某个构造方法class People {
String name;
People(this.name);
void eat() {
print('people -- eat');
}
}
class Person extends People {
int age;
Person(String name, int age): this.age = age, super(name);
void eat() {
// 这里的super, 看个人需求是否调用
super.eat();
print('Person -- eat');
}
}
main(List<String> args) {
var people = People('name');
people.eat();
var person = Person("top", 10);
person.eat();
}
Dart中抽象类是使用abstract声明的类Dart中没有具体实现的方法(没有方法体),就是抽象方法abstract class Size {
int width;
int height;
Size(this.width, this.height);
void getSize();
int getArea() {
return this.width * this.height;
}
}
class Area extends Size {
void getSize() {
print('width = $width, height = $height');
}
Area(int width, int height): super(width, height);
}
main(List<String> args) {
// 实例化Size会报错: Abstract classes can't be instantiated
// var size = Size(20, 2);
var area = Area(10, 2);
area.getArea();
print(area.getArea());
}
在Dart中只有单继承, 是不支持多继承的, 但是我们却可以通过其他方式间接实现多继承问题
Dart中的接口比较特殊, 没有一个专门的关键字来声明接口, 默认情况下所有的类都是隐式接口Dart开发中,我们通常将用于给别人实现的类声明为抽象类Dart中通过implements来实现多继承问题, 但是必须实现这个接口中的所有方法, 而且在方法的实现中不能调用super方法abstract class Woman {
void eat();
void student() {
print("student");
}
}
class Man {
void run() {
print("runner");
}
}
class Student implements Woman, Man {
void eat() {
print("eat");
}
void student() {
print("student--student");
}
void run() {
// 这里不能调用super方法
// super.run();
print("run");
}
}
main(List<String> args) {
var stu = Student();
stu.eat();
stu.run();
stu.student();
}
implements实现某个类时,类中所有的方法都必须被重新实现(无论这个类原来是否已经实现过该方法)class定义类之外,也可以通过mixin关键字来定义一个类。mixin定义的类用于被其他类混入使用,通过with关键字来进行混入mixin Runner {
run() {
print('在奔跑');
}
}
mixin Flyer {
fly() {
print('在飞翔');
}
}
// 这里可以对原方法不做任何实现
class Bird with Runner, Flyer { }
main(List<String> args) {
var bird = Bird();
bird.run();
bird.fly();
}
扩展函数就是可以在类的外部声明额外的类的函数,并且可以扩展系统函数,让使用更方便,代码更简洁,
熟悉Kotlin的同学都非常熟悉了,真的很好用。
extension。请确保你工程项目中pubspec.yaml文件中:environment:
sdk: ">=2.7.0 <3.0.0"
Dart中的扩展使用extension 、on关键字 ,形如:
extension <extension name> on <type> {
(<member definition>)*
}
例如String 的扩展:
extension NumberParsing on String {
int parseInt() {
return int.parse(this);
}
double parseDouble() {
return double.parse(this);
}
}
调用如下:
print("40".parseInt());
也可以利用 getter 操作符简化:
extension NumberParsing on String {
int get parseInt {
return int.parse(this);
}
double get parseDouble {
return double.parse(this);
}
}
此时调用如下:
print("40".parseInt);
有了上边的类的基础,再看单例模式就简单多了。
在实际编码过程中,单例模式常见应用有:
一般来说,要在代码中使用单例模式,结构上会有下面这些约定俗成的要求:
getInstance() 访问。
遵循以上这些要求,我们就不难能用 Dart 写出一个普通的单例模式:
class Singleton {
static Singleton _instance;
// 私有的命名构造函数
Singleton._internal();
static Singleton getInstance() {
if (_instance == null) {
_instance = Singleton._internal();
}
return _instance;
}
}
同时,在实现单例模式时,也需要考虑如下几点,以防在使用过程中出现问题:
如上文所说的,Dart 语言作为单线程模型的语言,实现单例模式时,我们本身已经可以不用再去考虑 线程安全 的问题了。Dart 的很多其他特性也依然可以帮助到我们实现更加 Dart 化的单例。
使用 getter 操作符,可以打破单例模式中既定的,一定要写一个 getInstance() 静态方法的规则,简化我们必须要写的模版化代码,如下的 get instance:
class Singleton {
//私有化构造
Singleton._internal();
static Singleton _instance;
//重写getter方法
static get instance {
if (_instance == null) {
_instance = Singleton._internal();
}
return _instance;
}
}
Dart 的 getter 的使用方式与普通方法大致相同,只是调用者不再需要使用括号,这样,我们在使用时就可以直接使用如下方式拿到这个单例对象:
final singleton = Singleton.instance;
而 Dart 中特有的 工厂构造函数(factory constructor)也原生具备了 不必每次都去创建新的类实例 的特性,将这个特性利用起来,我们就可以写出更优雅的 Dart(able) 单例模式了,如下:
class Singleton {
static Singleton _instance;
Singleton._internal();
// 工厂构造函数
factory Singleton() {
if (_instance == null) {
_instance = Singleton._internal();
}
return _instance;
}
}
这里我们不再使用 getter 操作符额外提供一个函数,而是将单例对象的生成交给工厂构造函数,此时,工厂构造函数仅在第一次需要时创建 _instance,并之后每次返回相同的实例。这时,我们就可以像下面这样使用普通构造函数的方式获取到单例了:
final singleton = Singleton();
如果你还掌握了 Dart 空安全及箭头函数等特性,那么还可以使用另一种方式进一步精简代码,写出像下面这样 Dart 风味十足的代码:
class Singleton {
static Singleton _instance;
Singleton._internal() {
_instance = this;
}
factory Singleton() => _instance ?? Singleton._internal();
}
late 操作符(工厂构造函数+空安全+箭头函数)这里,使用 ?? 作为 _instance 实例的判空操作符,如果为空则调用构造函数实例化否则直接返回,也可以达到单例的效果。
以上,Dart 单例中懒加载,是使用判空来实现的(if (_instance == null) 或 ??),但是在 Dart 空安全特性里还有一个非常重要的操作符 late ,它在语言层面就实现了实例的懒加载,如下面这个例子:
这样没用 late 操作符写就达不到懒加载目的,类加载时候就已经初始化:
class Singleton {
/// 私有化构造方法,可避免外部暴露构造函数,进行实例化
Singleton._();
/// 静态私有成员变量
static final Singleton _instance = Singleton._();
///重写_instance 的get 方法
static Singleton get instance => _instance;
}
工厂构造函数+空安全+箭头函数 ,使用late 之后
class Singleton {
Singleton._internal();
factory Singleton() => _instance;
static late final Singleton _instance = Singleton._internal();
}
对比以上写法,被标记为 late 的变量 _instance 的初始化操作将会延迟到字段首次被访问时执行,而不是在类加载时就初始化。这样,Dart 语言特有的单例模式的实现方式就这么产生了。
说到工厂构造函数/空安全操作符等 Dart 语法上的特性,Flutter 应用中的例子已经屡见不鲜了,但光看单例模式的定义,我们还必须联想到 Flutter 中另一个非常重要的 widget,那就是 InheritedWidget。
简单说下InheritedWidget:
InheritedWidget和React中的context功能类似,可以实现跨组件数据的传递。
定义一个共享数据的InheritedWidget,需要继承自InheritedWidget
updateShouldNotify方法是对比新旧_InheritedStateContainer,是否需要对更新相关依赖的Widget引用下图源《Flutter 开发之旅从南到北》—— 第九章图 9.4

上面代码中,我们通过继承 InheritedWidget 就实现了自己的可遗传组件 _InheritedStateContainer,其中的 data 变量表示全局状态数据,在这里就可以被认为是整个应用的一个单例对象。
_InheritedStateContainer 还接受 child 参数作为它的子组件,child 表示的所以子组件们就都能够以某种方式得到 data 这个单一的全局数据了。
使用的时候用_InheritedStateContainer将需要用到共享数据的widget进行包裹。
约定俗成地,Flutter 源码经常会提供一些 of 方法(类比 getInstance())作为帮助我们拿到全局数据的辅助函数。
以 Flutter 中典型的 Theme 对象为例。我们通常会在应用的根组件 MaterialApp 中创建 ThemeData 对象作为应用统一的主题样
式对象:
MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
在其他任意的组件中,我们可以使用 Theme.of(context) 拿到该对象了,且这个对象全局唯一。如下所示,我们可以将该 ThemeData 对象中的 primaryColor 应用在 Text 中:
// 使用全局文本样式
Text(
'Flutter',
style: TextStyle(color: Theme.of(context).primaryColor),
)
这个角度来看,InheritedWidget 完全可以被我们看作是最原生、最 Flutter 的单例应用了.
从实现普通单例到应用 getter 操作符 的 Dart 单例,到使用 工厂构造函数 Dart 单例,再到使用了 工厂构造函数 + 空安全语法 + 箭头函数 的 Dart 单例,最后结合对 InheritedWidget 概念的理解,看到了 Flutter 中特有的单例模式。
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr