Was ist eine Schnittstelle? Wie schreibe ich eleganten Code mithilfe von Schnittstellen in PHP?

青灯夜游
Freigeben: 2023-04-11 07:00:01
nach vorne
3295 Leute haben es durchsucht

Was ist eine Schnittstelle? Wie verwende ich Schnittstellen in PHP? In diesem Artikel geht es um die Verwendung von Schnittstellen zum Schreiben eleganterer PHP-Codes. Ich hoffe, er wird Ihnen hilfreich sein!

Was ist eine Schnittstelle? Wie schreibe ich eleganten Code mithilfe von Schnittstellen in PHP?

Beim Programmieren ist es wichtig sicherzustellen, dass der Code lesbar, wartbar, erweiterbar und einfach zu testen ist, und die Verwendung von Schnittstellen ist genau eine der Möglichkeiten, wie wir all diese Faktoren im Code verbessern können.

Zielgruppe

Die Zielgruppe dieses Artikels sind Entwickler, die über ein grundlegendes Verständnis von OOP-Konzepten (Object Oriented Programming) verfügen und Vererbung in PHP verwenden. Wenn Sie wissen, wie man Vererbung in PHP-Code verwendet, sollten Sie diesen Artikel gut verstehen.

Was ist eine Schnittstelle?

Kurz gesagt sind Schnittstellen lediglich Beschreibungen dessen, was eine Klasse tun soll, und sie können verwendet werden, um sicherzustellen, dass jede Klasse, die die Schnittstelle implementiert, jede darin definierte öffentliche Methode enthält.

Interface kann :

  • zum Definieren öffentlicher Methoden einer Klasse verwendet werden;
  • zum Definieren von Konstanten einer Klasse verwendet werden.

Schnittstelle :

    kann nicht zum Definieren privater oder geschützter Methoden einer Klasse verwendet werden;
  • kann zum Definieren von Eigenschaften einer Klasse verwendet werden.
  • Interface wird verwendet, um die öffentlichen Methoden zu definieren, die eine Klasse enthalten soll. Denken Sie daran, dass Sie nur die Signatur der Methode in der Schnittstelle definieren müssen und nicht den Hauptteil der Methode einschließen müssen (wie Sie es normalerweise bei Methoden in Klassen sehen). **Dies liegt daran, dass Schnittstellen nur zur Definition der Kommunikation zwischen Objekten verwendet werden und nicht zur Definition der Kommunikation und des Verhaltens wie in einer Klasse. **Um dieses Problem zu veranschaulichen, zeigt das Folgende eine Beispielschnittstelle, die mehrere öffentliche Methoden definiert:
interface DownloadableReport
{
    public function getName(): string;

    public function getHeaders(): array;

    public function getData(): array;
}
Nach dem Login kopieren
Nach dem Login kopieren
Laut der

php.net

-Dokumentation können wir wissen, dass Schnittstellen zwei Hauptzwecke haben:

Entwicklern das Erstellen ermöglichen Verschiedene Objektklassen, die austauschbar verwendet werden können, da sie dieselbe(n) Schnittstelle(n) implementieren. Zu den gängigen Beispielen gehören: mehrere Datenbankzugriffsdienste, mehrere Zahlungsgateways, unterschiedliche Caching-Strategien usw. Verschiedene Implementierungen können ausgetauscht werden, ohne dass Änderungen am Code erforderlich sind, der sie verwendet.
  • Ermöglicht einer Funktion oder Methode, Parameter zu akzeptieren, die einer Schnittstelle entsprechen, und diese zu bearbeiten, ohne sich darum zu kümmern, was das Objekt sonst noch tun kann oder wie es implementiert ist. Diese Schnittstellen werden normalerweise Iterable, Cacheable, Renderable usw. genannt, um zu veranschaulichen, was diese Schnittstellen tatsächlich bedeuten.
  • IterableCacheableRenderable 等,来说明这些接口的实际含义。

在 PHP 中使用接口

接口是 OOP(面向对象编程)代码库的重要部分。接口能让我们降低代码耦合并提高可扩展性。举个例子,让我们看看下面这个类:

class BlogReport
{
    public function getName(): string
    {
        return 'Blog report';
    }
}
Nach dem Login kopieren

如你所见,我们定义了一个类,类中有一个函数,返回一个字符串。这样一来,我们定义了该方法的行为,所以我们知道 getName() 是如何返回字符串的。不过,假设我们在另一个类调用这个方法;这个类不需要关心这个字符串如何构建的,它只关心该方法是否返回内容。举例来说,让我们看看如何在另一个类调用此方法:

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

        // 下载文件……
    }
}
Nach dem Login kopieren

尽管上面的代码正常运行,但我们设想一下,现在想给 UsersReport 类中增加下载用户报告的功能。显然,我们不能使用 ReportDownloadService 中的现有方法,因为我们已经强制规定方法只能传递 BlogReport 类。因此,我们必须修改把原有的下载方法名称改掉(避免重名),然后另外再添加一个类似的方法,如下所示:

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

        // 下载文件……
    }

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

        // 下载文件……
    }
}
Nach dem Login kopieren

