ホームページ バックエンド開発 PHPチュートリアル PHP コードのリファクタリング方法に関するディスカッション

PHP コードのリファクタリング方法に関するディスカッション

Apr 17, 2018 pm 01:41 PM
php 方法

この記事では、主に PHP コードの再構築方法を紹介し、PHP コードの再構築の概念、原理、関連する実装テクニック、注意事項をサンプルの形式で詳細に分析します。必要な友人は参考にしてください

この記事では、PHP の例を分析します。コードのリファクタリング方法。以下のように、参考用にみんなと共有してください:

PHP が単純なスクリプト言語から成熟したプログラミング言語に変化するにつれて、一般的な PHP アプリケーションのコード ベースの複雑さも増加します。これらのアプリケーションのサポートとメンテナンスを制御するために、さまざまなテスト ツールを使用してプロセスを自動化できます。その 1 つは単体テストで、作成したコードの正確さを直接テストできます。ただし、多くの場合、従来のコード ベースはこの種のテストには適していません。この記事では、コード ベースの改善への依存を減らしながら、一般的な単体テスト ツールを使用したテスト プロセスを簡素化するための、一般的な問題を含む PHP コードのリファクタリング戦略について説明します。

はじめに

PHP の開発の歴史を振り返ると、PHP は、当時人気のあった CGI スクリプトを置き換えるために使用されていた単純な動的スクリプト言語から、成熟した現代のプログラミング言語に変化したことがわかります。 コードベースが増大するにつれて、手動テストは不可能な作業となり、コードに対するすべての変更は、大小を問わず、アプリケーション全体に影響を及ぼします。これらの影響は、特定のページの読み込みやフォームの保存に影響を与えるだけの小さなものである場合もあれば、検出が難しい問題が発生したり、特定の条件下でのみ発生するエラーが発生したりする場合もあります。さらに、以前に修正された問題がアプリケーションで再発する可能性もあります。これらの問題を解決するために、多くのテスト ツールが開発されています。

一般的な方法の 1 つは、いわゆる機能テストまたは受け入れテストであり、典型的なユーザー操作を通じてアプリケーションをテストします。これはアプリケーション内の個々のプロセスをテストする優れた方法ですが、テスト プロセスが非常に遅くなる可能性があり、一般に、基になるクラスやメソッドが期待どおりに動作するかどうかのテストに失敗します。現時点では、単体テストという別のテスト方法を使用する必要があります。単体テストの目的は、アプリケーションの基礎となるコードの機能をテストし、実行後に正しい結果が得られることを確認することです。多くの場合、これらの「成長する」Web アプリケーションでは、時間の経過とともにテストがますます困難になるレガシー コードが徐々に導入され、開発チームがアプリケーションのテスト範囲を確保することが困難になります。これは、「テスト不可能なコード」と呼ばれることがよくあります。次に、アプリケーション内のテスト不可能なコードを特定する方法と、それを修正する方法を見てみましょう。

テスト不可能なコードを特定する

コードベースのテスト不可能性に関する問題領域は、コードを作成するときには明らかではないことがよくあります。 PHP アプリケーション コードを記述する場合、Web リクエストのフローに従ってコードを記述する傾向があり、その結果、通常はアプリケーション設計によりプロセス指向のアプローチが行われます。プロジェクトを完了させたり、アプリケーションをすぐに修正したりすることを急ぐあまり、開発者はコーディングを迅速に完了するために「手抜き」をする可能性があります。以前は、たとえ後続のサポート問題が発生する可能性があるとしても、開発者は最もリスクの少ない修正を行うことが多かったので、コードの書き方が不十分であったり混乱を招くと、アプリケーションのテスト不可能性の問題が悪化する可能性がありました。これらの問題領域は、通常の単体テストでは発見できません。

グローバル状態に依存する関数

