什麼是封裝?
把客觀的事物封裝成抽象的類,並且類別可以把自己的資料和方法只讓可信的類別或物件操作,對不可信的類別進行資訊的隱藏。簡單的說就是:封裝使物件的設計者與物件的使用者分開,使用者只要知道物件可以做什麼就可以了,不需要知道具體是怎麼實現的。封裝可以有助於提高類別和系統的安全性。
什麼是繼承?
繼承指的是建立一個新的衍生類,從一個或多個先前定義的類別中繼承資料和函數,可以重新定義或加進新資料和函數,從而建立了類的層次或等級。
什麼是多型?
多態性指的是: 相同操作作用與不同類別的實例,將產生不同的執行結果,也就是不同類別的物件收到相同的訊息時,將會得到不同的結果。
class eat { public function breakfast() { echo "吃早饭!"; } } class typist { public function type() { echo "打字!"; } } function printWorking($obj) { if ($obj instanceof eat) { echo $obj->breakfast(); } elseif ($obj instanceof typist) { echo $obj->type(); } else { echo "error"; } } printWorking(new eat()); echo PHP_EOL; printWorking(new typist());
輸出:
吃早餐!打字!
單一職責原則,開放封閉原則,里氏替換原則,依賴倒置原則,介面隔離原則
什麼是單一職責原則?
簡單的來說就是: 一個類別只做一件事情,不要為類別實作過多的功能點,避免相同的職責分散到不同的類別中。如果一個類別的職責過多可能造成變化的原因也就越多,使程式碼更加的耦合。如果雜交不清的職責將使得程式碼難以維護,牽一發而動全身。
例如:工廠模式,工廠模式之所以被稱為工廠模式是因為它負責 「生產」 物件。
工廠模式的好處:多個地方new了一個對象,當此類名稱改變就不需要一個個去修改,只需要修改一個地方。
<?php class MyObject { public function __construct() { echo "test code"; } } //工厂类 class MyFactory { public static function factory() { return new MyObject(); } } $instance = MyFactory::factory();//test code
什麼是開放封閉原則?
開放式封閉原則: 一個類別是可擴充的,而不可修改的。也就是說,對擴展開放,對修改封閉。
1.對擴充開放,表示 有新的需求或變更時,可以對現有的程式碼擴展,以適應新的情況。
2.對修改封閉,在對模組功能進行擴充時,不應該影響已有的程式模組。
實現開放封閉的原則的重點:抽象編程,而不是具體編程,因為抽象相對穩定,讓類別依賴與固定的抽象類別和接口,所以修改就是封閉的。而物件導向的繼承和多態機制,又可以繼承抽象類別或實作接口,聽過重寫其方法來改變固有的行為,實現方法新的拓展,所以就是開放。
例如:裝飾器模式(Decorator),可以動態地加入修改類別的功能。一個類別提供了一個功能,如果要在修改並添加額外的功能,傳統的編程模式,需要寫一個子類別去繼承它,並重新實現類別的方法,使用裝飾器模式,僅需在運行時添加一個裝飾器物件即可實現,可以實現最大的靈活性。
<?php /** * 输出一个字符串 * 装饰器动态添加功能 * Class EchoText */ class EchoText { protected $decorator = []; public function Index() { //调用装饰器前置操作 $this->beforeEcho(); echo "你好,我是装饰器。"; //调用装饰器后置操作 $this->afterEcho(); } //增加装饰器 public function addDecorator(Decorator $decorator) { $this->decorator[] = $decorator; } //执行装饰器前置操作 先进先出原则 protected function beforeEcho() { foreach ($this->decorator as $decorator) $decorator->before(); } //执行装饰器后置操作 先进后出原则 protected function afterEcho() { $tmp = array_reverse($this->decorator); foreach ($tmp as $decorator) $decorator->after(); } } /** * 装饰器接口 * Class Decorator */ interface Decorator { public function before(); public function after(); } /** * 颜色装饰器实现 * Class ColorDecorator */ class ColorDecorator implements Decorator { protected $color; public function __construct($color) { $this->color = $color; } public function before() { echo "<dis style='color: {$this->color}'>"; } public function after() { echo "</div>"; } } /** * 字体大小装饰器实现 * Class SizeDecorator */ class SizeDecorator implements Decorator { protected $size; public function __construct($size) { $this->size = $size; } public function before() { echo "<dis style='font-size: {$this->size}px'>**"; } public function after() { echo "</div>"; } } //实例化输出类 $echo = new EchoText(); //增加装饰器 $echo->addDecorator(new ColorDecorator('red')); //增加装饰器 $echo->addDecorator(new SizeDecorator('22')); //输出 $echo->Index();
什麼是里氏替換原則?
由於物件導向程式設計技術中的繼承在具體程式設計中過於簡單,在許多系統的設計和程式實作中,我們並沒有認真地、理性地思考應用系統中各個類別之間的繼承好關係是否合適,派生類別是否能正確地對基底類別中的某些方法進行重寫的問題。因此經常出現濫用繼承或錯誤的繼承現象,為系統後期的維護帶來了不少麻煩。
核心思想:子類別必須能夠替換其父類別。這個想法體現為對繼承機制的約束規範,只有子類別能夠替換父類別時才能保證系統在運作期間內辨識子類,這是保證繼承重複使用的基礎。
<?php //例子1 class Bird{ protect function fly(){ } } //翠鸟 class KingFisher extends Bird{ } //鸵鸟 class Ostrich extends Bird{ //鸵鸟不会飞啊 } //例子2 class A{ protect function add($a, $b){ return $a + $b; } } //重载 class B extends A{ protected function add($a, $b){ return $a + $b + 100; } }
里氏替換原則是對類別繼承的一種限制。對里氏替換原則有兩種理解:
1.不能隨便去繼承不合適的,有多餘方法或屬性的類別(例子1)。
2.子類別可以擴充父類別的功能,但不能改變父類別原有的功能(範例2)。
里氏替換原則包含幾個隱藏意義:
1.子類別可以實作父類別的抽象方法,但不能覆寫父類別的非抽象方法。
2.子類別中可以增加自己特有的方法。
3.當子類別的方法重載父類別的方法時,方法的前置條件(即方法的形參)要比父類別方法的輸入參數更寬鬆。
4.當子類別的方法實作父類別的抽象方法時,方法的後置條件(即方法的傳回值)要比父類別更嚴格。
什麼是依賴倒置原則?
簡單的來說就是:一個類,不應該強制依賴另一個類,每個類別對於另一個類別都是可替換的。
具體概念:
1.上層模組不應該依賴下層模組,它們共同依賴一個抽象(父類不能依賴子類,它們都要依賴抽象類)。
2.抽象不能依赖于具体,具体应该依赖于抽象。
为什么要依赖接口?因为接口体现对问题的抽象,同时由于抽象一般是相对稳定的或者是相对变化不频繁的,而具体是易变的。因此,依赖抽象是实现代码扩展和运行期内绑定(多态)的基础:只要实现了该抽象类的子类,都可以被类的使用者使用。
<?php interface employee { public function working(); } class teacher implements employee//具体应该依赖与抽象 { public function working(){ echo 'teaching...'; } } class coder implements employee { public function working(){ echo 'coding...'; } } class workA//例子1 { public function work(){ $teacher = new teacher; $teacher->working(); } } class workB//例子2 { private $e; public function set(employee $e){ $this->e = $e; } public function work(){ $this->e->working(); } } $worka = new workA;//workA 依赖于 teacher 类 不符合依赖倒置原则 $worka->work(); $workb = new workB;//workB 不依赖与某个类 既可以注入 teacher 也可以 注入 coder $workb->set(new teacher()); $workb->work();
在workA(例子1)中,work方法依赖于teacher实现;在workB(例子2)中,work转而依赖抽象,这样可以把需要的对象通过参数传入。上述代码通过接口,实现了一定程度的解耦,但仍然是有限的。不仅是使用接口,使用工厂等也能实现一定程度的解耦和依赖倒置。
在workB中,teacher实例通过set方法传入,从而实现了工厂模式。由于这样的实现仍然是硬编码的,为了实现代码的进一步扩展,把这个依赖关系写在配置文件里,指明workB需要一个teacher对象,专门由一个程序配置是否正确(如所依赖的类文件是否存在)以及加载配置中所依赖的实现,这个检测程序,就称为IOC容器(这里不清楚IOC的小伙伴可以自行谷歌)。
什么是接口隔离原则?
其核心思想是:使用多个小的专门的接口,而不要使用一个大的总接口(只需要关心接口,不需要关心实现)。
接口隔离原则体现在:
1.接口应该是内聚的,应该避免 “胖” 接口。
2.不要强迫依赖不用的方法,这是一种接口污染。
3.表明客户端不应该被强迫实现一些不会使用的接口,应该把胖接口分组,用多个接口代替它,每个接口服务于一个子模块。简单地说,就是使用多个专门的接口比使用单个接口要好很多。
隔离的手段主要有以下两种:
1、委托分离,通过增加一个新的类型来委托客户的请求,隔离客户和接口的直接依赖,但是会增加系统的开销(设计模式中,如:代理模式,策略模式中都用到了委托的概念,好奇的小伙伴可以自行谷歌,这里就不贴代码了)。
2、多重继承分离,通过接口多继承来实现客户的需求,这种方式是较好的。
胖接口的实例说明
<?php interface Animal{ public function walk(); public function speak(); } //实现狗的一个接口 class Dog implements Animal{ public function walk(){ echo "dogs can walk"; } public function speak(){ echo "dogs can speak"; } } //ok,现在我们想创建一个鱼类,它会游泳,怎么办呢? //我们必须要修改接口,还会影响到dog类的实现,而fish也需要实现walk和speak方法,如下代码所示: interface Animal{ public function walk(); public function speak(); public function swim(); } //修改后的Gog类 class Dog implements Animal{ public function walk(){ echo "dogs can walk"; } public function speak(){ echo "dogs can speak"; } public function swim(){ } } //鱼类 class Fish implements Animal{ public function walk(){ } public function speak(){ } public function swim(){ echo "fish can swim"; } }
这时Animal接口类就呈现出了”胖“接口的特征了。所谓胖接口其实就是接口中定义了不是所有实现类都需要的方法,就像Animal接口类,有些动物是不会游泳的,有些动物是不会行走的,还有些动物是不会飞的。如果将这些方法都写在一个Animal接口类中,那么后期的扩展和维护简直就是一场灾难。
那么,怎么解决以上问题呢?
很简单,接口细化即可,将Animal接口类拆分成三个接口类,然后用多继承分离接口就ok了。
<?php interface animalCanSpeak{ public function speak(); } interface AnimalCanSwim{ public function swim(); } interface animalCanSpeak{ public function speak(); } //定义好这几个接口类之后,dog和fish的实现就容易多了 //狗类 class Dog implements animalCanSpeak,animalCanWalk{ public function walk(){ echo "dogs can walk"; } public function speak(){ echo "dogs can speak"; } } //鱼类 class Fish implements AnimalCanSwim{ public function swim(){ echo "fish can swim"; } }
接口隔离原则(Interface Segregation Principle, ISP)的概念:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。
在使用接口隔离原则时,我们需要注意控制接口的粒度,接口不能太小,如果太小会导致系统中接口泛滥,不利于维护;接口也不能太大,太大的接口将违背接口隔离原则,灵活性较差,使用起来很不方便。一般而言,接口中仅包含为某一类用户定制的方法即可,不应该强迫客户依赖于那些它们不用的方法。
实践出真理
以上是物件導向的三大特性和五大原則是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!