这是我参与更文挑战的第1天,活动详情查看:更文挑战
面向对象六大原则
单一职责原则
- 介绍: 单一职责原则(Single Reponsibility Principle – SRP)
- 概念: 要求一个类应该只包含一组较高相关性的函数、数据封装。
- 理解: 理解将一个大系统尽可能拆分成一个个子模块运作,分工明确,职责清晰。
举例
例如计算机系统由硬件系统和软件系统组成。在计算机工作时每个系统都去执行自己的任务功能,从而实现计算机系统整个大系统运作。
通过一个伪代码简单模拟计算机运作,如下实现计算机执行硬件相关功能、还去执行软件相关功能。但从面向对象 – 单一职责原则来看,这个代码可以进一步优化。
class computer{
void hardwareFunc(){
... 运行硬件相关功能
}
void softwareFunc(){
... 运行软件相关功能
}
}
复制代码
修改完之后的代码如下,单一职责原则就是需要每个类各执其职做自己适合和应该做的事情。计算机系统更多作为统筹,主控硬件和软件,软件代码和硬件代码则由各自子系统负责完成。
class computer{
Software software;
Hardware hardware;
}
class Hardware{
void hardwareFunc(){
... 运行硬件相关功能
}
}
class Software{
void softwareFunc(){
... 运行软件相关功能
}
}
复制代码
开闭原则
- 介绍: 开闭原则(Open Close Principle – OCP)
- 概念: 软件中的对象应该是对外扩展是开放的,对外修改是封闭的。
- 理解: 不破坏原有方法,通过扩充方法或是继承升级达到功能完善,这样的好处在于不会对旧逻辑造成影响避免产生bug,又能扩充对象实现达到代码健硕。
举例
如下代码中一个系统System运行某种代码时通过type类型进行区分,对于后期扩展很不利。比如增加外置硬件功能ExtraHardWare则需要改动Code类和System类适配扩展。
class Hardware extend Code{
Hardware(){
type = 1;
}
void run1(){
... 运行硬件相关功能
}
}
class Software extend Code{
Software(){
type = 2;
}
void run2(){
... 运行软件相关功能
}
}
class Code{
int type;
void run1(){
... 运行软件相关功能
}
void run2(){
... 运行硬件相关功能
}
}
class System{
void start(Code code){
if(code.type == 1){
code.run1();
}else if(code.type == 2){
code.run2();
}
}
}
复制代码
符合开闭原则对Code进行重构,Code类抽象化实现基础功能。当需要拓展功能时只需要增加一种类型继承Code就能轻松扩展。
class Hardware extend Code{
void run(){
... 运行硬件相关功能
}
}
class ExtraHardware extend Code{
void run(){
... 运行外部硬件相关功能
}
}
class Software extend Code{
void run(){
... 运行软件相关功能
}
void clearCache(){}
}
abstart Code{
void run();
}
class System{
void start(Code code){
code.run();
}
}
复制代码
里氏替换原则
- 介绍: 里氏替换原则(Liskov Substitution Principle — LSP)
- 概念: 如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1代换o2时,程序P的行为没有变化,那么类型S是类型T的子类型。
- 理解: 只要父类能出现的,子类可以出现,即使替换为子类也不影响使用,反之则不支持。进一步理解就是对抽象的使用。
举例
最好的例子就在上述的开闭原则。
class System{
void start(Code code){
code.run();
}
}
System system = new System();
Software software = new Software();
system.start(software);
复制代码
像System类运行start时它只知道code但不知道code具体是哪个子类,所以在System中只能执行code的run方法不能执行Software的clearCache方法。
依赖倒置原则
- 介绍: 依赖倒置原则(Dependency Inversion Principle – DIP)
- 概念: 抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。
- 理解: 模块之间依赖是通过抽象发生的,实现类之间不能发生直接依赖关系,依赖关系应该是通过接口或则是抽象类产生。类和类之间直接依赖细节就有直接耦合,当具体实现发生变化就意味着需要修改依赖者。
举例
同样还是开闭原则的举例,若除了System需要可以跑Hardware,NewSystem也能跑Hardware怎么办。
class Software extend Code{
void run(){
... 运行软件相关功能
}
void clearCache(){}
}
abstart Code{
void run();
}
class System{
void start(Code code){
code.run();
}
}
复制代码
修改后,所有的类抽象化。大家都不用真身相见,不依赖细节只依赖抽象,符合依赖倒置原则。
class Hardware implements Code{
void run(){
... 运行硬件相关功能
}
}
class System implements BaseSystem{
void start(Code code){
code.run();
}
}
class NewSystem implements BaseSystem{
void start(Code code){
code.run();
}
}
interface Code{
void run();
}
interface BaseSystem{
void start(Code code);
}
void main(){
Code software = new Software();
BaseSystem system1 = new NewSystem();
system1.start(software);
BaseSystem system2 = new System();
system2.start(software);
}
复制代码
接口隔离原则
- 介绍: 接口隔离原则(Interface Segregation Principle, ISP)
- 概念: 客户端不依赖不需要的接口。
- 理解: 接口系统细分化,解开耦合,每个对象只关心自身关注的接口。
举例
如下代码结合依赖倒置原则,接口隔离原则在其基础上更进一步,每个类只关注自身需求,System关注run、Thread关注await。因此在Hardware对于抽象接口的使用中将两个类各自关系的抽象接口拆分开,分别由Code和Process各自定义。从而达到接口隔离,自私其职目的。
class Hardware implements Code,Process{
void run(){
... 运行硬件相关功能
}
void await(){
... 线程等待功能
}
}
class System{
void start(Code code){
code.run();
}
}
class Thread{
void await(Code code){
code.await();
}
}
interface Code{
void run();
}
interface Process{
void await();
}
void main(){
Code software = new Software();
System system = new System();
system.start(software);
Thread thread = new Thread();
thread.await(software);
}
复制代码
迪米特法则
- 介绍: 迪米特法则(Law of Demeter – LoD)
- 概念: 一个软件实体应当尽可能少地与其他实体发生相互作用。
- 理解: 一个类对于需要的对象耦合知道的越少越好,调用者只关心需要执行的方法即可。类与类之间关系越密切就越容易产生耦合,对于后期代码维护造成很大困扰。
举例
迪米特法则最大程度上将对象和对象直接的耦合最小化,如下如所示将员工直接的联系通过一个对象作为中间媒介相互隔离开,后期对于修改或许就只要就更改中间媒介对象即可。
不符合 | 符合 |
---|---|
![]() |
![]() |