抽象化によるサービスロケーターとブランチ-スーパーポーション

現在、 Git Workflowと呼ばれる一般的なアプリケーション開発アプローチです。 このアプローチを使用するかどうかを尋ねられたときに、「誰が使用しないのか」という驚きに答える場合があります。 一見すると、これは非常に便利なモデルであり、シンプルで簡単ですが、多数の参加者と開発を行う人は、合併がどれほど複雑で退屈であるかを知っています。 どのような深刻な紛争が発生する可能性があり、どのようなルーチンがそれらを解決するために働くのか すでに2人の開発者のチームで、次の合併についてため息を聞くことができます。10、20人の開発者についてはどうでしょうか。 さらに、多くの場合、3つの主要なブランチ(条件付き)dev、ステージング、prodがあります。これらは、最新の状態を維持し、マージの競合をテストして解決する必要があります。 そして、一方向だけでなく、逆方向にもあります。プロダクションにバグがあり、緊急に何かをする必要がある場合、ホットフィックスはしばしばプロダクションに入り、他のブランチにマージされるためです。 もちろん、レイアウトを担当するチームリーダーまたはその他の幸運な人がセミロボットである場合、問題は肥大化します。 しかし、別の開発オプションを試してみたいという要望がある場合は、カットの下で、スーパーポーションのオファーがあります。







構成部品



したがって、2つのパターン-Service LocatorBranch By Abstractionは、スーパーポーションを調理するための材料です。 読者がService Locatorに精通しているという事実に導かれますが、そうでない場合は、Martin Fowlerの記事をご覧ください 。 このパターンについては、正と負の両方の陰影を持つインターネットに関する多くの文献もあります。 誰かが彼をアンチパターンとさえ呼びます。 ハブで記事賛否両論」を読むことができます 私の意見は非常に成功した便利なパターンであり、無理をしないために使用方法を知る必要があります。 実際、私たちの世界のあらゆるものと同様に、妥協点を見つけてください。



したがって、2番目のコンポーネントはBranch By Abstractionです。 参照により、私は再びファウラーに興味を持っている人、そして怠な人に送ります-私はここでその本質を簡単に説明します。



開発者は、新しいブランチを作成したり、直接必要なクラスを編集したりする代わりに、新しい機能を追加したり、リファクタリングしたりするときに、クラスの隣に開発中のクラスのコピーを作成します。 クラスの準備ができると、元のクラスが新しいクラスに置き換えられ、テストのために実稼働用にレイアウトされます。 クラスがシステムの他のコンポーネントと競合しないように、クラスはインターフェースを実装します。
道徳教育
一般に、インターフェースの開発は非常に優れたアプローチであり、無視するべきではありません。 「その実装ではなく、インターフェースに基づいたプログラム」
多くの場合、Branch By Abstracionに関するチュートリアルでは、「最初に、開発者はデフォルトでオフになっている機能のスイッチをコミットし、クラスの準備ができたらオンにします。」と表示されます。 しかし、どのような「機能スイッチ」なのか、どのように実装されているのか、新しいクラスは古いクラスをどのように置き換えるのか、これらは説明にはありません。



魔法



それでは、成分を混ぜてポーションを手に入れましょう。 説明からレシピ自体に直接進みます。



interface IDo { public function doBaz(); public function doBar(); } class Foo implements IDo { public function doBaz() { /* do smth */ } public function doBar() { /* do smth */ } } class Baz implements IBaz { public function __construct(IDo $class) {} }
      
      





doBaz()



動作を変更し、新しいdoGood()



メソッドを追加するタスクが表示されます。 インターフェイスに新しいメソッドを追加し、 Foo



クラスにスタブを作成し、古いクラスの隣に新しいクラスを作成します。



 class FooFeature implements IDo { public function doBaz() { /* new code */ } public function doBar() { /* do smth */ } public function doGood() { /* do very good */ } }
      
      





素晴らしいですが、どのように機能を切り替えてクライアントコードに新しいクラスを実装するのでしょうか? これはService Locatorに役立ちます