グローバル変数は、PHP アプリケーションで便利です。これにより、アプリケーション内の一部の変数またはオブジェクトを初期化し、アプリケーション内の他の場所で使用できるようになります。ただし、この柔軟性には代償が伴い、テスト不可能なコードではグローバル変数の過剰使用が一般的な問題となります。リスト 1 でこれが起こっていることがわかります。

リスト 1. グローバル状態に依存する関数

<?php
function formatNumber($number)
{
  global $decimal_precision, $decimal_separator, $thousands_separator;
  if ( !isset($decimal_precision) ) $decimal_precision = 2;
  if ( !isset($decimal_separator) ) $decimal_separator = &#39;.&#39;;
  if ( !isset($thousands_separator) ) $thousands_separator = &#39;,&#39;;
  return number_format($number, $decimal_precision, $decimal_separator,
 $thousands_separator);
}
ログイン後にコピー

これらのグローバル変数は 2 つの異なる問題を引き起こします。最初の問題は、テストでこれらすべてのグローバル変数を考慮し、関数が受け入れられる有効な値に設定されていることを確認する必要があることです。 2 番目のより深刻な問題は、後続のテストの状態を変更したり、その結果を無効にしたりできないことです。グローバル状態がテスト実行前の状態にリセットされていることを確認する必要があります。 PHPUnit には、グローバル変数をバックアップし、テストの実行後にその値を復元できるツールがあり、この問題の解決に役立ちます。ただし、より良いアプローチは、テスト クラスがこれらのグローバル変数の値をメソッドに直接渡せるようにすることです。リスト 2 は、このアプローチの例を示しています。

リスト 2. グローバル変数のオーバーライドをサポートするようにこの関数を変更します

<?php
function formatNumber($number, $decimal_precision = null, $decimal_separator = null,
$thousands_separator = null)
{
  if ( is_null($decimal_precision) ) global $decimal_precision;
  if ( is_null($decimal_separator) ) global $decimal_separator;
  if ( is_null($thousands_separator) ) global $thousands_separator;
  if ( !isset($decimal_precision) ) $decimal_precision = 2;
  if ( !isset($decimal_separator) ) $decimal_separator = &#39;.&#39;;
  if ( !isset($thousands_separator) ) $thousands_separator = &#39;,&#39;;
  return number_format($number, $decimal_precision, $decimal_separator,
 $thousands_separator);
}
ログイン後にコピー

これにより、コードのテストが容易になるだけでなく、メソッドのグローバル変数への依存も少なくなります。これにより、グローバル変数を使用しないようにコードをリファクタリングできます。

リセットできない単一インスタンス

单一实例指的是旨在让应用程序中一次只存在一个实例的类。它们是应用程序中用于全局对象的一种常见模式,如数据库连接和配置设置。它们通常被认为是应用程序的禁忌, 因为许多开发人员认为创建一个总是可用的对象用处不大,因此他们并不太注意这一点。这个问题主要源于单一实例的过度使用,因为它会造成大量不可扩展的所谓 god objects 的出现。但是从测试的角度看,最大的问题是它们通常是不可更改的。清单 3就是这样一个例子。

清单 3. 我们要测试的 Singleton 对象

<?php
class Singleton
{
  private static $instance;
  protected function __construct() { }
  private final function __clone() {}
  public static function getInstance()
  {
    if ( !isset(self::$instance) ) {
      self::$instance = new Singleton;
    }
    return self::$instance;
  }
}
ログイン後にコピー

您可以看到,当单一实例首次实例化之后,每次调用 getInstance() 方法实际上返回的都是同一个对象,它不会创建新的对象,如果我们对这个对象进行修改,那么就可能造成很严重的问题。最简单的解决方案就是给对象增加一个 reset 方法。清单 4 显示的就是这样一个例子。

清单 4. 增加了 reset 方法的 Singleton 对象

<?php
class Singleton
{
  private static $instance;
  protected function __construct() { }
  private final function __clone() {}
  public static function getInstance()
  {
    if ( !isset(self::$instance) ) {
      self::$instance = new Singleton;
    }
    return self::$instance;
  }
  public static function reset()
  {
    self::$instance = null;
  }
}
ログイン後にコピー

