- 面向对象语言和面向对象编程思想要分开
- 面向过程语言和面向过程编程思想要分开
- 面向对象编程思想中使用面向对象的特性,继承、封装、多态、抽象,能够更容易应对复杂的程序设计
- 现实当中大部分都是用面向对象语言在做面向过程的事
- 因为面向过程的思想更符合人类思考做事过程
- 相反,面向对象的编程思想更难,需要更多经验
- 面向对象和面向过程并不是互斥的。在面向对象为主的程序中也可以用面向过程的思想实现简单的逻辑,比如各种Util和Constant包含静态方法的类。而且面向对象中一个个方法,其中的具体逻辑就是按照面向过程写出来的
- 同时,尽管面向过程语言本身没有直接提供面向对象的特性。但其实也可以通过一些技术实现这些特性,也可以用面向过程语言做面向对象的开发。只是成本会更高
- 抽象类是一个类,只能通过继承来使用,自身不能实例化
- 抽象类既然是类,就可以有成员变量,这些成员变量往往是不同子类所共有的
- 抽象类的抽象方法不需要抽象类实现,但子类必须实现抽象方法
- 抽象类作为一特殊的类,表示的仍是
is-a
的关系 - 接口不是类,是一组方法声明列表,不能有成员变量
- 实现接口的类必须实现接口中声明的方法
- 接口表示的是一种
has-a
的关系
- 以Java语言为例,抽象类不能实例化,所以构造方法要写成
protected
- 需要子类实现的方法时,现在基类中定义该方法,实现中抛出异常
- 封装不稳定的实现,暴露稳定的接口
- 上游系统面向接口而非实现编程,不依赖不稳定的实现细节,这样当实现发生变化的时候,上游系统的代码基本上不需要做改动,以此来降低代码间的耦合性,提高代码的扩展性
- 如果在我们的业务场景中,某个功能只有一种实现方式,未来也不可能被其他实现方式替换,那我们就没有必要为其设计接口,也没有必要基于接口编程,直接使用实现类就可以了
这里说的MVC架构是指Web项目开发中的架构,并非iOS开发中的
大多数后端项目都是采用MVC架构来开发,MVC架构就是一种基于贫血模型架构
MVC-将项目分成了Model、View、Controller三层,分别叫做模型层、展示层和逻辑层。MVC只是一个比较笼统的分层,具体到应用层面会根据项目进行调整
一种用的比较多的MVC分层模型,贫血模型,将代码分为Repository层、Service层和Controller层
- Repository层负责数据层的增删改查
- Service层负责复杂的业务逻辑
- Controller层用于为前端暴露接口
下面展示一下这个分层的样例代码
////////// Controller+VO(View Object) //////////
public class UserController {
private UserService userService; //通过构造函数或者IOC框架注入
public UserVo getUserById(Long userId) {
UserBo userBo = userService.getUserById(userId);
UserVo userVo = [...convert userBo to userVo...];
return userVo;
}
}
public class UserVo {//省略其他属性、get/set/construct方法
private Long id;
private String name;
private String cellphone;
}
////////// Service+BO(Business Object) //////////
public class UserService {
private UserRepository userRepository; //通过构造函数或者IOC框架注入
public UserBo getUserById(Long userId) {
UserEntity userEntity = userRepository.getUserById(userId);
UserBo userBo = [...convert userEntity to userBo...];
return userBo;
}
}
public class UserBo {//省略其他属性、get/set/construct方法
private Long id;
private String name;
private String cellphone;
}
////////// Repository+Entity //////////
public class UserRepository {
public UserEntity getUserById(Long userId) { //... }
}
public class UserEntity {//省略其他属性、get/set/construct方法
private Long id;
private String name;
private String cellphone;
}
为什么叫做贫血模型?
UserBo
只是单纯的数据模型,没有任何操作;具体的操作都在UserService中,这破坏了UserBo
的封装性,所以是面向过程而非OOP,所以UserBo
叫做贫血模型- 贫血模型之所以违反了OOP,根本在于数据与操作是分离的
为什么绝大多数的项目都采用贫血模型?
- 充血模型
- 类内部逻辑应该保持高内聚,类与类之间应该保持松耦合
- 迪米特法则可以指导实现松耦合
- 不该有直接依赖关系的类之间,不要有依赖
- 有依赖关系的类之间,尽量只依赖必要的接口
面向对象设计聚焦在代码层面(主要是针对类),那系统设计就是聚焦在架构层面(主要是针对模块),两者有很多相似之处。很多设计原则和思想不仅仅可以应用到代码设计中,还能用到架构设计中。实际上,我们可以借鉴面向对象设计的步骤,来做系统设计。
面向对象设计的本质就是把合适的代码放到合适的类中。合理地划分代码可以实现代码的高内聚、低耦合,类与类之间的交互简单清晰,代码整体结构一目了然。类比面向对象设计,系统设计实际上就是将合适的功能放到合适的模块中。合理地划分模块也可以做到模块层面的高内聚、低耦合,架构整洁清晰。在面向对象设计中,类设计好之后,我们需要设计类之间的交互关系。类比到系统设计,系统职责划分好之后,接下来就是设计系统之间的交互了。
评判一段代码质量,可以看这段代码是否可读、可扩展、可维护、灵活、简洁、可复用、可测试
具体到细节,我们可以参考如下点
- 目录设置是否合理、模块划分是否清晰、代码结构是否满足“高内聚、松耦合”?
- 是否遵循经典的设计原则和设计思想(SOLID、DRY、KISS、YAGNI、LOD 等)?
- 设计模式是否应用得当?是否有过度设计?
- 代码是否容易扩展?如果要添加新功能,是否容易实现?
- 代码是否可以复用?是否可以复用已有的项目代码或类库?是否有重复造轮子?
- 代码是否容易测试?单元测试是否全面覆盖了各种正常和异常的情况?
- 代码是否易读?是否符合编码规范(比如命名和注释是否恰当、代码风格是否一致等)?