File service.php





 if ($config->enableFooFeature) { //     : GET param, rand(),  .. $serviceLocator->set('foo', new FooFeature) } else { $serviceLocator->set('foo', new Foo) } $serviceLocator->set('baz', new Baz($serviceLocator->get('foo')));
      
      





BazクラスはFooに依存しています。 Service Locator自体が必要な依存関係を挿入します。開発者はロケーターからクラスを取得するだけです$serviceLocator->get('baz');







そして、超大国とは何ですか?



古いクラスを新しいクラスに置き換えることは、1つの場所で、ロケーターが使用されるアプリケーション全体で行われます。 精神的には、1つのクラスを別のクラスに置き換えるために、プロジェクト全体でnew Foo



Foo::doSmth()



を検索する必要がなくなったと想像できます。

このクラスまたはそのクラスがキーを使用してロケーターに取得する条件は何でも構いません。環境(dev、production)、GETパラメーター、rand()、時間などに応じた構成の設定です。



このような柔軟性により、1つのブランチで開発を行うことができます。これは、devとprodの両方です。 本番環境での設定では新機能がオフになっているため、合併や競合はありません。開発者は大胆にリポジトリにプッシュします。 開発中の機能は、他の開発者に表示されます。 特定の割合のユーザーで新しいコードがどのように動作するかを戦闘生産でテストしたり、特定のCookieを持つユーザーに対してのみ有効にしたりできます。 新しい機能をオン/オフする条件は、想像力によってのみ制限されます。 独創的な最適化をチェックして、使用する価値があるかどうか、パフォーマンスが向上するかどうか、どれだけかをすばやく確認できます。 新しいクラスが古いクラスを何も勝てないことが判明した場合は、それを削除して忘れてください。



プロダクションの新機能にバグがあることが突然判明した場合、ホットフィックスを書くために必死にロールバックしたり頭に戻ったりする必要はありません-それをロケーターに追加する条件をオフにして、ユーザーに安定したコードを含め、開発者がプロ​​ファイラーを有効にして問題を修正してコミットするだけですチェリーピックなし。 そのようなポーションでリリースする前に振るのは少なくなります:



画像



新しいクラスが最終的にテストされると、エッセンスを生成しないように古いクラスを完全に削除できます。 また、そのような開発コンセプトは、ビルドが同じブランチから収集される場合、Continious Integrationにより適しています。 グリーンビルド-生産は中断されません。アップロードすることができ、何もマージしたり、prodブランチでビルドを実行したりする必要はありません。 新しい機能の導入速度も向上しており、マスターが開発バージョンに遅れをとっているという問題はありません。



クライアントごとに異なるアプリケーション機能を持つプロジェクトを開発している可能性があります。 そのようなクライアントが少ない場合、各クライアントのアセンブリに抽象化による分岐を使用することも便利ですが、クライアントの成長に伴い、同様のクラスの数が増加します。 ある時点で、それらが多すぎて、ロケーターの構成が複雑すぎます。 この場合、顧客に応じてブランチを使用する方が便利かもしれませんが、各ブランチ内でスーパーポーションを使用することは誰も気にしません。



負の効果



多数のクラスは、このアプローチのマイナスに起因する可能性があります-常に新しい機能を追加し、リファクタリングし、仕事を終わらせなければ、プロジェクトを詰まらせるのは簡単です。 また、次の状況ではコードに優雅さが追加されません。



これらの問題は、クライアントコードを新しい環境にリファクタリングすることで解決されますが、新しい/安定したコードの切り替えの保存は失われます。



また、新しい機能を実装するためにコピーが作成されたクラスでバグが発見された場合にも、状況が発生する可能性があります。 2つの場所でエラーを修正する必要があります。



誰もこれを使用していますか?



はい。PaulHamantによると、このアプローチはFacebookとGoogleで実践されており、 Trunk Based Developmentと呼ばれています。 彼は彼のブログにこのトピックに関する多くの記事を持っています。読書に興味があるなら、 facebookgoogleについてです。



また、Chromiumの開発時には、チームは1つのトランクブランチとオン/オフフラグ機能を使用します。 膨大な数のさまざまなテスト(12kユニット、2k統合など)があるため、これによりトランクを地獄の悪魔に変えることはできず、リリースプロセスは非常に高い頻度でそれを維持するのに役立ちます。 これについては、 こちらの優れた記事をご覧ください



結論として、私はこのアプローチを私の実践に適用し、満足したと言います。 それを試してみてください。コード管理の作業量を減らし、生産性を高め、あなたを幸せにするでしょう。



All Articles