面向对象编程的六大原则是什么
一、总结
一句话总结:单一职责:里氏替换原则:依赖倒置:接口隔离:迪米特法则:开闭原则:
单一职责:
不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责(一项职责不是说的一个功能,一项职责可以有多个功能,比如老师这个职责就可以教书和讲课)。
里氏替换原则:
所有引用基类的地方必须能透明地使用其子类的对象,也就是说子类可以扩展父类的功能,但不能改变父类原有的功能
依赖倒置:
高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。简单的说就是尽量面向接口编程.
接口隔离:
客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。接口最小化,过于臃肿的接口依据功能,可以将其拆分为多个接口.
迪米特法则:
一个对象应该对其他对象保持最少的了解,简单的理解就是高内聚,低耦合,一个类尽量减少对其他对象的依赖,并且这个类的方法和属性能用私有的就尽量私有化.
开闭原则:
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭.当软件需求变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化.
1、是否这些原则遵守的越多越好?
ps:过于严苛的遵守六大原则,将分层变多,类变多,项目变的非常庞大.所以对这些原则要根据实际情况做出取舍.一般分层不要超过6层.超过6层,代码变的难以维护也难以跟踪.
2、单一职责的编程实例是什么?
不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。
我们最开始设计了一个类Human,我们赋予了四项功能.
以下是伪代码:
class Human
{
public function 教书();
public function 讲课();
public function 听课();
public function 写作业();
}
但我们发现,一个人不会既教书,又写作业,也不会既听课,又讲课.
所以我们将其拆分:
class Teacher
{
public function 教书();
public function 讲课();
}
class Student
{
public function 听课();
public function 写作业();
}
这就比较好的符合单一职责.
3、里氏替换原则是什么?
所有引用基类的地方必须能透明地使用其子类的对象,也就是说子类可以扩展父类的功能,但不能改变父类原有的功能
伪代码:
class 基类
{
public function say(){return '基类';}
}
class 子类 extends 基类
{
public function say($msg)
{
return $msg;
}
}
$a = new 基类();
$b = new 子类();
echo $b->say('子类');
子类重写了父类的方法,say要传入参数,导致了导致了父类原有的功能被改变了,这违背了里氏替换原则.
4、依赖倒置是什么?
高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。简单的说就是尽量面向接口编程.
我们需要在用户积分增加的时候,写入一个日志文件.最开始我们写入文件中.
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、接口隔离是什么?
客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。接口最小化,过于臃肿的接口依据功能,可以将其拆分为多个接口.
interface IUser{
注册接口
登录接口
充值接口
充值记录接口
手动充值(用于充值失败时,手动补发)
}
但实际中,我们发现手动充值在User类中用不到,因为这显然是个后台管理用户才可以用到.不符合接口隔离原则.我们拆分该接口.
interface IUser
{
注册接口
登录接口
充值接口
充值记录接口
}
interface IManageUser
{
手动充值(用于充值失败时,手动补发)
}
6、迪米特法则是什么?
一个对象应该对其他对象保持最少的了解,简单的理解就是高内聚,低耦合,一个类尽量减少对其他对象的依赖,并且这个类的方法和属性能用私有的就尽量私有化.
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、开闭原则的实例?
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭.当软件需求变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化.
我们有个产品基类,提供了一些列的方法:
class Product
{
............
一系列的方法
............
}
但随着业务的发展,我们需要Product能够提供打折功能.这个时候我们是直接在Product中增加一个discount方法?还是再扩展一个子类?
打折显然不是所有产品都需要的,如果所有产品都需要,我们一开始就会将其设计进去.可能只是BookProduct需要,所以我们应该扩展一个子类.这既没有修改原有的类,又扩展了该类的功能.符合了开闭原则.
class DiscountProduct extends Product
{
............
discount()
............
}
二、面向对象编程的六大原则
一.单一职责:
不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。
我们最开始设计了一个类Human,我们赋予了四项功能.
以下是伪代码:
class Human
{
public function 教书();
public function 讲课();
public function 听课();
public function 写作业();
}
但我们发现,一个人不会既教书,又写作业,也不会既听课,又讲课.
所以我们将其拆分:
class Teacher
{
public function 教书();
public function 讲课();
}
class Student
{
public function 听课();
public function 写作业();
}
这就比较好的符合单一职责.
二.里氏替换原则:
所有引用基类的地方必须能透明地使用其子类的对象,也就是说子类可以扩展父类的功能,但不能改变父类原有的功能
伪代码:
class 基类
{
public function say(){return '基类';}
}
class 子类 extends 基类
{
public function say($msg)
{
return $msg;
}
}
$a = new 基类();
$b = new 子类();
echo $b->say('子类');
子类重写了父类的方法,say要传入参数,导致了导致了父类原有的功能被改变了,这违背了里氏替换原则.
三.依赖倒置:
高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。简单的说就是尽量面向接口编程.
我们需要在用户积分增加的时候,写入一个日志文件.最开始我们写入文件中.
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,均可以调用.
四.接口隔离:
客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。接口最小化,过于臃肿的接口依据功能,可以将其拆分为多个接口.
interface IUser{
注册接口
登录接口
充值接口
充值记录接口
手动充值(用于充值失败时,手动补发)
}
但实际中,我们发现手动充值在User类中用不到,因为这显然是个后台管理用户才可以用到.不符合接口隔离原则.我们拆分该接口.
interface IUser
{
注册接口
登录接口
充值接口
充值记录接口
}
interface IManageUser
{
手动充值(用于充值失败时,手动补发)
}
五.迪米特法则:
一个对象应该对其他对象保持最少的了解,简单的理解就是高内聚,低耦合,一个类尽量减少对其他对象的依赖,并且这个类的方法和属性能用私有的就尽量私有化.
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(.....);
}
........
}
六.开闭原则:
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭.当软件需求变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化.
我们有个产品基类,提供了一些列的方法:
class Product
{
............
一系列的方法
............
}
但随着业务的发展,我们需要Product能够提供打折功能.这个时候我们是直接在Product中增加一个discount方法?还是再扩展一个子类?
打折显然不是所有产品都需要的,如果所有产品都需要,我们一开始就会将其设计进去.可能只是BookProduct需要,所以我们应该扩展一个子类.这既没有修改原有的类,又扩展了该类的功能.符合了开闭原则.
class DiscountProduct extends Product
{
............
discount()
............
}
ps:过于严苛的遵守六大原则,将分层变多,类变多,项目变的非常庞大.所以对这些原则要根据实际情况做出取舍.一般分层不要超过6层.超过6层,代码变的难以维护也难以跟踪.
https://www.cnblogs.com/itfenqing/p/7750524.html