ホームページ  >  記事  >  バックエンド開発  >  インターフェイスの意味と、インターフェイスを使用して高品質の PHP コードを記述する方法を説明する記事

インターフェイスの意味と、インターフェイスを使用して高品質の PHP コードを記述する方法を説明する記事

藏色散人
藏色散人転載
2021-11-08 15:43:373710ブラウズ

概要

コーディングにおいて重要なことの 1 つは、コードが読みやすく、保守しやすく、拡張可能で、テストが容易であることを確認することです。これらの問題を改善できる 1 つの方法は、インターフェイスを使用することです。

対象読者

この記事は、OOP (オブジェクト指向プログラミング) の概念と PHP の継承を基本的に理解している開発者を対象としています。PHP で継承の使用方法を知っている場合は、この記事を参照してください。記事が分かりやすくなります。

インターフェースとは何ですか?

基本的に、インターフェースは「クラスが何をすべきか」を記述します。インターフェイスは、インターフェイスを実装するクラスにインターフェイスで指定されたパブリック メソッドが確実に含まれるようにするために使用されます。

Interface :

  • は、クラス内のパブリック メソッドを定義するために使用できます。
  • は、クラス内の定数を定義するために使用されます。

インターフェイス :

  • を個別にインスタンス化できません。
  • は、クラス内のプライベート メソッドまたは保護されたメソッドを定義するために使用されます。
  • は、クラス内の属性を定義するために使用されます。

Interface は、クラスに含める必要があるパブリック メソッドを定義するために使用されます。覚えておく必要があるのは、インターフェイスではメソッド名、パラメータ、戻り値のみが定義され、メソッド本体は含まれないということです。これは、インターフェイスはオブジェクト間の通信を定義するためにのみ使用され、クラス間の通信の特定の動作を定義するために使用されるわけではないためです。少しコンテキストを説明するために、この例では、いくつかのパブリック メソッドを定義するインターフェイスの例を示します。

interface DownloadableReport
{
    public function getName(): string;

    public function getHeaders(): array;

    public function getData(): array;
}

php.net によると、インターフェイスには 2 つの主な目的があります。

  1. 開発者は次のことを行うことができます。異なるクラスのオブジェクトは、同じインターフェイスを実装しているため、互換的に使用できます。一般的な例としては、複数のデータベース アクセス サービス、複数の支払いゲートウェイ、またはさまざまなキャッシュ戦略があります。異なる実装は、それを使用するコードを変更することなく置き換えることができます。
  2. オブジェクトが他にできることや実装方法を気にせずに、関数またはメソッドがインターフェイスに準拠するパラメーターを受け入れて操作できるようにします。これらのインターフェイスには、動作の意味を説明するために、Iterable、Cacheable、Renderable などの名前が付けられることがよくあります。 [推奨学習: "PHP ビデオ チュートリアル "]

PHP でのインターフェイスの使用

インターフェイスは OOP (オブジェクト指向プログラミング) の非常に重要な部分です。 ) コード。これらにより、コードを分離し、スケーラビリティを向上させることができます。例として、次のクラスを見てみましょう:

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

ご覧のとおり、文字列を返すメソッドを持つクラスを定義しました。これにより、メソッドの動作が決定したので、getName() が返された文字列をどのように構築するかを確認できます。ただし、このメソッドを別のクラスのコードから呼び出すとします。もう 1 つのクラスは文字列がどのように構築されるかには関係なく、文字列が返されるかどうかだけを気にします。たとえば、このメソッドを別のクラスで呼び出す方法を見てみましょう。

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

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

上記のコードはすでに機能していますが、UserReport でこのメソッドを呼び出したい場合はどうなるかを想像してみましょう。ユーザーレポートをクラスにダウンロードします。もちろん、BlogReport クラスを 1 つだけ渡す必要があるため、ReportDownloadService の既存のメソッドを使用することはできません。したがって、既存のメソッドの名前を変更し、新しいメソッドを追加する必要があります。次のようになります:

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

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

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

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

実際には見えませんが、上記のクラスの残りのメソッドは同じコードを使用してダウンロードを構築すると想定しています。パブリック コードをメソッドに昇格させることはできますが、それでもまだいくつかのパブリック コードが存在します。これに加えて、このクラスにはほぼ同一のコードを持つ複数のエントリがあります。これにより、将来コードを拡張したり、テスト機能を追加したりするときに追加の作業が発生する可能性があります。

たとえば、新しい AnalyticsReport を作成します。このクラスに新しい downloadAnalyticsReportPDF() メソッドを追加する必要があります。この時点で、ファイルが急速に増大していることがわかります。今はインターフェースを使用するのに最適な時期です。

まずインターフェイスを作成します。 DownloadableReport というインターフェイスを作成し、次のように定義します。

interface DownloadableReport
{
    public function getName(): string;

    public function getHeaders(): array;

    public function getData(): array;
}

次に、BlogReportUsersReport を次のように変更します。以下に示すように、 DownloadableReport インターフェイスを実装します。何かを実証するために、意図的に間違った UsersReport コードを作成しました。

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

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

    public function getData(): array
    {
        return ['报告的数据在这里'];
    }
}
class UsersReport implements DownloadableReport
{
    public function getName()
    {
        return ['用户报告'];
    }

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

このコードを実行しようとすると、次の理由でエラーが発生します:

  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

以上がインターフェイスの意味と、インターフェイスを使用して高品質の PHP コードを記述する方法を説明する記事の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はlearnku.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。