亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

使用特征為接口定義函數的目的是什么

使用特征為接口定義函數的目的是什么

PHP
米脂 2022-09-03 16:16:38
抱歉,如果這是一個重復的問題或一個共同的設計原則,我已經搜索了一下,但無法找到這個問題的任何答案。我可能只是用錯誤的關鍵字搜索。我一直在看一個流行的庫Sabre/Event(https://sabre.io/event/),在代碼中有一個簡單的類/繼承模型,我試圖理解:類 EventEmitter 實現 EventEmitterInterface 并使用 EventEmitterTrait(請參閱下面的代碼)。EventEmitterTrait 在類上方有一條評論說:* Using the trait + interface allows you to add EventEmitter capabilities* without having to change your base-class.我試圖理解為什么這個評論是這么說的,以及為什么它允許在不改變基類的情況下添加功能,以及這與將例程放入EventEmitter本身有何不同。難道您不能擴展 EventEmitter 并在派生類中添加功能嗎?簡化代碼:// EventEmitter.phpclass EventEmitter implements EventEmitterInterface {    use EventEmitterTrait;}// EventEmitterInterface.phpinterface EventEmitterInterface {    // ... declares several function prototypes}// EventEmitterTrait.phptrait EventEmitterTrait {    // ... implements the routines declared in EventEmitterInterface}
查看完整描述

4 回答

?
Qyouu

TA貢獻1786條經驗 獲得超11個贊

你基本上在這里問了兩個問題。

  1. 什么是接口,為什么它們有用?

  2. 什么是特征,為什么它們有用?

要理解為什么接口有用,你必須對繼承和OOP有所了解。如果你以前聽說過意大利面條代碼這個術語(當你傾向于編寫如此糾纏在一起的命令式代碼時,你很難理解它),那么你應該把它比作術語“烤寬面條代碼”(當你將一個類擴展到如此多的層時,很難理解哪個層在做什么)。

1. 接口

接口通過允許一個類實現一組通用的方法而不必限制該類的層次結構來消除一些混淆。我們不從基類派生接口。我們只是將它們實現到一個給定的類中。

PHP中一個非常明顯的例子是DateTimeInterface。它提供了一組通用的方法,這些方法既可以實現,也可以實現。但是,它不會告訴這些類實現是什么。類是一種實現。接口只是類 sans 實現的方法。但是,由于這兩個東西都實現了相同的接口,因此很容易測試實現該接口的任何類,因為您知道它們將始終具有相同的方法。所以我知道兩者都將實現該方法,它將接受a作為輸入并返回a,無論哪個類正在實現它。我甚至可以編寫我自己的實現,并保證該方法具有相同的簽名。DateTimeDateTimeImmutableDateTimeDateTimeImmutableformatStringStringDateTimeDateTimeInterface

所以想象一下,我寫了一個接受一個對象的方法,并且該方法期望在該對象上運行該方法。如果它不關心具體地給了它哪個類,那么該方法可以簡單地將其原型鍵入為?,F在,任何人都可以自由地在自己的類中實現,而不必從某個基類擴展,并為我的方法提供一個保證以相同方式工作的對象。DateTimeformatDateTimeInterfaceDateTimeInterface


因此,相對于您的示例,您可以將類的相同功能(如)添加到任何甚至可能無法從 擴展的類中,但只要我們知道它實現了相同的接口,我們就確信它具有具有相同簽名的相同方法。對于 ..c,這意味著同樣的事情。EventEmitterDateTimeDateTimeEventEmitter

2. 性狀

與接口不同,Traits實際上可以提供實現。它們也是水平繼承的一種形式,與擴展類的垂直繼承不同。因為兩個完全不同的類不是從同一個基類派生的,可以使用相同的。這是可能的,因為在PHP中,特征基本上只是編譯器輔助的復制和粘貼。想象一下,您從字面上復制了一個特征內部的代碼,并在編譯時之前將其粘貼到使用它的每個類中。你會得到同樣的結果。您只是將代碼注入不相關的類中。Trait

這很有用,因為有時您有一個方法或一組方法,這些方法或方法在兩個不同的類中被證明是可重用的,即使這些類的其余部分沒有其他共同點。

例如,假設您正在編寫一個CMS,其中有一個類和一個類。這兩個類都沒有以任何有意義的方式相關。他們做非常不同的事情,其中一個擴展另一個是沒有意義的。但是,它們都共享一個共同的特定行為:flag() 方法,該方法指示對象已被用戶標記以違反服務條款。DocumentUser

trait FlagContent {    public function flag(Int $userId, String $reason): bool {        $this->flagged = true;        $this->byUserId = $userId;        $this->flagReason = $reason;        return $this->updateDatabase();
    }
}

現在考慮一下,也許您的CMS還有其他內容可能會被標記,例如類,類,甚至類。這些類通常都是不相關的。僅僅使用一個特定的類來標記內容可能沒有多大意義,例如,如果必須將相關對象的屬性傳遞給該類以更新數據庫。它們從基類派生也沒有意義(它們彼此完全無關)。在每個類中重寫相同的代碼也沒有意義,因為在一個地方而不是許多地方更改它更容易。ImageVideoComment

因此,這里似乎最明智的做法是使用 .Trait


因此,與你的示例相比,它們為您提供了一些可以在實現類中重用的特征,基本上可以更輕松地重用代碼,而無需從基類(水平繼承)擴展。EventEmitter


查看完整回答
反對 回復 2022-09-03
?
湖上湖

TA貢獻2003條經驗 獲得超2個贊

根據 Sabre 的 Event Emitter 關于“集成到其他對象”的文檔:

要將發射器功能添加到任何類,只需對其進行擴展即可。

如果無法擴展,因為該類已經是現有類層次結構的一部分,則可以使用提供的 trait。

因此,在這種情況下,我們的想法是,如果您使用自己的對象,這些對象已經是類層次結構的一部分,則可以簡單地實現接口并使用該特征,而不是擴展發射器類(您將無法擴展)。


查看完整回答
反對 回復 2022-09-03
?
慕的地6264312

TA貢獻1817條經驗 獲得超6個贊

這是一個有趣的問題,我將嘗試給出我的看法。正如你所問的,


使用特征為接口定義函數的目的是什么?

Traits基本上使您能夠創建一些可重用的代碼或功能,然后可以在代碼庫中的任何地方使用?,F在,PHP不支持多重繼承,因此特征和接口可以解決這個問題。這里的問題是為什么特征雖然??想象一下下面的場景,


class User

{

  public function hasRatings()

  {

    // some how we want users to have ratings

  }


  public function hasBeenFavorited()

  {

    // other users can follow

  }


  public function name(){}

  public function friends(){}


  // and a few other methods

}

現在假設我們有一個 post 類,它與 user 具有相同的邏輯,可以通過 have 和 方法實現?,F在,一種方法是簡單地從用戶類繼承。hasRatings()hasBeenFavorited()


class Post extends User

{

  // Now we have access to the mentioned methods but we have inherited

  // methods and properties which is not really needed here

}

因此,要解決此問題,我們可以使用特征。


trait UserActions 

   {

     public function hasRatings()

      {

        // some how we want users to have ratings

      }


      public function hasBeenFavorited()

      {

        // other users can follow

      }


   }

有了這種邏輯,我們現在就可以在代碼中需要它的任何地方使用它。


class User

{

  use UserActions;

}


class Post

{

  use UserActions;

}

現在假設我們有一個報告類,我們希望根據用戶操作生成某些報告。


class Report 

{

   protected $user;


   public function __construct(User $user)

   {

     $this->user = $user

   }


   public function generate()

   {

     return $this->user->hasRatings();

   }

}

現在,如果我想為生成報告,會發生什么。實現這一目標的唯一方法是新建另一個報告類,即可能..你能看出我在哪里嗎?當然,可能還有另一種方式,我不必重復自己。這就是接口或合同到位的地方。牢記這一點,讓我們重新定義我們的報告類,并使其接受契約而不是具體類,這將始終確保我們有權訪問。PostPostReportUserActions


interface UserActionable

{

   public function hasRatings();


   public function hasBeenFavorited();

}




 class Report 

    {

       protected $actionable;


       public function __construct(UserActionable $actionable)

       {

         $this->actionable = $actionable;

       }


       public function generate()

       {

         return $this->actionable->hasRatings();

       }

    }


//lets make our post and user implement the contract so we can pass them

// to report


class User implements UserActionable

{

  uses UserActions;

}


class Post implements UserActionable

{

  uses UserActions;

}


// Great now we can switch between user and post during run time to generate 

// reports without changing the code base


$userReport = (new Report(new User))->generate();

$postReport = (new Report(new Post))->generate();

簡而言之,接口和特性有助于我們實現基于SOLID原則的設計,許多解耦的代碼和更好的組合。希望有所幫助


查看完整回答
反對 回復 2022-09-03
?
慕田峪9158850

TA貢獻1794條經驗 獲得超8個贊

集成到其他對象文檔說:

如果無法擴展,因為該類已經是現有類層次結構的一部分,則可以使用提供的 trait”。

我知道,當您已經擁有不想更改的OOP設計并且想要添加事件功能時,這是一種解決方法。例如:

Model -> AppModel -> Customer

PHP沒有多重繼承,所以可以擴展,但不能同時擴展。如果你在代碼中實現接口是不能在其他地方重用的;如果您實現例如 它隨處可見,這可能是不可取的。CustomerAppModelEmitterCustomerAppModel

使用特征,您可以編寫自定義事件代碼,并挑選重用它的位置。


查看完整回答
反對 回復 2022-09-03
  • 4 回答
  • 0 關注
  • 137 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號