Dart 之面向对象设计

177 阅读2分钟

Dart之面向对象设计.png

前言

面向对象设计是面向对象方法中的一个关键阶段,它紧随面向对象分析阶段之后。在这一阶段,我们将基于面向对象分析的结果,运用五大设计原则来确保软件的高质量开发。

一、基本思想

面向对象方法(OOM) 的基本思想是通过引入对象的概念,将现实世界中的事物(所有事物)认为为对象,在对象的基础之上再进行抽象(提取出多个对象具有的相同属性和方法)。

面向对象设计(OOD) 的基本思想是将现实世界中的实体或概念抽象为对象,并通过这些对象之间的相互作用来实现系统的功能(对面向对象方法的进行一步细化)。

二、设计原则

2.1 单一职责原则(Single Responsibility Principle, SRP)

一个类应该只有一个引起它变化的原因。

理解: 一个类只负责一个功能领域内的事务,也就是说设计时应当让一个类只负责一个功能。这样带来的好处是当需求发生变化时,该类的修改只影响到该功能领域,而不会波及其他功能领域。

示例:

/// 定义一个Bird类(只负责介绍内容,不负责判断其会哪些技能)。
class Bird{
  String name;
  Bird(this.name);
  void introduce() {
    print('我是${this.name}');
  }
}
/// 定义一个BirdSkill类(只负责判断其会哪些技能,不负责介绍内容)。
class BirdSkill{
  String name;
  bool flyable;
  BirdFly(this.name,this.flyable);
  void canfly(){
    if (flyable){
      print('${this.name}会飞!');
    }else{
      print('${this.name}不会飞!');
    }
  }
}
void main(){
  Bird eagle_a = Bird('鹰');
  eagle_a.introduce();
  BirdSkill eagle_b = BirdSkill('鹰',true);
  eagle_b.canfly();
  // 当出现一个新的物种需要添加时,
  // 只需对应修改其基本信息类Bird和是否会技能类BirdSkill。
  BirdSkill penguin = BirdSkill('企鹅',false); 
  // 当出现企鹅时只需修改BirdSkill添加企鹅行为即可。
  penguin.canfly();
}
// 输出:
我是鹰
鹰会飞!
企鹅不会飞!

2.2 开放封闭原则(Open/Closed Principle, OCP)

软件实体(类、函数、模块等)应该对扩展开放,对修改进行封闭。

理解: 当需求变化时,应该能够通过添加新代码来满足新需求,而不是修改已有的代码。

示例:

/// 定义一个Bird类,不同的鸟类实现需要扩展Bird类而不是修改。
class Bird{
  String name;
  Bird(this.name);
  void introduce() {
    print('我是${this.name}');
  }
}
/// 定义一个Penguin类 
/// 开放封闭原则不建议修改Bird类中内容,而建议对Bird类进行扩展。
class Penguin extends Bird{
    Penguin(super.name);
    void flyable(){
        print('${this.name}不会飞!');
    }
}
void main(){
  Penguin penguin = Penguin('企鹅');
  penguin.flyable();
}
// 输出:
企鹅不会飞!

2.3 接口隔离原则(Interface Segregation Principle, ISP)

客户端不应该依赖于其不使用的接口。

理解: 不应该让客户使用他们不使用的接口,应该尽量避免大而全的接口设计,而应该是多而小。使得每个接口只包含一组紧密相关的操作。

示例:

/// 定义一个Bird类,其中包含了会飞的方法。
class Bird{
  String name;
  Bird(this.name);
  void flyable(){
     print('${this.name}会飞');
  }
  void introduce() {
    print('我是${this.name}');
  }
}
/// 定义一个Penguin类 企鹅不会飞不需要Bird接口中的flyable(),
/// 应该让Bird中只包含一组紧密相关的接口。
class Penguin extends Bird{
    Penguin(super.name);
}
void main(){
  Penguin penguin = Penguin('企鹅');
  penguin.introduce(); 
}
// 输出:
我是企鹅

2.4 依赖倒置原则(Dependency Inversion Principle, DIP)

高层模块不应该依赖于低层模块,二者都应该依赖于抽象,而抽象不应该依赖于细节,细节应该依赖于抽象。

理解: 高层模块是指那些负责业务逻、决策等关键功能的部分;低层模块是指那些执行具体操作的部分。高层模块可以理解为对类的抽象,低层模块可以理解从对象中抽象出的类,两者都应该依赖于抽象,而不是依赖于具体的实现。

示例:

/// 定义一个Bird类,
/// 这个类不应该依赖于canfly()这个具体的实现,应该依赖于接口或抽象。
class Bird{
  String name;
  Bird(this.name);
  void canfly(){
     print('${this.name}会飞');
  }
  void introduce() {
    print('我是${this.name}');
  }
}
/// 定义一个Penguin类,不应爱依赖于具体的实现,应该依赖于接口。
class Penguin{
  String name;
  Penguin(this.name);
  void introduce() {
    print('我是${this.name}');
  }
}
void main(){
  Bird eagle = Bird('鹰');
  eagle.canfly();
  Penguin penguin = Penguin('企鹅');
  penguin.introduce(); 
}
// 输出:
鹰会飞
我是企鹅

正确做法: 应该为introduce()与canfly()建立一个接口让Penguin类去实现接口。

/// 定义一个抽象类
abstract class Bird{
  void canfly();
  void introduce();
}

2.5 里氏替换原则(Liskov Substitution Principle, LSP)

子类应当能够替换基类的内容而不影响程序的正确性。

理解: 子类应当可以完全替换其父类,而不会影响程序的正确性。

示例:

/// 定义一个Bird类,
/// 任何子类都可以替代父类的eat()方法而不会影响程序的正确性。
/// 确保了继承关系的合理性。
class Bird{
  String name;
  Bird(this.name);
  void eat() {
    print('我会吃');
  }
}

三、总结

本小节介绍了面向对象方法中的第二个阶段——面向对象设计(OOD),其中主要介绍了面向对象设计的设计原则,包括单一职责原则、开放封闭原则、接口隔离原则、依赖倒置原则和里式替换原则。