摘要:面向对象设计的五大原则单一职责原则接口隔离原则开放封闭原则替换原则依赖倒置原则。主要是针对继承的设计原则,继承与派生多态是的主要特性。
面向对象设计的五大原则:单一职责原则、接口隔离原则、开放-封闭原则、替换原则、依赖倒置原则。这些原则主要是由Robert C.Martin在《敏捷软件开发——原则、方法、与实践》一书中总结出来,这五大原则也是23种设计模式的基础。
单一职责原则 Single Pesponsibility Principle, SRP
在MVC框架中,对于表单插入数据库字段过滤与安全检查应该是放在control层处理还是model层处理,这类问题都可以归到单一职责的范围。
单一职责有两个含义:
避免相同的职责分散到不同的类中
一个类承担太多职责
遵守SRP的好处:
减少类之间的耦合
提高类的复用性
在实际代码开发中的应用:工厂模式、命令模式、代理模式等。
工厂模式(Factory)允许在代码执行时实例化对象。之所以被称为工厂模式是因为它负责“生产”对象。以数据库为例,工厂需要的就是根据不同的参数,生成不同的实例化对象。它只负责生产对象,而不负责对象的具体内容。
定义一个适配器接口:
定义MySQL数据库操作类:
_dbLink = @mysql_connect($config->host . (empty($config->port) ? "" : ":" . $config->port), $config->user, $config->password, true)) { if(@mysql_select_db($config->database, $this->_dbLink)){ if($config->charset){ mysql_query("SET NAMES "{$config->charset}"", $this->_dbLink); } return $this->_dbLink; } } //数据库异常 throw new DbException(@mysql_error($this->_dbLink)); } /** * 执行数据库查询 * @param string $query 数据库查询SQL字符串 * @param mixed $handle 连接对象 * @return resource */ public function query($query, $handle) { if ($resource = @mysql_query($query, $handle)) { return $resource; } } } ?>
SQLite数据库操作类:
_dbLink = sqlite_open($config->file, 0666, $error)) { return $this->_dbLink; } throw new DbException($error); } /** * 执行数据库查询 * @param string $query 数据库查询SQL字符串 * @param mixed $handle 连接对象 * @return resource */ public function query($query, $handle) { if ($resource = @sqlite_query($query, $handle)) { return $resource; } } } ?>
定义一个工厂类,根据传入不同的参数生成需要的类:
调用:
$db = sqlFactory::factory("MySQL"); $db = sqlFactory::factory("SQLite");
命令模式分离“命令的请求者”和“命令的实现者”方面的职责。
模拟服务员与厨师的过程:
cook = $cook; } public function execute(){ $this->cook->meal();//把消息传递给厨师,让厨师做饭 } } class DrinkCommand implements Command { private $cook; //绑定命令接受者 public function __construct(cook $cook){ $this->cook = $cook; } public function execute(){ $this->cook->drink(); } } ?>
模拟顾客与服务员的过程:
mealCommand = $mealCommand; $this->drinkCommand = $drinkCommand; } public function callMeal(){ $this->mealCommand->execute(); } public function callDrink(){ $this->drinkCommand->execute(); } } ?>
实现命令模式:
$control = new cookControl; $cook = new cook; $mealCommand = new MealCommand($cook); $drinkCommand = new DrinkCommand($cook); $control->addCommand($mealCommand, $drinkCommand); $control->callMeal(); $control->callDrink();
接口隔离原则 Interface Segregation Principle,ISP
接口隔离原则(Interface Segregation Principle,ISP)表明客户端不应该被强迫实现一些不会使用的接口,应该把胖接口分组,用多个接口代替它,每个接口服务于一个子模块。简单地说,就是使用多个专门的接口比使用单个接口要好很多。
ISP主要观点:
1.一个类对另外一个类的依赖性应当是建立在最小接口上的。
ISP可以达到不强迫客户(接口使用者)依赖于他们不用的方法,接口的实现类应该只呈现为单一职责的角色(遵守SRP原则)。
ISP可以降低客户之间的相互影响——当某个客户程序要求提供新的职责(需求变化)而迫使接口发生变化时,影响到其他客户程序的可能性会最小。
2.客户端程序不应该依赖它不需要的接口方法(功能)。
ISP强调的是接口对客户端的承诺越少越好,并且要做到专一。
接口污染就是为接口添加不必要的职责。“接口隔离”其实就是定制化服务设计的原则。使用接口的多重继承实现对不同的接口的组合,从而对外提供组合功能——达到“按需提供服务”。
对于接口的污染,使用下面两种处理方式:
利用委托分离接口。
利用多继承分离接口。
委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理,如策略模式、代理模式等都应用到了委托的概念。
开放-封闭原则
随着软件系统的规模不断增大,软件系统的维护和修改的复杂性不断提高,这种困境促使法国工程院士Bertrand Meyer在1998年提出了“开放-封闭”(Open-Close Principle, OCP)原则,基本思想是:
Open(Open for extension)模块的行为必须是开放的、支持扩展的,而不是僵化的。
Closed(Closed for modification)在对模块的功能进行扩展时,不应该影响或大规模地影响已有的程序模块。
换句话说,也就是要求开发人员在不修改系统中现有功能代码(源代码或二进制代码)的前提下,实现对应用系统的软件功能的扩展。用一句话概括就是:一个模块在扩展性方面应该是开放的而在更改性方面应该是封闭的。
开放-封闭能够提高系统的可扩展性和可维护性,但这也是相对的。
以播放器为例,先定义一个抽象的接口:
interface Process { public function process(); }
然后对此接口进行扩展,实现解码和输出的功能:
class playerEncode implements Proess { public function process(){ echo "encode "; } } class playerOutput implements Process { public function process(){ echo "output "; } }
对于播放器的各种功能,这里是开放的。只要你遵守约定,实现了process接口,就能给播放器添加新的功能模块。
接下来为定义播放器的线程调度管理器,播放器一旦接收到通知(可以是外部单击行为,也可以是内部的notify行为),将回调实际的线程处理:
class playProcess { private $message = null; public function __construct(){ } public function callback(Event $event){ $this->message = $event->click(); if($this->message instanceof Process){ $this->message->process(); } } }
具体的产品出来了,在这里定义一个MP4类,这个类是相对封闭的,其中定义事件的处理逻辑:
class MP4 { public function work(){ $playProcess = new playProcess(); $playProcess->callback(new Event("encode")); $playProcess->callback(new Event("output")); } }
最后为事件分拣的处理类,此类负责对事件进行分拣,判断用户或内部行为,以产生正确的“线程”,供播放器内置的线程管理器调度:
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; } } }
运行:
$mp4 = new MP4; $mp4->work(); //打印结果 encode output
如何遵守开放-封闭原则
实现开放-封闭的核心思想就是抽象编程的核心思想就是对抽象编程,而不是对具体编程,因为抽象相对稳定。让类依赖于固定的抽象,这样的修改就是封闭的;而通过面向对象的继承和多态机制,可以实现对抽象体的继承,通过覆写其方法来改变固有的行为,实现新的扩展方法,所以对于扩展就是开放的。
1.在设计方面充分应用“抽象”和封装的思想。
一方面就是要在软件系统中找出各种可能的“可变因素”,并将之封装起来;另一方面,一种可变性因素不应当散落在多个不同代码模块中,而应当被封装到一个对象中。
2.在系统功能编程实现方面应用面向接口编程。
当需求发生变化时,可以提供该接口新的实现类,以求适应变化。
面向接口编程要求功能类实现接口,对象声明为接口类型。再设计模式中,装饰模式比较明显地用到OCP。
替换原则
替换原则也称里氏替换原则(Liskov Substitution Principle, LSP)的定义和主要思想如下:由于面向对象编程技术中的继承在具体的编程中过于简单,在许多系统的设计和编程实现中,我们并没有认真地、理性地思考应用系统中各个类之间的继承关系是否合适,派生类是否能正确地对其基类中的某些方法进行重写等问题。因此经常出现滥用继承或者错误地进行了继承等现象,给系统的后期维护带来不少麻烦。
LSP指出:子类型必须能够替换掉它们的父类型,并出现在父类能够出现的任何地方。
LSP主要是针对继承的设计原则,继承与派生(多态)是OOP的主要特性。
如何遵守LSP设计原则:
父类的方法都要在子类中实现或重写,并且派生类只实现其抽象类中声明的方法,而不应当给出多余的方法定义或实现。
在客户段程序中只应该使用父类对象而不应当直接使用子类对象,这样可以实现运行期绑定(动态绑定)。
如果A、B两个类违反了LSP的设计,通常的做法是创建一个新的抽象类C,作为两个具体类的超类,将A和B的共同行为移到C中,从而解决A和B行为不完全一致的问题。
依赖倒置原则 Dependence Inversion Principle, DIP
依赖倒置简单地讲就是将依赖关系倒置为依赖接口,具体概念如下:
上层模块不应该依赖于下层模块,它们共同依赖于一个抽象(父类不能依赖子类,它们都要依赖抽象类)。
抽象不能依赖于具体,具体应该依赖于抽象。
为什么要依赖接口?因为接口体现对问题的抽象,同时由于抽象一般是相对稳定的或者是相对变化不频繁的,而具体是易变的。因此,依赖抽象是实现代码扩展和运行期内绑定(多态)的基础:只要实现了该抽象类的子类,都可以被类的使用者使用。
working(); } } class workB { private $e; public function set(employee $e){ $this->e = $e; } public function work(){ $this->e->working(); } } $worka = new workA; $worka->work(); $workb = new workB; $workb->set(new teacher()); $workb->work();
在workA中,work方法依赖于teacher实现;在workB中,work转而依赖于抽象,这样可以把需要的对象通过参数传入。
在workB中,teacher实例通过setter方法传入,从而实现了工厂模式。由于这样的是实现是硬编码的,为了实现代码的进一步扩展,把这个依赖关系写在配置文件里,指明workB需要一个teacher对象,专门由一个程序检测配置是否正确(如所依赖的类文件是否存在)以及加载配置中所依赖的实现,这个检测程序,就称为IOC容器。
IOC(Inversion Of Control)是依赖倒置原则(Dependence Inversion Principle, DIP)的同义词。依赖注入(DI)和依赖查找(DS)是IOC的两种实现。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/28428.html
摘要:设计原则梳理,参考核心技术与最佳实践敏捷开发原则模式与实践,文章面向对象设计的五大原则设计模式原则单一职责原则定义特性仅有一个引起类变化的原因一个类只承担一项职责职责变化的原因避免相同的职责分散到不同的类,功能重复问题一个类承担的职责过多, PHP设计原则梳理,参考《PHP核心技术与最佳实践》、《敏捷开发原则、模式与实践》,文章PHP面向对象设计的五大原则、设计模式原则SOLID 单一...
摘要:如果看不懂的话,可以在评论区中提问,我会第一时间回答你无论何时我一直都在嗯哼该文章属于编程中的那些经典套路设计模式汇总系列 在正式阅读前,我先谈谈我们该用什么姿势和心态学习设计模式: 如果你还没有过多的编程经验(泛指半年以下),我建议你把它当做小说来看,能看懂多少是多少,因为半年以下经验的程序员用到设计模式的情况只会出现在面试上,至于实际工作中?相对来说这部分不会由你负责。 如果你已...
摘要:前言本章我们要讲解的是五大原则语言实现的第篇,里氏替换原则。因此,违反了里氏替换原则。与行为有关,而不是继承到现在,我们讨论了和继承上下文在内的里氏替换原则,指示出的面向对象。 前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第3篇,里氏替换原则LSP(The Liskov Substitution Principle )。英文原文:http://fre...
摘要:简单工厂模式就是遵循了这一原则,它让不同职责的类各司其职。重点来了,简单工厂模式的不足但是简单工厂模式有一个不足,虽然它遵循了单一职责原则,但它违反了另一条同样很重要的原则开放封闭原则。 该文章属于《编程中的那些经典套路——设计模式汇总》系列,并且以下内容基于语言PHP 面向对象五大原则中有一点非常重要的原则:单一职责原则。 简单工厂模式就是遵循了这一原则,它让不同职责的类各司其职。 ...
摘要:,开始我们的第一篇单一职责。通过解耦可以让每个职责工更加有弹性地变化。关于本文本文转自大叔的深入理解系列。深入理解系列文章,包括了原创,翻译,转载,整理等各类型文章,原文是大叔的一个非常不错的专题,现将其重新整理发布。 前言 Bob大叔提出并发扬了S.O.L.I.D五大原则,用来更好地进行面向对象编程,五大原则分别是: The Single Responsibility Princi...
阅读 702·2021-11-18 10:02
阅读 3578·2021-09-02 10:21
阅读 1725·2021-08-27 16:16
阅读 2057·2019-08-30 15:56
阅读 2385·2019-08-29 16:53
阅读 1371·2019-08-29 11:18
阅读 2951·2019-08-26 10:33
阅读 2639·2019-08-23 18:34