现在,我们可以在每次测试之前调用 reset 方法,保证我们在每次测试过程中都会先执行 singleton 对象的初始化代码。总之,在应用程序中增加这个方法是很有用的,因为我们现在可以轻松地修改单一实例。

使用类构造函数

进行单元测试的一个良好做法是只测试需要测试的代码,避免创建不必要的对象和变量。您创建的每一个对象和变量都需要在测试之后删除。这对于文件和数据库表等 麻烦的项目来说成为一个问题,因为在这些情况下,如果您需要修改状态,那么您必须更小心地在测试完成之后进行一些清理操作。坚持这一规则的最大障碍在于对 象本身的构造函数,它执行的所有操作都是与测试无关的。清单 5 就是这样一个例子。

清单 5. 具有一个大 singleton 方法的类

<?php
class MyClass
{
  protected $results;
  public function __construct()
  {
    $dbconn = new DatabaseConnection(&#39;localhost&#39;,&#39;user&#39;,&#39;password&#39;);
    $this->results = $dbconn->query(&#39;select name from mytable&#39;);
  }
  public function getFirstResult()
  {
    return $this->results[0];
  }
}
ログイン後にコピー

在这里,为了测试对象的 fdfdfd 方法,我们最终需要建立一个数据库连接,给表添加一些记录,然后在测试之后清除所有这些资源。如果测试 fdfdfd完全不需要这些东西,那么这个过程可能太过于复杂。因此,我们要修改 清单 6所示的构造函数。

清单 6. 为忽略所有不必要的初始化逻辑而修改的类

<?php
class MyClass
{
  protected $results;
  public function __construct($init = true)
  {
    if ( $init ) $this->init();
  }
  public function init()
  {
    $dbconn = new DatabaseConnection(&#39;localhost&#39;,&#39;user&#39;,&#39;password&#39;);
    $this->results = $dbconn->query(&#39;select name from mytable&#39;);
  }
  public function getFirstResult()
  {
    return $this->results[0];
  }
}
ログイン後にコピー

我们重构了构造函数中大量的代码,将它们移到一个 init() 方法中,这个方法默认情况下仍然会被构造函数调用,以避免破坏现有代码的逻辑。然而,现在我们在测试过程中只能够传递一个布尔值 false 给构造函数,以避免调用 init()方法和所有不必要的初始化逻辑。类的这种重构也会改进代码,因为我们将初始化逻辑从对象的构造函数分离出来了。

经硬编码的类依赖性

正如我们在前一节介绍的,造成测试困难的大量类设计问题都集中在初始化各种不需要测试的对象上。在前面,我们知道繁重的初始化逻 辑可能会给测试的编写造成很大的负担(特别是当测试完全不需要这些对象时),但是如果我们在测试的类方法中直接创建这些对象,又可能造成另一个问题。清单 7显示的就是可能造成这个问题的示例代码。

清单 7. 在一个方法中直接初始化另一个对象的类

<?php
class MyUserClass
{
  public function getUserList()
  {
    $dbconn = new DatabaseConnection(&#39;localhost&#39;,&#39;user&#39;,&#39;password&#39;);
    $results = $dbconn->query(&#39;select name from user&#39;);
    sort($results);
    return $results;
  }
}
ログイン後にコピー

假设我们正在测试上面的 getUserList方法,但是我们的测试关注点是保证返回的 用户清单是按字母顺序正确排序的。在这种情况下,我们的问题不在于是否能够从数据库获取这些记录,因为我们想要测试的是我们是否能够对返回的记录进行排 序。问题是,由于我们是在这个方法中直接实例化一个数据库连接对象,所以我们需要执行所有这些繁琐的操作才能够完成方法的测试。因此,我们要对方法进行修 改,使这个对象可以在中间插入,如 清单 8所示。

清单 8. 这个类有一个方法会直接实例化另一个对象,但是也提供了一种重写的方法

<?php
class MyUserClass
{
  public function getUserList($dbconn = null)
  {
    if ( !isset($dbconn) || !( $dbconn instanceOf DatabaseConnection ) ) {
      $dbconn = new DatabaseConnection(&#39;localhost&#39;,&#39;user&#39;,&#39;password&#39;);
    }
    $results = $dbconn->query(&#39;select name from user&#39;);
    sort($results);
    return $results;
  }
}
ログイン後にコピー

これで、予期されるデータベース接続オブジェクトと互換性のあるオブジェクトを直接渡し、新しいオブジェクトを作成する代わりにこのオブジェクトを直接使用できるようになりました。モック オブジェクトを渡すこともできます。つまり、ハードコードされた方法で一部の呼び出しメソッドで必要な値を直接返すことができます。ここでは、データベース接続オブジェクトのクエリ メソッドをシミュレートできるため、実際にデータベースにクエリを実行せずに結果を返すだけで済みます。このようなリファクタリングによって、アプリケーションが指定されたデフォルトのデータベース接続にバインドするだけでなく、必要に応じて別のデータベース接続を接続できるようになるため、このアプローチも改善できます。

テスト可能なコードの利点

明らかに、よりテスト可能なコードを作成すると、PHP アプリケーションの単体テストを確実に簡素化できます (この記事に示されている例でわかるように) が、このプロセスでは、アプリケーションの設計、モジュール性、安定性が向上します。私たちは皆、PHP アプリケーションの主要プロセスの 1 つに多くのビジネス ロジックやプレゼンテーション ロジックが詰め込まれた「スパゲッティ」コードを見たことがあるでしょう。これは間違いなく、アプリケーションを使用するユーザーに重大なサポート問題を引き起こすことになります。コードをよりテストしやすくする過程で、以前に問題があったコードの一部をリファクタリングしました。これらのコードは、設計面だけでなく機能面でも問題がありました。これらの関数とクラスの汎用性を高め、ハードコーディングされた依存関係を削除して、アプリケーションの他の部分でより簡単に再利用できるようにすることで、コードの再利用性を向上させます。さらに、今後のコード ベースのサポートを簡素化するために、不適切に記述されたコードをより高品質のコードに置き換えます。

結論

この記事では、PHP アプリケーションにおけるテスト不可能なコードの典型的な例を通じて、PHP コードのテスト容易性を向上させる方法を検討しました。また、アプリケーションでこのような状況がどのように発生するのか、そしてテストを容易にするために問題のあるコードを適切に修正する方法についても説明します。また、これらのコード変更により、コードのテスト容易性が向上するだけでなく、一般にコードの品質が向上し、リファクタリングされたコードの再利用性も向上することもわかりました。

関連する推奨事項:

PHP コード再利用メカニズムの例の詳細な説明


以上がPHP コードのリファクタリング方法に関するディスカッションの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Ubuntu および Debian 用の PHP 8.4 インストールおよびアップグレード ガイド Ubuntu および Debian 用の PHP 8.4 インストールおよびアップグレード ガイド Dec 24, 2024 pm 04:42 PM

PHP 8.4 では、いくつかの新機能、セキュリティの改善、パフォーマンスの改善が行われ、かなりの量の機能の非推奨と削除が行われています。 このガイドでは、Ubuntu、Debian、またはその派生版に PHP 8.4 をインストールする方法、または PHP 8.4 にアップグレードする方法について説明します。

PHP 開発用に Visual Studio Code (VS Code) をセットアップする方法 PHP 開発用に Visual Studio Code (VS Code) をセットアップする方法 Dec 20, 2024 am 11:31 AM

Visual Studio Code (VS Code とも呼ばれる) は、すべての主要なオペレーティング システムで利用できる無料のソース コード エディター (統合開発環境 (IDE)) です。 多くのプログラミング言語の拡張機能の大規模なコレクションを備えた VS Code は、

PHPでHTML/XMLを解析および処理するにはどうすればよいですか? PHPでHTML/XMLを解析および処理するにはどうすればよいですか? Feb 07, 2025 am 11:57 AM

このチュートリアルでは、PHPを使用してXMLドキュメントを効率的に処理する方法を示しています。 XML(拡張可能なマークアップ言語)は、人間の読みやすさとマシン解析の両方に合わせて設計された多用途のテキストベースのマークアップ言語です。一般的にデータストレージに使用されます

今まで知らなかったことを後悔している 7 つの PHP 関数 今まで知らなかったことを後悔している 7 つの PHP 関数 Nov 13, 2024 am 09:42 AM

あなたが経験豊富な PHP 開発者であれば、すでにそこにいて、すでにそれを行っていると感じているかもしれません。あなたは、運用を達成するために、かなりの数のアプリケーションを開発し、数百万行のコードをデバッグし、大量のスクリプトを微調整してきました。

JSON Web Tokens(JWT)とPHP APIでのユースケースを説明してください。 JSON Web Tokens(JWT)とPHP APIでのユースケースを説明してください。 Apr 05, 2025 am 12:04 AM

JWTは、JSONに基づくオープン標準であり、主にアイデンティティ認証と情報交換のために、当事者間で情報を安全に送信するために使用されます。 1。JWTは、ヘッダー、ペイロード、署名の3つの部分で構成されています。 2。JWTの実用的な原則には、JWTの生成、JWTの検証、ペイロードの解析という3つのステップが含まれます。 3. PHPでの認証にJWTを使用する場合、JWTを生成および検証でき、ユーザーの役割と許可情報を高度な使用に含めることができます。 4.一般的なエラーには、署名検証障害、トークンの有効期限、およびペイロードが大きくなります。デバッグスキルには、デバッグツールの使用とロギングが含まれます。 5.パフォーマンスの最適化とベストプラクティスには、適切な署名アルゴリズムの使用、有効期間を合理的に設定することが含まれます。

母音を文字列にカウントするPHPプログラム 母音を文字列にカウントするPHPプログラム Feb 07, 2025 pm 12:12 PM

文字列は、文字、数字、シンボルを含む一連の文字です。このチュートリアルでは、さまざまな方法を使用してPHPの特定の文字列内の母音の数を計算する方法を学びます。英語の母音は、a、e、i、o、u、そしてそれらは大文字または小文字である可能性があります。 母音とは何ですか? 母音は、特定の発音を表すアルファベットのある文字です。大文字と小文字など、英語には5つの母音があります。 a、e、i、o、u 例1 入力:string = "tutorialspoint" 出力:6 説明する 文字列「TutorialSpoint」の母音は、u、o、i、a、o、iです。合計で6元があります

PHPでの後期静的結合を説明します(静的::)。 PHPでの後期静的結合を説明します(静的::)。 Apr 03, 2025 am 12:04 AM

静的結合(静的::) PHPで後期静的結合(LSB)を実装し、クラスを定義するのではなく、静的コンテキストで呼び出しクラスを参照できるようにします。 1)解析プロセスは実行時に実行されます。2)継承関係のコールクラスを検索します。3)パフォーマンスオーバーヘッドをもたらす可能性があります。

PHPマジックメソッド(__construct、__destruct、__call、__get、__setなど)とは何ですか? PHPマジックメソッド(__construct、__destruct、__call、__get、__setなど)とは何ですか? Apr 03, 2025 am 12:03 AM

PHPの魔法の方法は何ですか? PHPの魔法の方法には次のものが含まれます。1。\ _ \ _コンストラクト、オブジェクトの初期化に使用されます。 2。\ _ \ _リソースのクリーンアップに使用される破壊。 3。\ _ \ _呼び出し、存在しないメソッド呼び出しを処理します。 4。\ _ \ _ get、dynamic属性アクセスを実装します。 5。\ _ \ _セット、動的属性設定を実装します。これらの方法は、特定の状況で自動的に呼び出され、コードの柔軟性と効率を向上させます。

See all articles