假设上面的方法中的下载文件部分(注释掉的部分)使用了相同的代码,而且我们可以将这些相同的代码单独写成一个方法,但我们仍会有一些重复的代码(译者注:指的是每个方法中都会有 $name = $report->getName();)以及有多个几乎相同的类的入口。这可能会给将来扩展代码或测试带来额外的工作量。

例如,假设我们创建了一个新的 AnalyticsReport;我们现在需要向该类添加一个新的 downloadAnalyticsReportPDF() 方法。你可以清晰的看到这个文件将如何膨胀(译者注:指每增加一个类型,就要增加一个下载方法)。这就是一个使用接口的完美场景!

让我们从创建第一个接口开始:让我们将其命名为 DownloadableReport,定义如下:

interface DownloadableReport
{
    public function getName(): string;

    public function getHeaders(): array;

    public function getData(): array;
}
Nach dem Login kopieren
Nach dem Login kopieren

我们现在可以更新 BlogReportUsersReport 来实现 DownloadableReport 接口,如下例所示。但是请注意,作为演示用途,我故意把 UsersReportSchnittstellen in PHP verwenden

Schnittstellen sind ein wichtiger Teil der OOP-Codebasis (Object Oriented Programming). Mithilfe von Schnittstellen können wir die Codekopplung reduzieren und die Skalierbarkeit erhöhen. Schauen wir uns als Beispiel die folgende Klasse an: 🎜
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.'];
    }
}
Nach dem Login kopieren
🎜Wie Sie sehen, haben wir eine Klasse mit einer Funktion definiert, die einen String zurückgibt. Auf diese Weise definieren wir das Verhalten der Methode, sodass wir wissen, wie getName() einen String zurückgibt. Angenommen, wir rufen diese Methode in einer anderen Klasse auf. Diese Klasse muss sich nicht darum kümmern, wie die Zeichenfolge aufgebaut ist, sondern nur darum, ob die Methode Inhalt zurückgibt. Sehen wir uns zum Beispiel an, wie diese Methode in einer anderen Klasse aufgerufen wird: 🎜
class UsersReport implements DownloadableReport
{
    public function getName()
    {
        return ['Users Report'];
    }

    public function getData(): string
    {
        return 'The data for the report is here.';
    }
}
Nach dem Login kopieren
🎜 Obwohl der obige Code gut funktioniert, stellen wir uns vor, dass wir jetzt die Möglichkeit zum Herunterladen von Benutzerberichten zur Klassenfunktion UsersReport hinzufügen möchten . Offensichtlich können wir die vorhandenen Methoden in ReportDownloadService nicht verwenden, da wir erzwungen haben, dass die Methode nur an die Klasse BlogReport übergeben werden kann. Daher müssen wir den Namen der ursprünglichen Download-Methode ändern (um doppelte Namen zu vermeiden) und dann eine ähnliche Methode hinzufügen, wie unten gezeigt: 🎜
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.'];
    }
}
Nach dem Login kopieren
Nach dem Login kopieren
🎜 Angenommen, der Download-Dateiteil der obigen Methode (der auskommentierte Teil) verwendet den gleichen Code, und wir können diese gleichen Codes in eine separate Methode schreiben, aber wir werden immer noch einige wiederholte Codes haben (Anmerkung des Übersetzers: Bezieht sich auf $name = $ in jeder Methode report->getName(); code>) und es gibt mehrere Einträge fast identischer Klassen. Dies kann zu zusätzlichem Aufwand für die Erweiterung des Codes oder Tests in der Zukunft führen. 🎜🎜Angenommen, wir erstellen einen neuen <code>AnalyticsReport; jetzt müssen wir dieser Klasse eine neue downloadAnalyticsReportPDF()-Methode hinzufügen. Sie können deutlich sehen, wie sich diese Datei erweitert. Dies ist ein perfektes Szenario für die Verwendung von Schnittstellen! 🎜🎜Beginnen wir mit der Erstellung unserer ersten Schnittstelle: Nennen wir sie DownloadableReport und definieren wir sie wie folgt: 🎜
class ReportDownloadService
{
    public function downloadReportPDF(DownloadableReport $report)
    {
        $name = $report->getName();

        // 下载文件……
    }

}
Nach dem Login kopieren
Nach dem Login kopieren
🎜Wir können jetzt BlogReport und UsersReportaktualisieren > um die DownloadableReport-Schnittstelle zu implementieren, wie im folgenden Beispiel gezeigt. Bitte beachten Sie jedoch, dass ich zu Demonstrationszwecken den Code in UsersReport absichtlich falsch geschrieben habe: 🎜rrreeerrreee🎜 Aber wenn wir versuchen, den Code auszuführen, erhalten wir aus folgendem Grund eine Fehlermeldung: 🎜
  • 缺少 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.'];
    }
}
Nach dem Login kopieren
Nach dem Login kopieren

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

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

        // 下载文件……
    }

}
Nach dem Login kopieren
Nach dem Login kopieren

我们现在可以把 UsersReportBlogReport 对象传入 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视频教程

Das obige ist der detaillierte Inhalt vonWas ist eine Schnittstelle? Wie schreibe ich eleganten Code mithilfe von Schnittstellen in PHP?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:juejin.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!