PHP の 3 層構造 (パート 1)?? 単純な 3 層構造
まずは、コード 1 に示す、最も単純な 3 層構造のコードを見てみましょう。
// コード 1
// 外部層
Class LWordHomePage {
// メッセージを追加
PUBLIC FUNCTION APPEND ($ newlword) {
// 中間サービス層を呼び出す
$ Serv = New LwordServiceCore ()
$; Serv-&T; append($newLWord);
}
};
//中間サービス層
class LWordServiceCore {
append($newLWord)
}}; 3 つの呼び出しシーケンスを直感的に確認します。階層構造。しかし、実際の開発では、この単純な 3 層構造ではニーズを満たすことができません。外観層と中間サービス層のコードから始めましょう。 new キーワードを直接使用して、外観層 LWordHomePage クラスの中間サービス クラス LWordServiceCore を作成して呼び出すことは、ハードコーディング メソッドです。実際のプロジェクト開発プロセスでは、外観層と中間サービス層を別人が開発する、つまり複数人で機能モジュールを完成させる場合があります。アピアランス層の LWordHomePage クラスの開発は、LWordServiceCore クラスが完全に開発されるまで待つことができません (つまり、アピアランス層は、中間のサービス層が完全に開発されるまで待って開始することはできません)。プロジェクトを複数人で同時に開発できるようにするには、コード設計を削減する必要があります。アピアランス層の開発進捗に合わせて、一時的な中間サービスクラスを編成することも可能です。中間のサービス層が完全に開発されたら、それを置き換えるだけです... 図 2:
(図 2) に示すように、外観層は異なるサービス間で切り替わります
明らかに、これを達成するには、要件を考慮すると、新しいキーワードを直接使用して外観レイヤーで LWordServiceCore クラスを作成して呼び出すのは非常に柔軟性がありません。臨機応変かつ自由に切り替えるのは難しい! !中間サービス層の一時的な実装として機能する TempService クラスを作成できます。また、TempService と LWordServiceCore という 2 つのクラスも分析する必要があります。どちらもメッセージを追加するための同じ追加関数を持っていますが、1 つは一時的なもので、もう 1 つは実際のものです。 TempService クラスと LWordServiceCore クラスは両方ともパブリック関数を持っているため、共通の親クラスが必要です。このパブリック上位クラスには他のメンバーや属性が存在しないことを考慮して、このパブリック上位クラスはインターフェイス、つまり ILWordService! として定義されます。 UML クラス図を図 3 に示します。
(図 3) ILWordService インターフェイスの定義と実装
TempService または LWordServiceCore クラス オブジェクトは LWordHomePage クラス内で直接作成されず、作成プロセスクラス MyServiceFactory (単純なファクトリ パターン) に渡されます。このようにして、外観層の LWordHomePage クラスは ILWordService インターフェイスを認識するだけでよく、外観層のコードは特定の中間サービス コードを考慮しません。これにより、外観層と特定のサービス コードの分離が効果的に実現されます。
これは何に相当しますか?ちょうど 2 人のハードウェア エンジニアのように、1 人はコンピュータ グラフィックス カードを製造し、もう 1 人はコンピュータのマザーボードを製造します。グラフィックス カードを製造するエンジニアは、グラフィックス カードをテスト回路に接続して、グラフィックス カードが適切に動作するかどうかをテストできますか?同様に、マザーボードを製造するエンジニアは、マザーボードを別のテスト回路に挿入して、マザーボードが適切に動作するかどうかをテストすることもできます。 2 人のエンジニアが作業を完了したら、作業結果を結び付けることができます。これは、ほぼ半分の時間を節約できる並行開発方法です。ソフトウェアエンジニアリングの観点から、インターフェースコードを設計する際には、生産効率を向上させるために複数人による同時開発をサポートする必要があるかどうかも考慮する必要があります。
UML クラス図 (図 3 を参照) に従って、コード 2 に示すように PHP コードを変更します。
//コード 2、ファクトリを通じてメッセージ サービスを作成し、
//外観レイヤー クラス
class LWordHomePage {
use using using out through out‐through off out off の ‐ to
‐ ‐ create();
'''''''''' ' ‐ ' out's ' ' ' ‐ ‐‐‐‐‐‐‐‐ and append($newLWord);
};
// サービス ファクトリ クラス
class MyServiceFactory {
public static function create() LWordServiceCore () // メッセージを追加します
public function append( $ newlword) {
// Tempor ary コード(省略)}};
// 中間サービス層
Class LWordService Implements IlwordService {
/ / メッセージを追加します
PUBLIC FUNCTION {用 // データ アクセス レイヤーを呼び出します
$dbTask ($newLWord ) {
(パート 2) )?? PHP は AOP を実装します
中間サービス層に焦点を当てましょう。中間サービス層のコードは比較的単純で、データ アクセス層のコードを呼び出してメッセージをデータベースに保存するだけです。コード1に示すように、コード1
//中間サービスレイヤーclass lwordserviceecore ilwordservice {public function append($ newlword)lworddbtask();たとえば、メッセージを追加する前にユーザーの権限を確認したいと考えています。メッセージを残せるのは登録ユーザーだけです!コード 2 に示すように、コードを変更する必要があります。
// コード 2、ログイン検証を追加
// 中間サービス層
class LWordServiceCoreimplements ILWordService {
// メッセージを追加
public function append( $newLWord) {
if (!($userLogin)) {
);
}
} ;
マーケティング部門も、メッセージに汚い言葉が含まれている場合は、メッセージを追加する前にその内容を確認したいと考えています。保存されません。コード 3 に示すように、コードの変更を続けます。
//コード 3、汚い言葉のフィルタリングを追加します
//中間サービス層
class LWordServiceCoreimplements ILWordService {
public function append($newLWord)ログイン}} (If (STRISTR ($ newlword, "sb") {
// を含む悪口、メッセージの送信失敗}}
$ dbtask = new lworddbtask ();
$ dbtask ->append($newLWord);
}
};
製品部門も提案新しい要件があり、ポイント メカニズムへの参加を希望しています。具体的には、メッセージを残すことに成功するたびに、ユーザーに +5 ポイントが与えられます。コード 4 に示すように、コードの変更を続けます。
// コード 4、メッセージ ポイント メカニズムを追加します
// 中間サービス層
class LWordServiceCoreimplements ILWordService {// メッセージを追加します
パブリック関数append($newLWord) { }
データ アクセス層
score = $score + 5 E Saveuserscore ($ ユーザー名, $ スコア);
}}};
は、1,000 ポイントが蓄積された後にユーザーにアップグレードされることを期待しています。コード 5 に示すように、コードの変更を続けます。
// コード 5、ユーザー アップグレード ルールを追加
// 中間サービス層
class LWordServiceCoreimplements ILWordService {
// メッセージを追加
public function append ($newLWord) { }
データ アクセス層を呼び出します
= new LWordDBTask(); use using use using using ‐ $score = $score + ;
saveUserLevel($ユーザー名、$レベル);
需要が増加するにつれて、中間サービス層のコードを常に変更する必要があります。しかし、要件が増えれば増えるほど、中間サービス層のコードがより多く、より大きくなるということは、難しくないはずです。結局のところ、3 層構造の開発モデルを使用しても、プロジェクトの難易度を効果的に下げることはできません。さらに、需要の変化に応じて中間サービス コードを変更した後は、新しいコードを効果的にテストするのではなく、すべてのコードを再テストする必要があります...
実際に、この掲示板のコードを注意深く分析してみましょう。ビジネスロジックとサブビジネスロジックの概念を最初に提案します。何はともあれ、メッセージの内容をデータベースに保存することがビジネス ロジックの根幹です。これが主要なビジネス ロジックです。需要が増加したため、このセクションは変更されていません。データベースに保存する前の権限の確認と内容のチェック、データベースに保存した後にユーザーにポイントを追加してユーザーをアップグレードするなど、これらはすべて前処理と仕上げのタスクであり、すべて二次的なビジネス ロジックです。メインのビジネス ロジックはほとんど変わりませんが、二次的なビジネス ロジックは非常に頻繁に変更されます。コードの可読性と保守性を向上させるために、これらの二次的なビジネス ロジックを別の場所に配置し、メインのビジネス ロジックに干渉しないようにすることを検討できます。メインのビジネス ロジックは、実行すべきことに集中する必要があります。その他のことについては、メインのビジネス ロジックは気にしません。コード 6 に示すように、コードは次のように記述できます。
// コード 6、メイン ビジネス ロジックとセカンダリ ビジネス ロジックを分離します
// 中間サービス層
class LWordServiceCoreimplements ILWordService {
// Addメッセージ
パブリック関数 append($newLWord) {
BeforeAppend($newLWord);
// データ アクセス層を呼び出します
$dbTask = new LWordDBTask();
$dbTask-> append($newLWord) ;
BehindAppend ($newLWord); これにより、メインのビジネス ロジック コードからセカンダリ ビジネス ロジックがクリーンアップされます。メイン ビジネス ロジックは、Append の前に「オーバーチュア」関数があり、Append の後ろに「エピローグ」関数があることを知っていますが、メイン ビジネス ロジックは、オーバーチュア関数とエピローグ関数で具体的に何が行われるかを知りませんし、知る必要もありません。もちろん、実際のコーディング作業はそれほど単純ではありません。また、製品部門やマーケティング部門のニーズの変化も考慮する必要があるため、そのような変化に対応するにはプラグイン方式を導入するのが最善ですが、それに依存します。 beforeAppend と BehindAppend の 2 つの関数のみでこの目標を達成するのが最善の方法です。これは達成できません ~
プラグイン メソッドを実装したい場合は、インターフェイスを作成できます。インターフェイスを使用する利点は、定義と実装を分離できること、もう 1 つはポリモーフィズムを実現できることです。メッセージ拡張インターフェイス ILWordExtension を作成します。これには、beforeAppend と BehindAppend の 2 つの関数があります。権限の検証、コンテンツ検査、ボーナス ポイントなどの機能は、ILWordExtension インターフェイスを実装する 3 つの実装クラスとみなすことができます。主要なビジネス ロジックは、2 番目のビジネス ロジックを完成させるために、これら 3 つの実装クラスを横断します。図 1:
(図 1) に示すように、拡張インターフェイス
CheckPowerExtension 拡張クラスはユーザー権限の検証に使用され、CheckContentExtension 拡張クラスはメッセージ コンテンツのチェックに使用され、AddScoreExtension 拡張クラスはユーザーのボーナスポイントとアップグレードに使用されます。回路図コードはコード 7 に示されています:
// コード 7、拡張インターフェイスを追加します
// 拡張インターフェイス
interface ILWordExtension {
// メッセージを追加する前
public function beforeAppend($newLWord);
// メッセージを追加した後
public function BehindAppend($newLWord);
} ;
//権限を確認します
class CheckPowerExtensionimplements ILWordExtension {
// メッセージを追加する前に
public function beforeAppend($newLWord) {
// ここでユーザー権限を決定します
}
// メッセージ追加後
public function BehindAppend( $newLWord) {
}
};
// メッセージテキストを確認
class CheckContentExtensionimplements ILWordExtension {
// メッセージ追加前
public function beforeAppend($newLWord) {
if (strist($newLWord, "SB")) {
with use using through using through out through out through through off ‘s’ through ‘s’ through '' ‐ ‐ ‐‐‐‐‐‐ {
//メッセージを追加
public function beforeAppend($newLWord) {
}
use using using using using using using using out using using ' 's ' using using 's through using ‐‐‐ ‐‐‐‐‐‐ 1 対 3 から 1 対 1 対 1 回のうち、 LWordService Implements IlwordService {
// メッセージを追加します
Public Function Append ($ newlword) {
// メッセージを追加する前に
$ THIS-& GT;访 // データ アクセス層を呼び出します
$ dbtask = new lwordddbtask (); end($newLWord ; $ext-> ;beforeAppend($newLWord);
foreach ($extArray として $ext) {
反復各拡張機能を介して、この関数の背後に付属関数を呼び出します。/ ;
新しい要件がある場合は、ILWordExtension 実装クラスを追加して getExtArray 関数に登録するだけです。その後、プログラムは組織化され、拡張可能になったと考えられます。
しかし、忙しすぎて満足しないでください。このスケーラビリティには問題があります。新しい要件が発生した場合は、ILWordExtension 実装クラスを追加できますが、これは確かに正しいことです。ただし、この新しいクラスを getExtArray 関数に登録するということは、メインのビジネス ロジック コードを引き続き変更する必要があることを意味します。改造できないのでしょうか?新しい需要の変化があるたびに、メインのビジネス ロジックに通知する必要がありますが、これは結局のところ良いことではありません。最も理想的な状況は、新しい拡張コードがシステムに追加された後、メイン ビジネス ロジックが新しい拡張機能をまったく認識しないため、メイン ビジネス ロジック コードを変更する必要がないことです。この目的を達成するには、図 2 に示すように、設計計画を最適化する必要もあります。
(図 2)、拡張機能を呼び出すメイン プログラム (つまり、 、中間サービス クラス LWordServiceCore)、ILWordExtension (拡張子) があることだけを知っておいてください。CheckPowerExtension (権限拡張子をチェック)、CheckContentExtension (コンテンツ拡張子をチェック)、AddScoreExtension (追加スコア) の 3 つのクラスがあることを知る必要はありません。拡大)。これら 3 つのクラスの呼び出しプロセスは、LWordExtensionFamily (拡張ファミリー クラス) に移動されました。
LWordExtensionFamily は、実際には複数の ILWordExtension インターフェイス インスタンスを格納できるコンテナ クラスです。図 2 からわかるように、このコンテナ クラスは ILWordExtension インターフェイスを実装するだけでなく、複数の ILWordExtension インターフェイス インスタンスも集約するため、非常に特殊です。 LWordServiceCore クラスの場合、このクラスは ILWordExtension インターフェイスのみを認識しますが、このインターフェイスに 3 つの実装クラスがあることは認識しません。 LWordExtensionFamily クラスは、中間サービス クラスの要件をよく満たす ILWordExtension インターフェイスを実装しています。この拡張機能ファミリー クラスは、ILWordExtension の実装クラスが 3 つあることを認識しており、それらを 1 つずつ呼び出します。LWordExtensionFamily コードは大まかに次のとおりです。コード8に示すように、
//コード8、ファミリー
//展開lordextensionsionsionsionsionsionly {//拡張アレイ
// extensionaray = array(); function addextent(ilwordextent $ extension){使用の使用 - $ extension - > beforeapend($ newlword);(((((((((((((((((((((((((((拡張機能の挿入までをスムーズに実装するには、ファクトリクラス MyExtensionFactory を作成するのが最適です。コード 9 に示すように:
// コード 9
// カスタム拡張機能ファクトリー
class MyExtensionFactory {
public static function createLWordExtension() /拡張子を追加
- wef ;
// 返される内容に注意してくださいこれは、拡張機能ファミリーのクラス オブジェクトです。
// 拡張機能ファミリー LWordExtensionFamily も、たまたまインターフェイス ILWordExtension を実装しています。
// したがって、これはビジネス ロジックの要件と一致しています
// これ以降、ビジネス ロジックは使用できなくなります。詳細は気にしてください 拡張オブジェクトの場合、知っておく必要があるのは拡張ファミリーだけです
}
}
拡張ファクトリー クラスを使用する利点は、拡張インスタンスを自由に追加および削除できることであり、これによりプラグ可能なプログラミングが実装されます。 LWordServiceCore クラスの場合、ILWordExtension インターフェイスは 1 つだけ知られています。LWordExtensionFamily の場合、各拡張機能を 1 つずつ呼び出す必要があることが知られていますが、拡張機能の特定の数は MyExtensionFactory を通じて指定されます。各人の責任の構造も非常に明確です。 MyExtensionFactory クラスの createLWordExtension 関数が、 new キーワードなどのハードコーディングによって拡張子リストを追加するのではなく、設定ファイルをより賢く読み取る方法によって拡張子リストを取得すると仮定すると、より便利であり、柔らかい毛織物?ただし、これについてはこの記事では説明しません。
中間サービス層は、ファクトリ クラスを通じて ILWordExtension インターフェイスの特定のインスタンスを取得し、その beforeAppend メソッドと BehindAppend メソッドを呼び出します。もちろん、中間サービスは、ファクトリ クラスが複数の ILWordExtension インスタンスを含むコンテナを返すことを知りません (このコンテナは ILWordExtension インターフェイスも実装しているため)。したがって、中間サービスは、拡張機能が 1 つずつ呼び出されることを知りません。完全なコードはコード 10 に示されています:
// コード 10、完全なコード
// 拡張インターフェイス
interface ILWordExtension {
// メッセージを追加する前
public function beforeAppend($newLWord);
// メッセージを追加した後
public function BehindAppend($newLWord);
};
// 権限を確認します lClass Checkpowerextension Implements IlwordEXTENSION {
// メッセージの前に追加します
PUBLIC FUNCTION BeForeappend ($ newlword) {
// ここでユーザーの権限を判断します} // メッセージを残した後、Public Function Behindappend ( $ newLWord) {
}
};
// メッセージ テキストを確認します
class CheckContentExtensionimplements ILWordExtension {
// メッセージを追加する前に
public function beforeAppend($newLWord) {
if (stristr($newLWord, "クソ"))
throw new Exception();
}
// メッセージ追加後
public function BehindAppend($newLWord) {
}
};
// ユーザーポイント
class AddScoreExtensionimplements ILWordExtension {
beforeAppend( $newLWord) {
}
// メッセージ追加後
public function BehindAppend($newLWord) {
// 拡張子配列
private $_extensionArray = array();
// 拡張子を追加
public function addExtension(ILWordExtension $拡張){
$ this-> _extensionArray [] = $ extension;
}
// [$ newlword)の後のパブリック関数Beforeapend($ newlword){
public append($ newlword){
foreach($ this this $ this($ newlword){ - > addextension(new checklwordextension());
コード 10 から、CheckPowerExtension、CheckContentExtension、AddScoreExtension、および LWordExtensionFamily はすべて ILWordExtension インターフェイスを実装していますが、それらの beforeAppend 関数プロセスと BehindAppend 関数プロセスは完全に異なることがわかります。特に、LWordExtensionFamily 拡張機能ファミリー クラスには実際のビジネス ロジック処理はありませんが、呼び出しを各拡張機能に順番に渡します。具象クラスでの beforeAppend 関数と BehindAppend 関数のさまざまな実装は、オブジェクト指向プログラミングにおける非常に典型的な機能、つまりポリモーフィズムです。
二次的なビジネス ロジックをさまざまな拡張機能に分散することは、AOP (アスペクト指向プログラミング) プログラミング手法と非常によく似ています。権限の検証、コンテンツのチェック、ポイントは別の側面として考えることができます。これらの側面は、主要なビジネス ロジックと交差しますが、主要なビジネス ロジックには影響を与えません。拡張コードが主要なビジネスを妨げないという利点があります。ロジックを使用して、拡張機能をコーディングして単体テストし、MyExtensionFactory ファクトリ クラスを通じて拡張機能をビジネス プロセスに挿入することもできます。完全な実行プロセスを図 3 に示します。
(図 3)、実行プロセス