前言
面向对象设计是面向对象方法中的一个关键阶段,它紧随面向对象分析阶段之后。在这一阶段,我们将基于面向对象分析的结果,运用五大设计原则来确保软件的高质量开发。
一、基本思想
面向对象方法(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),其中主要介绍了面向对象设计的设计原则,包括单一职责原则、开放封闭原则、接口隔离原则、依赖倒置原则和里式替换原则。