Dart 之面向对象编程

302 阅读4分钟

Dart之面向对象编程.png

前言

面向对象编程(OOP) 作为编程范式的重要里程碑,彻底改变了我们构建软件的方式。它引入对象的概念,强调以对象为核心,以一种更接近现实世界的方式建模问题域,通过封装、继承、多态等特性,实现了代码的模块化、复用性和灵活性的显著提升。接下来让我们一起在Dart世界中探索OOP的奥秘。

注: 对象是数据和作用于数据的操作的封装体。

一、面向对象编程概述

面向对象编程(OOP)是相对于面向过程来说的,是一种编程范式。它引入了对象的概念,以一种更接近现实世界的方式建模问题域。其核心思想在于封装、继承、多态和抽象

记忆方法: 一二三四五 二十三

一个类:类的相关概念。

两个世界:现实世界、计算机世界。

三大特性:封装、继承、多态。

四大支柱:封装、继承、多态、抽象。

五大原则:单一职责原则、开放封闭原则、接口隔离原则、依赖倒置原则、里式替换原则。

二十三种设计模式(后续文章介绍)。

二、类

类:创建对象的模版或蓝图。

实例生成(由构造函数完成)对象,对象抽象为类。 类与对象关系图.png

注意: 在设计与实现时,我们首先接触的不是对象而是类和类层次结构。

2.1、类的定义

由class关键字+类名+类体(大括号{})定义。

  • 使用 class 关键字进行定义。
  • 类名:类的唯一标识,遵循标识符命名规则,命名约束一般使用帕斯卡命名法(单词首字母大写)。
  • 类体(大括号{}):包含属性、方法、构造函数,同时限定作用域。

示例:

/// 使用class关键字定义一个类名为Animal的类。
class Animal{
    String name; // 属性
    Animal(this.name); // 构造函数
    void roar(){ // 方法
        print('${this.name} 会叫');
    }
}

2.2、继承与类层次结构

继承是子类与父类共享属性与方法的一种构造。一般是用类来体现继承关系。如下图中子类(鸟类、鱼类) 继承自父类(动物类)。

面向对象编程继承图.png 子类与父类的继承关系构成了类层次结构。 当执行子类的实例生成方法时,其执行顺序为:

  • 首先在类层次结构中的子类沿继承路径上溯至它的一个基类,然后自顶向下执行该子类所有父类的实例生成方法。
  • 当子类的实例消除时,执行顺序恰好相反。

2.3、重置

重置或覆盖:子类重新定义父类中继承过来的方法。其基本思想是动态绑定的支持。

示例:

/// 使用class关键字定义一个类名为Animal的类。
class Animal{
    String name; // 属性
    Animal(this.name); // 构造函数
    void skill(){ // 方法
        print('${this.name} 会叫!');
    }
}
/// 定义一个Bird类继承自Animal类
class Bird extends Animal{
    Bird(super.name); // 子类继承父类时,必须实例化父类
    @override
    void skill(){ // 重置了父类中的skil()方法
        print('${this.name} 会飞!');
    }
    
}
void main() {
    Animal parrot_a = Animal('鹦鹉');
    parrot_a.skill();
    Bird parrot_b = Bird('鹦鹉');
    parrot_b.skill();
}
// 输出:
鹦鹉 会叫!
鹦鹉 会飞!

2.4、setter与getter

Dart 支持显式定义访问或修改私有属性。

  • setter:显式修改私有属性。
  • getter:显式访问私有属性。
  • 注意:Dart中私有属性针对于不同Dart文件来说。

示例:

/// 使用class关键字定义一个类名为Animal的类。
class Animal{
   String _name; // 属性
   Animal(this._name); // 构造函数
   // Getter
   String get name => _name; 
   // Setter 
   set name(String value) { 
       if (value.isNotEmpty) {
           _name = value;
       } else { 
           throw ArgumentError('Name cannot be empty');
       } 
   }
   void skill(){ // 方法
        print('${this._name} 会叫!');
    }
}

void main() {
    Animal parrot_a = Animal('鹦鹉');
    parrot_a.skill();
    parrot_a.name = '喜鹊'; // 修改私有属性
    parrot_a.skill();
}
// 输出:
鹦鹉 会叫!
喜鹊 会叫!

2.5、对象自身引用

对象自身引用是编程语言中的一种特有的结构。Dart中使用this关键字。

示例:

/// 使用class关键字定义一个类名为Animal的类。
class Animal{
    String name; 
    Animal(this.name); // this.name对象自身引用
    void skill(){ 
        print('${this.name}会叫!'); // this.name对象自身引用
    }
}

void main() {
    Animal parrot_a = Animal('鹦鹉');
    parrot_a.skill();
}
// 输出:
鹦鹉会叫!

三、对象

对象是数据和作用于数据的操作的封装体。

3.1、类的实例化

对象是类的实例化。

示例:

/// 使用class关键字定义一个类名为Animal的类。
class Animal{
    String name; 
    Animal(this.name); 
    void skill(){ 
        print('${this.name}会叫!');
    }
}

void main() {
    Animal parrot_a = Animal('鹦鹉'); // 实例化Animal类
    parrot_a.skill(); // parrot_a 为实例化的对象。
}
// 输出:
鹦鹉会叫!

3.2、消息传递

对象之间进行通信的构造。通常包含接收者,接收者方法,接收者参数、返回值(可选)。

示例:

/// 使用class关键字定义一个类名为Animal的类。
class Animal{
    String name; 
    Animal(this.name); 
    void skill(){ 
        print('${this.name}会叫!'); 
    }
}
void main() {
    Animal parrot_a = Animal('鹦鹉'); // 实例化Animal类
    parrot_a.skill(); // 消息传递,向parrot_a 对象发送消息
}
// 输出:
鹦鹉会叫!

四、构造函数

4.1、默认构造函数

构造函数名与类名相同。

示例:

/// 使用class关键字定义一个类名为Animal的类。
class Animal{
    String name; 
    Animal(this.name); // 构造函数。函数名与类名相同。
}

4.1、命名构造函数

Dart中允许有多个构造函数,并且可以给构造函数起一个别名。

示例:

/// 使用class关键字定义一个类名为Animal的类。
class Animal{
    String name;
    Animal(this.name); // 构造函数。函数名与类名相同。
    Animal.getname(this.name); // 命名构造函数,getname为重新命的名。
    void skill(){
        print('$name会叫');
    }
}
void main(){
    Animal parrot_a = Animal('鹦鹉');
    parrot_a.skill(); // 输出:鹦鹉会叫
    Animal parrot_b = Animal.getname('鹦鹉');
    parrot_b.skill(); // 输出:鹦鹉会叫
}

在Dart中可以在构造函数后加冒号(:)表示为初始化列表,初始化列表内容可以是表达式方法调用

示例:

/// 使用class关键字定义一个类名为Animal的类。
class Animal{
    String name; // 属性
    double wingspan;
    double height;
    Animal(this.name, this.wingspan, this.height); // 默认构造函数
    Animal.getinfo(this.name,this.wingspan):this.height=wingspan + 1; // 命名构造函数
    void info(){
        print('${this.name}高:${this.height},翅膀长:${this.wingspan}');
    }
}
void main(){
    Animal parrot_a = Animal('鹦鹉',10,11);
    parrot_a.info(); // 输出:鹦鹉高:11.0,翅膀长:10.0
    Animal parrot_b = Animal.getinfo('鹦鹉',10);
    parrot_b.info(); // 输出:鹦鹉高:11.0,翅膀长:10.0
}

继承时子类调用父类的构造函数。

示例:

/// 使用class关键字定义一个类名为Animal的类。
class Animal{
    String name; 
    Animal(this.name); 
    void skill(){
        print('${this.name} 会叫!');
    }
}
/// 定义一个Bird类继承自Animal类
class Bird extends Animal{
    Bird(super.name); // 调用父类的构造函数
    @override
    void skill(){ 
        print('${this.name} 会飞!');
    }
    
}
void main() {
    Animal parrot_a = Animal('鹦鹉');
    parrot_a.skill();
    Bird parrot_b = Bird('鹦鹉');
    parrot_b.skill();
}
// 输出:
鹦鹉 会叫!
鹦鹉 会飞!

4.2、工厂构造函数

工厂构造函数用于返回现有的实例或不同类型的对象,而不必每次都创建新的实例。

4.2.1、工厂构造函数返回现有的实例

使用 factory 关键字

示例:

/// 使用class关键字定义一个类名为Animal的类。
class Animal{
  static Map<String,Animal> _cahe = <String, Animal>{};
  final String name;
  Animal._factory(this.name); // 只能使用命名构造函数
  factory Animal(String name){   //  工厂构造函数固定写法,函数名与类名相同
    if (_cahe.containsKey(name)){
      return _cahe[name]!; // 必须断言其不为空,否则编译器报错
    }else{
      final animal = Animal._factory(name);
      _cahe[name] = animal;
      return animal;
    }
  }
  void skill(){
    print('${this.name} 会叫!');
  }
}
void main() {
  Animal parrot_a = Animal('鹦鹉');
  Animal parrot_b = Animal('鹦鹉');
  print(parrot_a == parrot_b); // 输出:true
}

4.2.2、工厂构造函数返回不同类型的对象

通过工厂构造函数可以简洁的返回不同类型的对象。

示例:

/// 使用class关键字定义一个类名为Animal的类。
class Animal{
  String name;
  Animal(this.name);
  void roar(){
    print('${this.name} 会叫!');
  }
}
/// 定义一个Bird类继承自Animal类
class Bird extends Animal{
  Bird(super.name);
  @override
  void roar(){
    print('${this.name}叽叽喳喳!');
  }

}
/// 定义一个Dog类继承自Animal类
class Dog extends Animal{
  Dog(super.name);
  @override
  void roar(){
    print('${this.name}汪汪!');
  }
}
/// 工厂模式的类
class AnimalFactory{
  // 静态工厂方法
  static Animal createFactory(String name){ // Animal为返回的函数类型
    switch(name){
      case '小狗':
        return Dog(name);
      case '小鸟':
        return Bird(name);
      default:
        throw ArgumentError('Unknown Animal name: $name');
    }
  }
}
void main() {
  Animal bird = AnimalFactory.createFactory('小鸟');
  bird.roar(); // 输出:小鸟叽叽喳喳!
  Animal dog = AnimalFactory.createFactory('小狗');
  dog.roar(); // 输出:小狗汪汪!
}

四、总结

本小节聚焦于面向对象方法的应用,首先阐述了面向对象编程的核心理念,随后详细展示了在Dart语言中如何定义类与对象的过程,并最终详细介绍了类定义中不可或缺的构造函数部分。