Maison >développement back-end >tutoriel php >Qu'est-ce qu'une interface ? Comment écrire du code élégant en utilisant des interfaces en PHP ?
Qu'est-ce qu'une interface ? Comment utiliser les interfaces en PHP ? Cet article parlera de l'utilisation d'interfaces pour écrire du code PHP plus élégant. J'espère qu'il vous sera utile !
En programmation, il est important de s'assurer que le code est lisible, maintenable, extensible et facile à tester ; et l'utilisation d'interfaces est exactement l'une des façons dont nous pouvons améliorer tous ces facteurs dans le code.
Le public cible de cet article est constitué de développeurs qui ont une compréhension de base des concepts de POO (programmation orientée objet) et qui utilisent l'héritage en PHP. Si vous savez utiliser l’héritage dans le code PHP, vous devez bien comprendre cet article.
En bref, les interfaces sont simplement des descriptions de ce qu'une classe doit faire, et elles peuvent être utilisées pour garantir que toute classe qui implémente l'interface inclura toutes les méthodes publiques définies en son sein.
Interface peut :
L'interface ne peut pas :
L'interface est utilisée pour définir les méthodes publiques qu'une classe doit inclure. N'oubliez pas qu'il vous suffit de définir la signature de la méthode dans l'interface et vous n'avez pas besoin d'inclure le corps de la méthode (comme vous voyez habituellement les méthodes dans les classes). **En effet, les interfaces ne sont utilisées que pour définir la communication entre les objets, et non pour définir la communication et le comportement comme dans une classe. **Pour illustrer ce problème, ce qui suit 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 la documentation php.net, on peut savoir que les interfaces ont deux objectifs principaux :
Permettre aux développeurs de créer Différentes classes d'objets qui peuvent être utilisées de manière interchangeable car elles implémentent la ou les mêmes interfaces. Les exemples courants incluent : plusieurs services d’accès aux bases de données, plusieurs passerelles de paiement, différentes stratégies de mise en cache, etc. Différentes implémentations peuvent être interchangées sans nécessiter 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 généralement nommées Iterable
, Cacheable
, Renderable
, etc. pour illustrer ce que signifient réellement ces interfaces. Iterable
、Cacheable
、Renderable
等,来说明这些接口的实际含义。
接口是 OOP(面向对象编程)代码库的重要部分。接口能让我们降低代码耦合并提高可扩展性。举个例子,让我们看看下面这个类:
class BlogReport { public function getName(): string { return 'Blog report'; } }
如你所见,我们定义了一个类,类中有一个函数,返回一个字符串。这样一来,我们定义了该方法的行为,所以我们知道 getName()
是如何返回字符串的。不过,假设我们在另一个类调用这个方法;这个类不需要关心这个字符串如何构建的,它只关心该方法是否返回内容。举例来说,让我们看看如何在另一个类调用此方法:
class ReportDownloadService { public function downloadPDF(BlogReport $report) { $name = $report->getName(); // 下载文件…… } }
尽管上面的代码正常运行,但我们设想一下,现在想给 UsersReport
类中增加下载用户报告的功能。显然,我们不能使用 ReportDownloadService
中的现有方法,因为我们已经强制规定方法只能传递 BlogReport
类。因此,我们必须修改把原有的下载方法名称改掉(避免重名),然后另外再添加一个类似的方法,如下所示:
class ReportDownloadService { public function downloadBlogReportPDF(BlogReport $report) { $name = $report->getName(); // 下载文件…… } public function downloadUsersReportPDF(UsersReport $report) { $name = $report->getName(); // 下载文件…… } }
假设上面的方法中的下载文件部分(注释掉的部分)使用了相同的代码,而且我们可以将这些相同的代码单独写成一个方法,但我们仍会有一些重复的代码(译者注:指的是每个方法中都会有 $name = $report->getName();
)以及有多个几乎相同的类的入口。这可能会给将来扩展代码或测试带来额外的工作量。
例如,假设我们创建了一个新的 AnalyticsReport
;我们现在需要向该类添加一个新的 downloadAnalyticsReportPDF()
方法。你可以清晰的看到这个文件将如何膨胀(译者注:指每增加一个类型,就要增加一个下载方法)。这就是一个使用接口的完美场景!
让我们从创建第一个接口开始:让我们将其命名为 DownloadableReport
,定义如下:
interface DownloadableReport { public function getName(): string; public function getHeaders(): array; public function getData(): array; }
我们现在可以更新 BlogReport
和 UsersReport
来实现 DownloadableReport
接口,如下例所示。但是请注意,作为演示用途,我故意把 UsersReport
class BlogReport implements DownloadableReport { public function getName(): string { return 'Blog report'; } public function getHeaders(): array { return ['The headers go here']; } public function getData(): array { return ['The data for the report is here.']; } }🎜Comme vous pouvez le voir, nous avons défini une classe avec une fonction qui renvoie une chaîne. De cette façon, nous définissons le comportement de la méthode, nous savons donc comment
getName()
renvoie une chaîne. Cependant, supposons que nous appelions cette méthode dans une autre classe ; cette classe n'a pas besoin de se soucier de la manière dont la chaîne est construite, elle se soucie uniquement de savoir si la méthode renvoie du contenu. Par exemple, voyons comment appeler cette méthode dans une autre classe : 🎜class UsersReport implements DownloadableReport { public function getName() { return ['Users Report']; } public function getData(): string { return 'The data for the report is here.'; } }🎜 Bien que le code ci-dessus fonctionne correctement, imaginons que nous souhaitions maintenant ajouter la possibilité de télécharger des rapports utilisateur dans la classe
UsersReport
. . Évidemment, nous ne pouvons pas utiliser les méthodes existantes dans ReportDownloadService
car nous avons imposé que la méthode ne puisse être transmise qu'à la classe BlogReport
. Par conséquent, nous devons modifier le nom de la méthode de téléchargement d'origine (pour éviter les noms en double), puis ajouter une méthode similaire, comme indiqué ci-dessous : 🎜class UsersReport implements DownloadableReport { public function getName(): string { return 'Users Report'; } public function getHeaders(): array { return []; } public function getData(): array { return ['The data for the report is here.']; } }🎜 Supposons que la partie fichier de téléchargement de la méthode ci-dessus (la partie commentée) utilise le même code, et nous pouvons écrire ces mêmes codes dans une méthode distincte, mais nous aurons toujours du code répété (Note du traducteur : fait référence à
$name = $ dans chaque méthode report->getName(); code>) et il existe plusieurs entrées de classes presque identiques. Cela peut créer du travail supplémentaire pour étendre le code ou tester à l'avenir. 🎜🎜Par exemple, supposons que nous créons un nouveau <code>AnalyticsReport
; nous devons maintenant ajouter une nouvelle méthode downloadAnalyticsReportPDF()
à cette classe. Vous pouvez clairement voir comment ce fichier va se développer. C’est un scénario parfait pour utiliser les interfaces ! 🎜🎜Commençons par créer notre première interface : nommons-la DownloadableReport
et définissons-la comme ceci : 🎜class ReportDownloadService { public function downloadReportPDF(DownloadableReport $report) { $name = $report->getName(); // 下载文件…… } }🎜Nous pouvons maintenant mettre à jour
BlogReport
et UsersReport
pour implémenter l'interface DownloadableReport
, comme indiqué dans l'exemple suivant. Mais veuillez noter qu'à des fins de démonstration, j'ai intentionnellement écrit le code dans UsersReport
de manière incorrecte : 🎜rrreeerrreee🎜 Mais lorsque nous essayons d'exécuter le code, nous recevrons une erreur pour la raison suivante : 🎜缺少 getHeaders()
方法.
getName()
方法不包括接口的方法签名中定义的返回类型。
getData()
方法定义了一个返回类型,但它与接口的方法签名中定义的类型不同。
因此,为了修复 UsersReport
使其正确实现 DownloadableReport
接口,我们可以将其修改为:
class UsersReport implements DownloadableReport { public function getName(): string { return 'Users Report'; } public function getHeaders(): array { return []; } public function getData(): array { return ['The data for the report is here.']; } }
现在两个报告类都实现了相同的接口,我们可以这样更新我们的 ReportDownloadService
:
class ReportDownloadService { public function downloadReportPDF(DownloadableReport $report) { $name = $report->getName(); // 下载文件…… } }
我们现在可以把 UsersReport
或 BlogReport
对象传入 downloadReportPDF
方法中,而且不会出现任何错误。这是因为我们知道该对象实现了报告类的必要方法,并且将返回我们期望的数据类型。
通过向方法传递了一个接口,而不是一个具体的类,我们可以根据方法的实际作用(而不是方法的实现原理)来解耦 ReportDownloadService
类和这些报告类。
如果我们想创建一个新的 AnalyticsReport
,我们可以让它实现相同的接口。这样一来,我们不必添加任何新的方法,只需要将报告对象传递给同一个的 downloadReportPDF()
方法。如果你正在构建你自己的包或框架,接口可能对你特别有用。你只需要告诉使用者要实现哪个接口,然后他们就可以创建自己的类。例如,在 Laravel 中,我们可以通过实现 Illuminate\Contracts\Cache\Store
接口来创建自己的自定义缓存驱动类。
除了能改进代码之外,我喜欢使用接口的另一个原因是 —— 它们起到了“代码即文档”的作用。例如,如果我想弄清楚一个类能做什么,不能做什么,我倾向于先看接口,然后再看实现它的类。接口能够告诉我们所有可被调用的方法,而不需要我们过多地关心这些方法的底层实现方式是怎样的。
值得注意的是,Laravel
中的“契约(contract)”和“接口(interface)”这两个词语是可互换的。根据 Laravel 文档,“契约是一组由框架提供的核心服务的接口”。所以,记住:契约是一个接口,但接口不一定是契约。通常情况下,契约只是框架提供的一个接口。关于使用契约的更多信息,我建议大家可以阅读这一篇文档。它很好地剖析了契约究竟是什么,也对使用契约的方式与场景做了一定的叙述。
希望通过阅读这篇文章,你能对什么是接口、如何在 PHP 中使用接口以及使用接口的好处有一个简单的了解。
原文地址:https://dev.to/ashallendesign/using-interfaces-to-write-better-php-code-391f
原文作者:Ash Allen
译者:kamly、jaredliw
推荐学习:《PHP视频教程》
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!