Maison  >  Article  >  développement back-end  >  Un article expliquant la signification des interfaces et comment utiliser les interfaces pour écrire du code PHP de haute qualité

Un article expliquant la signification des interfaces et comment utiliser les interfaces pour écrire du code PHP de haute qualité

藏色散人
藏色散人avant
2021-11-08 15:43:373690parcourir

Public cible

Cet article s'adresse aux développeurs qui ont une compréhension de base des concepts de POO (programmation orientée objet) et de l'héritage en PHP. Si vous savez comment utiliser l'héritage en PHP, alors cet article sera plus facile à comprendre.

Qu'est-ce qu'une interface ?

Fondamentalement, une interface décrit ce qu'une classe doit faire. Les interfaces sont utilisées pour garantir que toute classe qui implémente l'interface contient les méthodes publiques spécifiées par l'interface.

Les interfaces

peuvent

 :

être utilisées pour définir des méthodes publiques dans une classe.

Utilisé pour définir des constantes dans les classes.
  • Les interfaces
  • ne peuvent pas
  •  :

être instanciées individuellement.

Utilisé pour définir des méthodes privées ou protégées dans une classe.
  • Utilisé pour définir les attributs dans les classes.
  • L'interface est utilisée pour définir les méthodes publiques qui doivent être incluses dans une classe. Ce qu'il faut retenir, c'est que l'interface définit uniquement le nom de la méthode, les paramètres et la valeur de retour, mais n'inclut pas le corps de la méthode. En effet, les interfaces ne sont utilisées que pour définir la communication entre les objets, et non pour définir le comportement spécifique de la communication entre les classes. Pour donner un peu de contexte, cet exemple montre un exemple d'interface qui définit plusieurs méthodes publiques :
  • interface DownloadableReport
    {
        public function getName(): string;
    
        public function getHeaders(): array;
    
        public function getData(): array;
    }
  • Selon php.net, les interfaces ont deux objectifs principaux :

Permettre aux développeurs de créer des objets de différentes classes, lesquels objets peuvent être utilisés de manière interchangeable car ils implémentent la même interface. Un exemple courant est celui de plusieurs services d’accès à des bases de données, de plusieurs passerelles de paiement ou de différentes stratégies de mise en cache. Différentes implémentations peuvent être remplacées sans aucune modification du code qui les utilise.

