第二章 面向对象的设计原则
在面向对象中,类是基本单位,各种设计都是围绕着类来进行的。类与类之间的关系,构成了设计模式的大部分内容
2.1 面向对象设计的五大原则
1、单一职责原则(Single Pesponsibility Principle,SRP)
- 含义
1、避免相同的职责分散到不同的类中
2、避免一个类承担太多的职责 - 作用
1、减少类之间的耦合
2、提高类的复用性
数据库操作,SNS网站的动态实现
实例:餐馆示例
/**
* 厨师类,命令接受者与执行者
*/
class cook {
public function meal() {
echo "番茄炒蛋","<br/>";
}
public function drink() {
echo "紫菜蛋花汤","<br/>";
}
public function ok() {
echo "完毕","<br/>";
}
}
/**
* 命令接口(服务员)
*/
interface Waiter {
public function execute();
}
/**
* 模拟服务员与厨师的交互过程
*/
class MealWaiter implements Waiter {
private $cook;
public function __construct(cook $cook) {//类型限定,下同
$this->cook = $cook;
}
public function execute() {
$this->cook->meal();
}
}
class DrinkWaiter implements Waiter {
private $cook;
public function __construct(cook $cook) {
$this->cook = $cook;
}
public function execute() {
$this->cook->drink();
}
}
/**
* 顾客点菜
*/
class Custommers {
private $mealWaiter;
private $drinkWaiter;
public function addWaiter(Waiter $mealWaiter, Waiter $drinkWaiter) {//类型限定
$this->mealWaiter = $mealWaiter;
$this->drinkWaiter = $drinkWaiter;
}
public function callmeal() {
$this->mealWaiter->execute();
}
public function calldrink() {
$this->drinkWaiter->execute();
}
}
/**
* 实现命令模式
*/
$custommer = new Custommers;//一个顾客
$cook = new cook;//一个厨师
$mealWaiter = new MealWaiter($cook);//一个点菜的服务员
$drinkWaiter = new DrinkWaiter($cook);//一个点汤的服务员
$custommer->addWaiter($mealWaiter, $drinkWaiter);//叫来这两个服务员
$custommer->callmeal();//点菜
$custommer->calldrink();//点汤
- 需要遵循的做法
1、根据业务流程,把业务对象提炼出来
标准化业务链后,对业务对象内部进一步处理,把第一次标准化视为最高层抽象,第二次视为次高层抽象
2、职责的分类需要注意
规划好各自的职责范围
2、接口隔离原则(Interface Segregation Principle, ISP)
- 接口隔离
~ 一个类对另外一个类的依赖性应当是建立在最小的接口上
~ 接口使用方程序不应该依赖它不需要的接口方法 - 对接口的污染
~ 定制化服务设计,使用接口的多重继承实现对不同的接口的组合,从而对外提供组合功能。
~ 高内聚:接口应该具备一些基本的功能,能独一完成一个基本的任务,而不能过于细化
~ 处理方法:* 利用委托分离接口 * 利用多继承分离接口
3、开放-封闭原则 (Open-Close Principle, OCP)
- 基本思想
1、Open(Open for extension):模块的行为必须是开放、支持扩展的,而不是僵化的。
2、Closed (Closed for modification):在对模块的功能进行扩展时,不应该影响已有的程序模块 - 一个模块在扩展性方面应该是开放的而在更改性方面应该是封闭的
- 示例
<?php
//开放封闭原则的示例:播放器
interface process {
public function process();
}
//编码
class playerencode implements process {
public function process() {
echo "encode","<br/>";
}
}
//输出
class playeroutput implements process {
public function process() {
echo "output","<br/>";
}
}
//播放器的线程调度管理器
class playprocess {
private $message = NULL;
public function __construct() {
}
public function callback(event $event) {
$this->message = $event->click();
if ($this->message instanceof process) {//instanceof:1、判断一个对象是否是某个类的实例;2、判断一个对象是否实现了某个接口
$this->message->process();
}
}
}
//播放器的事件处理类,对事件进行分拣
class event {
private $m;
public function __construct($me) {
$this->m = $me;
}
public function click() {
switch ($this->m) {
case 'encode':
return new playerencode();
break;
case 'output':
return new playeroutput();
break;
}
}
}
//播放器的事件处理逻辑
class mp4 {
public function work() {
$playProcess = new playProcess();
$playProcess->callback(new event('encode'));
$playProcess->callback(new event('output'));
}
}
//实现
$mp4 = new mp4();
$mp4->work();
- 如何遵守OCP原则
1、在设计方面充分应用抽象和封装思想
~ 找出各种可变因素,将之封装
~ 一种可变因素封装在一个对象中
2、在系统功能编程实现方面应用面向接口的编程
~ 当需求发生变化时,可以提供该接口新的实现类,以求适应变化
4、替换原则/里氏替换原则(Liskov Substitution Principle, LSP)
- 针对滥用继承的问题提出的设计原则
- 子类必须能够替换掉它们的父类,并出现在父类的任何地方
- 遵循方式
1、父类的方法都要在子类中实现或者重写,并且派生类只实现其抽象类中声明的方法,而不应给出多余的方法定义或实现
2、在客户端程序中只应该使用父类对象而不应当直接使用子类对象,这样可以实现运行期绑定(动态多态)
5、依赖倒置原则(Dependence Inversion Principle, DIP)/IOC(Inversion of Control)
- 上层模块不应该依赖于下层模块,它们共同依赖于同一个对象(父类不能依赖子类,它们都要依赖抽象类)
- 抽象不能依赖于具体,具体应该依赖于抽象
网友评论