From Apprentice To Artisan 筆記(一)

CrazyZard發表於2020-07-19

關注分離
每個類應該有獨立的職責,並且該職責完全被這個類封裝。好處就是 Web 控制器和資料訪問解耦。這會使得實現儲存遷移更容易,測試也會更容易。“Web” 就僅僅是為你真正的應用做資料的傳輸了。

如何做
定義一個介面,並實現該介面

//1.定義介面
interface BillerInterface  {
   public function bill()
}
interface BillingNotifierInterface  {
    public function notify()
}
//2.實現介面
class BService implements A {
   public function notice(){
      ...
   }
}
//依賴注入
class AController implements BillerInterface
{
     public function _contrust(BillingNotifierInterface $notifier)
     {
        $this->service = $notifier;
     }

     public function bill()
     {
         $this->service->notify();
     }
   }
}

類只要遵守了約定,就不用再考慮如何實現提示功能。只要是遵守約定(介面)的類, 類都能用。這不僅僅是方便了我們的開發,而且我們還可以通過模擬 Interface 來進行無痛測試。

寫介面可能看上去挺麻煩,但實際上能加速你的開發。你不用實現任何介面,就能使用模擬庫來模擬你的介面,進而測試整個後臺邏輯!

對於小而簡單的應用來說,介面通常是不必要的。在你確信不會改變的地方就沒有必要使用介面了。

在大型應用中介面是很有幫助的。和提升的程式碼靈活性、可測試性比起來,多敲鍵盤費的功夫就微不足道了。

如果你不喜歡寫介面,那就先簡單的寫程式碼吧。日後再精進即可。

IoC 容器可以使你更容易管理依賴注入,Laravel 框架擁有一個很強大的IoC容器。Laravel 的核心就是這個 IoC 容器,這個 IoC 容器使得框架各個元件能很好的在一起工作。事實上 Laravel 的 Application 類就是繼承自 Container 類!

如何在應用中管理、注入這些物件?

  1. 可以將 Stripe 的支付實現繫結到容器裡
    App::bind('BillerInterface', function()
    {
     return new StripeBiller(App::make('BillingNotifierInterface'));
    });
  2. 處理 BillingInterface 時,我們額外需要一個 BillingNotifierInterface 的實現,也就是再來一個 bind:
    App::bind('BillingNotifierInterface', function()
    {
      return new EmailBillingNotifier;
    });
    現在不管在應用的哪裡需要一個提示器,我們總會得到SmsBillingNotifier的物件。
    可以通過改變一行代表就可以改變返回的物件(簡訊運營商)

在應用中只例項化某類一次?使用 singleton 方法
容器的 instance 方法和 singleton 方法很類似,區別是 instance 可以繫結一個已經存在的物件。然後容器每次返回的都是這個物件了。

用反射來自動處理依賴是 Laravel 容器的一個最強大的特性。反射是一種執行時探測類和方法的能力。比如,PHP 的 ReflectionClass 可以探測一個類的方法。method_exists 某種意義上說也是一種反射。

class UserController extends BaseController
{
    public function __construct(StripBiller $biller)
    {
        $this->biller = $biller;
    }
}

注意這個控制器的建構函式暗示著有一個 StripBiller 型別的引數。使用反射就可以檢測到這種型別暗示。當 Laravel 的容器無法解決一個型別的明顯繫結時,容器會試著使用反射來解決。程式流程類似於這樣的:

  1. 已經有一個 StripBiller 的繫結了麼?

  2. 沒繫結?那用反射來探測一下 StripBiller 吧。看看他都需要什麼依賴。

  3. 解決 StripBiller 需要的所有依賴(遞迴處理)

  4. 使用 ReflectionClass->newInstanceArgs() 來例項化 StripBiller

假設我們沒有為 BillerInterface 做任何繫結, 容器該怎麼知道要注入什麼類呢?要知道 interface 不能被例項化,因為它只是個約定。如果我們不提供更多資訊的話,容器是無法例項化這個依賴的。我們需要明確指出哪個類要實現這個介面,這就需要用到 bind 方法:

App::bind('BillerInterface','StripBiller');

這裡我們只傳了一個字串進去,而不是一個匿名函式。 這個字串告訴容器總是使用 StripBiller 來作為 BillerInterface 的實現類。 此外我們也獲得了只改一行程式碼即可輕鬆改變實現的能力。比如,假設我們需要切換到 Balanced Payments 作為我們的支付提供商,我們只需要新寫一個 BalancedBiller 來實現 BillerInterface 介面,然後這樣修改容器程式碼:

App::bind('BillerInterface', 'BalancedBiller');

可以使用 singleton 方法來實現單例模式

App::singleton('BillerInterface', 'StripBiller');

擴充套件 單例模式
在PHP中,所有的變數無論是全域性變數還是類的靜態成員,都是頁面級的,每次頁面被執行時,都會重新建立新的物件,都會在頁面執行完畢後被清空,這樣似乎PHP單例模式就沒有什麼意義了,所以PHP單例模式我覺得只是針對單次頁面級請求時出現多個應用場景並需要共享同一物件資源時是非常有意義的。
一個應用中會存在大量的資料庫操作,比如過資料庫控制程式碼來連線資料庫這一行為,使用單例模式可以避免大量的new操作,因為每一次new操作都會消耗記憶體資源和系統資源。 如果系統中需要有一個類來全域性控制某些配置資訊,那麼使用單例模式可以很方便的實現.
隨便加上 _clone 深淺複製支援

PHP 是一種鴨子型別的語言。所謂鴨子型別的語言,一個物件可用的方法取決於使用方式,而非這個方法從哪兒繼承或實現。 換言之在程式裡,一個物件看上去是個 User ,方法響應也像個 User ,那他就是個 User 。

介面就是約定。介面不包含任何程式碼實現,只是定義了一個物件應該實現的一系列方法。如果一個物件實現了一個介面,那麼我們就能確信這個介面所定義的一系列方法都能在這個物件上使用。因為有約定保證了特定方法的實現標準,通過多型也能使型別安全的語言變得更靈活。

多型含義很廣,其本質上是說一個實體擁有多種形式。在本書中,我們講多型是一個介面有著多種實現。比如 UserRepositoryInterface 可以有 MySQL 和 Redis 兩種實現,每一種實現都是 UserRepositoryInterface 的一個例項。

php 面對物件三大特性: 封裝、繼承、多型

  • 抽象
    抽象類是一個程式設計概念,Abstract Classes。在設計模式中,抽象類不能夠被例項化/初始化,但是可以依靠具體類的繼承(extends )來實現。
  • 介面
    PHP介面類interface就是一個類的領導者,指明方向,子類必須完成它指定方法。
  • 介面 vs 抽象
    • 介面使用implements,抽象用extends
    • 介面沒有成員變數,抽象有,可實現資料的封裝
    • 介面沒有建構函式,抽象有
    • 介面都是public,抽象類有private、protected、public
    • 一個類可同時實現多個介面,但值能實現一個抽象類
  • 封裝
  • 繼承
    abstract class Animal {}
    class Whale extends Animal {}
  • 多型
    多型是指在物件導向中能夠根據使用類的上下文來重新定義或改變類的性質和行為。
    唯獨這個多型,php體現的十分模糊。原因是php是弱型別語言
    上次面試的時候把我問懵了

介面就像是大綱,在開發程式的“骨架”時非常有用。在設計元件時,使用介面進行設計和討論都是對你的團隊有益處的。比如定義一個 BillingNotifierInterface 然後討論他有什麼方法。在寫任何實現程式碼前先用介面討論好一套好的 API!

本作品採用《CC 協議》,轉載必須註明作者和本文連結

快樂就是解決一個又一個的問題!

相關文章