面向对象编程的六大原则是什么
面向对象编程的六大原则是什么
一、总结
一句话总结:单一职责:里氏替换原则:依赖倒置:接口隔离:迪米特法则:开闭原则:
单一职责(一个类只负责一项职责): 里氏替换原则(子类可以扩展父类的功能,但不能改变父类原有的功能):
依赖倒置(尽量面向接口编程):接口隔离(一个类对另一个类的依赖应该建立在最小的接口上):
迪米特法则(高内聚,低耦合:一个类尽量减少对其他对象的依赖):开闭原则:(对扩展开放,对修改关闭)
单一职责:
不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责(一项职责不是说的一个功能,一项职责可以有多个功能,比如老师这个职责就可以教书和讲课)。
里氏替换原则:
所有引用基类的地方必须能透明地使用其子类的对象,也就是说子类可以扩展父类的功能,但不能改变父类原有的功能
依赖倒置:
高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。简单的说就是尽量面向接口编程.
接口隔离:
客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。接口最小化,过于臃肿的接口依据功能,可以将其拆分为多个接口.
迪米特法则:
一个对象应该对其他对象保持最少的了解,简单的理解就是高内聚,低耦合,一个类尽量减少对其他对象的依赖,并且这个类的方法和属性能用私有的就尽量私有化.
开闭原则:
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭.当软件需求变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化.
1、是否这些原则遵守的越多越好?
灵活遵守+取舍:过于严苛的遵守六大原则,将分层变多,类变多,项目变的非常庞大.所以对这些原则要根据实际情况做出取舍.一般分层不要超过6层.超过6层,代码变的难以维护也难以跟踪.
ps:过于严苛的遵守六大原则,将分层变多,类变多,项目变的非常庞大.所以对这些原则要根据实际情况做出取舍.一般分层不要超过6层.超过6层,代码变的难以维护也难以跟踪.
2、单一职责的编程实例是什么?
是什么:一个类只负责一项职责
实例:最开始设计了一个类Human,我们赋予了四项功能(教书,讲课,听课,写作业),一个人不会既教书,又写作业,也不会既听课,又讲课。我们将其拆分:老师类(教书,讲课),学生类:(听课,写作业)
不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。
我们最开始设计了一个类Human,我们赋予了四项功能.
以下是伪代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | class Human { public function 教书(); public function 讲课(); public function 听课(); public function 写作业(); } 但我们发现,一个人不会既教书,又写作业,也不会既听课,又讲课. 所以我们将其拆分: class Teacher { public function 教书(); public function 讲课(); } class Student { public function 听课(); public function 写作业(); } 这就比较好的符合单一职责. |
3、里氏替换原则是什么及实例?
是什么:子类可以扩展父类的功能,但不能改变父类原有的功能
实例:见代码,子类可以实现父类的接口,但是不要去修改父类的方法,比如父类有say方法无参数,子类say方法带参数就违背了里氏替换原则(因为修改了父类原有的功能,改善方法就是把父类的say写成接口即可)
所有引用基类的地方必须能透明地使用其子类的对象,也就是说子类可以扩展父类的功能,但不能改变父类原有的功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 伪代码: class 基类 { public function say(){ return '基类' ;} } class 子类 extends 基类 { public function say($msg) { return $msg; } } $a = new 基类(); $b = new 子类(); echo $b->say( '子类' ); 子类重写了父类的方法,say要传入参数,导致了导致了父类原有的功能被改变了,这违背了里氏替换原则. |
4、依赖倒置是什么及实例?
是什么:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。简单的说就是尽量面向接口编程.
实例:用户的积分数据需要写进文件和写进数据库,传统方式新增加类型(比如再写进缓存)比较麻烦,而面向接口编程就很方便了,写了一个writeLog的接口,文件写方式和数据库写方式都继承这个接口
感悟:实例中的面向接口编程真的很方便,$user->addPoint(100, new Log()); $user->addPoint(100, new DbLog());
高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。简单的说就是尽量面向接口编程.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | 我们需要在用户积分增加的时候,写入一个日志文件.最开始我们写入文件中. class User { ................ public function addPoint($dValue, Log log) { $ this ->point += $dValue; $log->writeLog($ this ); } ............... } class Log { public function writeLog(User $user) { write_log_to_file.... } } // 调用 $user = new User(); $user->addPoint(100, new Log()); 后面需求有变化,需要写入数据库 class User { ................ public function addPoint($dValue, Log log) { $ this ->point += $dValue; $log->writeLog($ this ); } ............... } class DbLog { public function writeLog(User $user) { write_log_to_db.... } } // 调用 $user = new User(); $user->addPoint(100, new Log()); 代码改动颇大,每次需求的变化都需要改动许多代码,我们现在建立一个接口,所有的日志类都继承该接口.我们让User类中的addPoint依赖该接口. interface ILog{ public function writeLog(User $user); } class Log implements ILog { public function writeLog(User $user) { write_log_to_file.... } } class DbLog implements ILog { public function writeLog(User $user) { write_log_to_db.... } } class User { ................ public function addPoint($dValue, ILog log) { $ this ->point += $dValue; $log->writeLog($ this ); } ............... } // 调用 $user = new User(); $user->addPoint(100, new Log()); $user->addPoint(100, new DbLog()); 这样子后面无论再增加其他日志类,只要实现了ILog,均可以调用. |
5、接口隔离是什么?
是什么:客户端不应该依赖它不需要的接口
实例:接口IUser中有(注册接口,手动充值(用于充值失败时,手动补发)接口),但实际中,我们发现手动充值在User类中用不到,因为这显然是个后台管理用户才可以用到.不符合接口隔离原则.我们拆分该接口.
客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。接口最小化,过于臃肿的接口依据功能,可以将其拆分为多个接口.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | interface IUser{ 注册接口 登录接口 充值接口 充值记录接口 手动充值(用于充值失败时,手动补发) } 但实际中,我们发现手动充值在User类中用不到,因为这显然是个后台管理用户才可以用到.不符合接口隔离原则.我们拆分该接口. interface IUser { 注册接口 登录接口 充值接口 充值记录接口 } interface IManageUser { 手动充值(用于充值失败时,手动补发) } |
6、迪米特法则是什么?
是什么:高内聚,低耦合(减少依赖+尽量私有化):一个类尽量减少对其他对象的依赖,并且这个类的方法和属性能用私有的就尽量私有化.
实例:Product类中有公共的buy()和repertory(),这是一个产品类,我们期望用户在购买掉一个产品后,该产品的库存减去1.用户购买了,产品库存才会减去1.减库存,依赖于购买.所以我们应该将repertory声明为private,其他对象也只需要了解该对象的buy即可.
实例:User类中有public function log(Log $log),log中有写日志和读日志方法,我们期望对User类的某些操作,记录日志.但User类log方法依赖Log类,但Log类的printLog方法,User类并不需要,这应该要看日志的系统管理员如:AdminUser需要的方法.,去掉Log类的printLog方法,或者面向接口:(User类中的log方法用的只有写日子方法的log类)
一个对象应该对其他对象保持最少的了解,简单的理解就是高内聚,低耦合,一个类尽量减少对其他对象的依赖,并且这个类的方法和属性能用私有的就尽量私有化.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | class Product { ...... public function buy(){...} public function repertory(){...} ...... } 这是一个产品类,我们期望用户在购买掉一个产品后,该产品的库存减去1.用户购买了,产品库存才会减去1.减库存,依赖于购买.所以我们应该将repertory声明为 private ,其他对象也只需要了解该对象的buy即可. class User { ........ public function log(Log $log) { $log->writeLog(.....); } ........ } class Log { // 写日志 public function writeLog(User $user) { $log->writeLog(.....); } // 打印日志 public function printLog(User $user) { ..... } } 我们期望对User类的某些操作,记录日志.但User类log方法依赖Log类,但Log类的printLog方法,User类并不需要,这应该要看日志的系统管理员如:AdminUser需要的方法. 去掉Log类的printLog方法 class Log { // 写日志 public function writeLog(User $user) { $log->writeLog(.....); } } 或者面向接口: interface ILog { public function writeLog(User $user); } class Log implements ILog { // 写日志 public function writeLog(User $user) { $log->writeLog(.....); } // 打印日志 public function printLog(User $user) { ..... } } class User { ........ public function log(ILog $log) { $log->writeLog(.....); } ........ } |
7、开闭原则的实例?
是什么:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭.当软件需求变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化.
实例:我们有个产品基类,提供了一些列的方法,但随着业务的发展,我们需要Product能够提供打折功能.这个时候我们是直接在Product中增加一个discount方法?还是再扩展一个子类?打折显然不是所有产品都需要的,如果所有产品都需要,我们一开始就会将其设计进去.可能只是BookProduct需要,所以我们应该扩展一个子类.这既没有修改原有的类,又扩展了该类的功能.符合了开闭原则.
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭.当软件需求变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 我们有个产品基类,提供了一些列的方法: class Product { ............ 一系列的方法 ............ } 但随着业务的发展,我们需要Product能够提供打折功能.这个时候我们是直接在Product中增加一个discount方法?还是再扩展一个子类? 打折显然不是所有产品都需要的,如果所有产品都需要,我们一开始就会将其设计进去.可能只是BookProduct需要,所以我们应该扩展一个子类.这既没有修改原有的类,又扩展了该类的功能.符合了开闭原则. class DiscountProduct extends Product { ............ discount() ............ } |
二、面向对象编程的六大原则
一.单一职责:
不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。
我们最开始设计了一个类Human,我们赋予了四项功能.
以下是伪代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | class Human { public function 教书(); public function 讲课(); public function 听课(); public function 写作业(); } 但我们发现,一个人不会既教书,又写作业,也不会既听课,又讲课. 所以我们将其拆分: class Teacher { public function 教书(); public function 讲课(); } class Student { public function 听课(); public function 写作业(); } 这就比较好的符合单一职责. |
二.里氏替换原则:
所有引用基类的地方必须能透明地使用其子类的对象,也就是说子类可以扩展父类的功能,但不能改变父类原有的功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 伪代码: class 基类 { public function say(){ return '基类' ;} } class 子类 extends 基类 { public function say($msg) { return $msg; } } $a = new 基类(); $b = new 子类(); echo $b->say( '子类' ); 子类重写了父类的方法,say要传入参数,导致了导致了父类原有的功能被改变了,这违背了里氏替换原则. |
三.依赖倒置:
高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。简单的说就是尽量面向接口编程.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | 我们需要在用户积分增加的时候,写入一个日志文件.最开始我们写入文件中. class User { ................ public function addPoint($dValue, Log log) { $ this ->point += $dValue; $log->writeLog($ this ); } ............... } class Log { public function writeLog(User $user) { write_log_to_file.... } } // 调用 $user = new User(); $user->addPoint(100, new Log()); 后面需求有变化,需要写入数据库 class User { ................ public function addPoint($dValue, Log log) { $ this ->point += $dValue; $log->writeLog($ this ); } ............... } class DbLog { public function writeLog(User $user) { write_log_to_db.... } } // 调用 $user = new User(); $user->addPoint(100, new Log()); 代码改动颇大,每次需求的变化都需要改动许多代码,我们现在建立一个接口,所有的日志类都继承该接口.我们让User类中的addPoint依赖该接口. interface ILog{ public function writeLog(User $user); } class Log implements ILog { public function writeLog(User $user) { write_log_to_file.... } } class DbLog implements ILog { public function writeLog(User $user) { write_log_to_db.... } } class User { ................ public function addPoint($dValue, ILog log) { $ this ->point += $dValue; $log->writeLog($ this ); } ............... } // 调用 $user = new User(); $user->addPoint(100, new Log()); $user->addPoint(100, new DbLog()); 这样子后面无论再增加其他日志类,只要实现了ILog,均可以调用. |
四.接口隔离:
客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。接口最小化,过于臃肿的接口依据功能,可以将其拆分为多个接口.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | interface IUser{ 注册接口 登录接口 充值接口 充值记录接口 手动充值(用于充值失败时,手动补发) } 但实际中,我们发现手动充值在User类中用不到,因为这显然是个后台管理用户才可以用到.不符合接口隔离原则.我们拆分该接口. interface IUser { 注册接口 登录接口 充值接口 充值记录接口 } interface IManageUser { 手动充值(用于充值失败时,手动补发) } |
五.迪米特法则:
一个对象应该对其他对象保持最少的了解,简单的理解就是高内聚,低耦合,一个类尽量减少对其他对象的依赖,并且这个类的方法和属性能用私有的就尽量私有化.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | class Product { ...... public function buy(){...} public function repertory(){...} ...... } 这是一个产品类,我们期望用户在购买掉一个产品后,该产品的库存减去1.用户购买了,产品库存才会减去1.减库存,依赖于购买.所以我们应该将repertory声明为 private ,其他对象也只需要了解该对象的buy即可. class User { ........ public function log(Log $log) { $log->writeLog(.....); } ........ } class Log { // 写日志 public function writeLog(User $user) { $log->writeLog(.....); } // 打印日志 public function printLog(User $user) { ..... } } 我们期望对User类的某些操作,记录日志.但User类log方法依赖Log类,但Log类的printLog方法,User类并不需要,这应该要看日志的系统管理员如:AdminUser需要的方法. 去掉Log类的printLog方法 class Log { // 写日志 public function writeLog(User $user) { $log->writeLog(.....); } } 或者面向接口: interface ILog { public function writeLog(User $user); } class Log implements ILog { // 写日志 public function writeLog(User $user) { $log->writeLog(.....); } // 打印日志 public function printLog(User $user) { ..... } } class User { ........ public function log(ILog $log) { $log->writeLog(.....); } ........ } |
六.开闭原则:
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭.当软件需求变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 我们有个产品基类,提供了一些列的方法: class Product { ............ 一系列的方法 ............ } 但随着业务的发展,我们需要Product能够提供打折功能.这个时候我们是直接在Product中增加一个discount方法?还是再扩展一个子类? 打折显然不是所有产品都需要的,如果所有产品都需要,我们一开始就会将其设计进去.可能只是BookProduct需要,所以我们应该扩展一个子类.这既没有修改原有的类,又扩展了该类的功能.符合了开闭原则. class DiscountProduct extends Product { ............ discount() ............ } |
ps:过于严苛的遵守六大原则,将分层变多,类变多,项目变的非常庞大.所以对这些原则要根据实际情况做出取舍.一般分层不要超过6层.超过6层,代码变的难以维护也难以跟踪.
https://www.cnblogs.com/itfenqing/p/7750524.html
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· PPT革命!DeepSeek+Kimi=N小时工作5分钟完成?
· What?废柴, 还在本地部署DeepSeek吗?Are you kidding?
· DeepSeek企业级部署实战指南:从服务器选型到Dify私有化落地
· 程序员转型AI:行业分析
2018-05-07 WebStorm(Amaze开发工具)--JavaScript 开发工具
2018-05-07 amazeui学习笔记三(你来我往1)--常见问题FAQs
2018-05-07 amazeui学习笔记二(进阶开发5)--Web 组件开发规范Rules
2018-05-07 amazeui学习笔记二(进阶开发4)--JavaScript规范Rules
2018-05-07 HTML的SEO(搜索引擎优化)标准
2018-05-07 amazeui学习笔记二(进阶开发3)--HTML/CSS规范Rules
2018-05-07 hbs模板(zmaze ui用的)