Permet à une fonction ou à une méthode d'accepter des paramètres conformes à une interface et d'opérer sur eux, sans se soucier de ce que l'objet peut faire d'autre ou de la manière dont il est implémenté. Ces interfaces sont souvent nommées Iterable, Cacheable, Renderable, etc. pour décrire la signification du comportement. [Apprentissage recommandé : "
    Tutoriel vidéo PHP
  1. "]
  2. Utilisation des interfaces en PHP
  3. Les interfaces sont une partie très importante du code POO (Programmation Orientée Objet). Ils nous permettent de découpler notre code et d'améliorer l'évolutivité. A titre d'exemple, regardons la classe suivante :
class BlogReport
{
    public function getName(): string
    {
        return 'Blog report';
    }
}

Comme vous pouvez le voir, nous avons défini une classe avec une méthode qui renvoie une chaîne. En faisant cela, nous avons déterminé le comportement de la méthode, nous pouvons donc voir comment getName() construit la chaîne renvoyée. Cependant, disons que nous appelons cette méthode à partir du code d'une autre classe. L'autre classe ne se soucie pas de la manière dont la chaîne est construite, elle se soucie uniquement de savoir si elle est renvoyée. Par exemple, voyons comment appeler cette méthode dans une autre classe :

class ReportDownloadService
{
    public function downloadPDF(BlogReport $report)
    {
        $name = $report->getName();

        // 在这里下载文件...
    }
}

Bien que le code ci-dessus fonctionne déjà, imaginons ce que se passerait si nous voulions maintenant ajouter une méthode pour télécharger les rapports des utilisateurs. Bien sûr, nous ne pouvons pas utiliser les méthodes existantes dans ReportDownloadService car nous avons été obligés de passer dans une seule classe BlogReport. Par conséquent, nous devons renommer la méthode existante et en ajouter une nouvelle. Comme ceci :

class ReportDownloadService
{
    public function downloadBlogReportPDF(BlogReport $report)
    {
        $name = $report->getName();

        // 在这下载文件...
    }

    public function downloadUsersReportPDF(UsersReport $report)
    {
        $name = $report->getName();

        // 在这下载文件...
    }
}

Bien que vous ne puissiez pas le voir, nous supposons que le reste des méthodes de la classe ci-dessus utilisent le même code pour créer le téléchargement. Nous pouvons promouvoir le code public en méthodes, mais nous aurons toujours du code public. En plus de cela, nous avons plusieurs entrées dans cette classe avec un code presque identique. Cela peut entraîner un travail supplémentaire lorsque vous tenterez d'étendre le code ou d'ajouter des fonctionnalités de test à l'avenir. getName()是如何构建返回的字符串的。但是,假设我们在另一个类中的代码中调用此方法。另一个类并不会关心字符串是如何构建的,它只关心它是否被返回。例如,让我们看看如何在另一个类中调用此方法:

interface DownloadableReport
{
    public function getName(): string;

    public function getHeaders(): array;

    public function getData(): array;
}

尽管上面的代码已经可以用了,让我们设想一下,若是我们现在想要在 UserReport 类中增加一个下载用户报告的方法。当然,我们不能使用 ReportDownloadService 中已经存在的方法,因为我们已经强制只能传入一个 BlogReport 类。因此,我们必须重命名现有方法,再添加一个新的方法。像这样:

class BlogReport implements DownloadableReport
{
    public function getName(): string
    {
        return '博客报告';
    }

    public function getHeaders(): array
    {
        return ['头在这'];
    }

    public function getData(): array
    {
        return ['报告的数据在这里'];
    }
}

虽然你实际看不到,但我们假设上述的类中,其余的方法都使用相同的代码来构建下载。我们可以将公共的代码提升为方法,但我们仍然会有一些公共的代码。除此之外,我们还有多个进入这个类的几乎是相同代码的入口。这可能会在未来尝试扩展代码或添加测试功能时导致额外的工作量。

举个例子,我们创建一个新的 AnalyticsReport;我们现在需要为这个类增加一个新的 downloadAnalyticsReportPDF() 方法。这时你可能会观察到这个文件正在快速增长。这时就是使用接口的绝佳时机。

我们从创建一个接口开始。我们要创建一个叫做 DownloadableReport 的接口,并这样定义:

class UsersReport implements DownloadableReport
{
    public function getName()
    {
        return ['用户报告'];
    }

    public function getData(): string
    {
        return '报告的数据在这里';
    }
}

我们现在要更改 BlogReport 和 UsersReport 来实现 DownloadableReport 接口,如下所示。我故意写错了 UsersReport

Par exemple, nous créons un nouveau AnalyticsReport ; nous devons maintenant ajouter une nouvelle méthode downloadAnalyticsReportPDF() à cette classe. À ce stade, vous remarquerez peut-être que le fichier croît rapidement. C’est le moment idéal pour utiliser les interfaces.

Nous commençons par créer une interface. Nous allons créer une interface appelée DownloadableReport et la définir comme ceci : 🎜
class UsersReport implements DownloadableReport
{
    public function getName(): string
    {
        return '用户报告';
    }

    public function getHeaders(): array
    {
       return [];
    }

    public function getData(): array
    {
        return ['报告的数据在这里'];
    }
}
🎜Nous allons maintenant modifier BlogReport et UsersReport pour implémenter Interface DownloadableReport , comme indiqué ci-dessous. J'ai intentionnellement écrit le mauvais code pour UsersReport pour démontrer quelque chose. 🎜
class ReportDownloadService
{
    public function downloadReportPDF(DownloadableReport $report)
    {
        $name = $report->getName();

        // 在这下载文件
    }

}
rrreee🎜Si nous essayons d'exécuter ce code, nous obtiendrons une erreur car : 🎜
  1. 找不到 getHeaders() 方法。
  2. getName() 方法返回类型在接口中定义的方法返回值类型中。
  3. getData() 方法定义了返回类型,但和接口中定义的返回类型不同。

因此,要让 UsersReport 正确地实现 DownloadableReport 接口,我们需要作如下变动:

class UsersReport implements DownloadableReport
{
    public function getName(): string
    {
        return '用户报告';
    }

    public function getHeaders(): array
    {
       return [];
    }

    public function getData(): array
    {
        return ['报告的数据在这里'];
    }
}

现在我们两个报告类都实现了相同的接口,我们可以像这样更新我们的 ReportDownloadService

class ReportDownloadService
{
    public function downloadReportPDF(DownloadableReport $report)
    {
        $name = $report->getName();

        // 在这下载文件
    }

}

现在我们向 downloadReportPDF() 方法传入了 UsersReport 或 BlogReport 对象,没有任何错误出现。这是因为我们现在知道了报告类所需要的必要方法,并且报告类会按照我们预期的类型返回数据。

向方法传入接口,而不是向类传入接口,这样的结果使得 ReportDownloadService 和报告类产生松散耦合,这根据的是方法做什么,而不是如何做

如果我们想要创建一个新的 AnalyticsReport,我们需要让它实现相同的接口,然后它就会允许我们将报告类实例传入相同的 downloadReportPDF() 方法中,而不用添加其他新的方法。如果你正在构建自己的应用或框架,想要让其他开发人员有创建给他们自己的类的功能,这将非常有用。举个例子,在 Laravel 中,你可以通过实现 Illuminate\Contracts\Cache\Store 接口来创建自定义的缓存类。

除了使用接口来改进代码外,我更倾向于喜欢接口的「代码即文档」特性。举个例子,如果我想要知道一个类能做什么和不能做什么,我更喜欢在查看类之前先查看接口。它会告诉我所有可调用的方法,而不需要关心这些方法具体是怎么运行的。

对于像我这样的 Laravel 开发者来说,值得注意的一件事是,你会经常看到 接口 interface契约 contract 交替使用。根据 Laravel 文档,「Laravel 的契约是一组定义了框架提供的核心服务的接口」。因此,契约是一个接口,但接口不一定是契约。通常来说,契约只是框架提供的一个接口。有关契约的更多内容,我建议阅读一下 文档,因为我认为它在契约的理解和使用途径上有很好的见解。

结论

阅读本文后,我们希望你能简要地了解到了什么是接口,如何在 PHP 中使用接口,以及使用接口的好处。

对于我的 Laravel 开发者读者们,我下周会更新一篇新的博文,讲解如何在 Laravel 中使用接口实现桥接模式。如果你感兴趣,可以订阅我的网站更新通知,这样当我更新时,你就会收到通知。

如果这篇文章有助于你理解 PHP 接口,我期待在评论区听到你的声音。继续去创造令人惊叹的作品吧!

非常感谢  Aditya Kadam 、 Jae Toole 和 Hannah Tinkler 帮助我校对和改进这篇文章。

原文地址:https://dev.to/ashallendesign/using-interfaces-to-write-better-php-code-391f

译文地址:https://learnku.com/php/t/62228

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer