web-dev-qa-db-ja.com

一部のユーザーの機能を非表示/無効にする

アプリの無料版と有料版があるとしましょう。有料版は、ユーザーが利用できる機能に関する無料版のスーパーセットです。つまり、有料版は無料アプリのすべての機能に加えて追加機能を備えています。

起動時に読み込まれるフラグ(無料/有料など)に基づいて機能の可用性を切り替えるパターンはありますか?

どこにでも次のコードブロックがあるという考えは好きではありません。

if(isFreeVersion){
    // ...
} else {
    // ...
}

バージョンごとに2つのgitブランチを持つことはオプションではありません。これは、2つ(またはそれ以上)のコードソースを維持することを意味し、一般的に非現実的であるように思われ、ここで詳しく説明します。 バージョン管理で同じコードベース

これを行う方法はありますか?それでも、単一のコードベースがあり、無料/有料フラグをチェックする条件ステートメントでコードを散らかしていませんか?

これは以前に何度も議論されていたと思いますし、この問題に取り組むためのいくつかのパターンがあると確信していますが、それを見つけることができません。

Android/Javaを使用しています。

10
Tadija Bagarić

if(isFreeVersion)のような条件は、コード内で1回発生する必要があります。これはパターンではありませんが、その名前を知っていることは確かです。それは the DRY principal と呼ばれます。 "if(isFreeVersion) "がコードの複数の場所にあるということは、この行/そのロジックを繰り返したことを意味します。つまり、繰り返しを避けるためにリファクタリングする必要があります。

if(isFreeVersion)」は、さまざまな機能の内部構成オプションのリストを設定するために使用する必要があります。結果のコードは次のようになります。

_ if(isFreeVersion)
 {
      feature1Enabled=false;
      feature2Enabled=false;
      maxNoOfItems=5;
      advertisingStrategy=new ShowLotsOfAdvertisementsStrategy();
      // ...
 } 
 else
 {
      feature1Enabled=true;
      feature2Enabled=true;
      maxNoOfItems=int.MaxValue; // virtually unlimited
      advertisingStrategy=new ShowMinimalAdvertisementsStrategy();
 }
_

これは、単一の「isFreeVersion」フラグを異なる機能にマッピングします。機能コントロールがより複雑なパラメーター化を必要とする場合、個々の機能に個別のブールフラグを使用するか、または他の種類のパラメーター、たとえば共通のインターフェイスを持つ異なる戦略オブジェクトを使用するかをここで決定できます。

これで、無料バージョンと有料バージョンの内容を1か所で制御できるため、このロジックのメンテナンスが非常に簡単になります。 (DRYの原則に従うことにより)たくさんのif(feature1Enabled)ステートメントでコードが乱雑にならないように注意する必要がありますが、このチェックのメンテナンスはそうではありませんたとえば、既存の有料機能を無料にする場合(またはその逆の場合)、変更する必要があるものをより適切に制御できます。

最後に、Fowlerの feature toggles に関するブログ記事を見てみましょう。彼は、機能のエントリポイント/トグルポイントについて説明しています。一つの中心的なポイントを引用させてください:

新しい機能コードのすべてのコードパスをトグルで保護するのではなく、ユーザーをそこに誘導するエントリポイントのみに焦点を当て、それらのエントリポイントをトグルします。

したがって、全体的な戦略として、ユーザーインターフェイスに注目し、特定の機能を表示または非表示にするために必要な最小限のポイントにチェックを制限します。これにより、コードベースをクリーンに保ち、不要な混乱を防ぐことができます。

14
Doc Brown

if/elseブロックが気に入らない場合は、継承を使用するようにリファクタリングできます( を参照)条件付きのポリモーフィズム (Marin FowlerのRefactoringの本)から)。これは:

  • コードについて推論することを少し簡単にします。

  • 1つは無料バージョン用、もう1つは有料バージョン用の2つのクラスを作成できるようにします。これにより、他のクラスに呼び出しがディスパッチされ、無料バージョンと有料バージョンの区別が2つのクラスに制限されます(3つは基本クラス)。

  • 安価なバリアントやプレミアムバージョンなど、他の形式のソフトウェアを後で簡単に追加できます。別のクラスを追加し、コードでonceと宣言するだけで、コードベース全体が引き続き期待どおりに機能することがわかります。

9

Feature Toggle Pattern を適用すると、あなたの質問はかなりうまく解決できるようです。

よくあることですが、 Pete Hodgsonが1つの記事で説明しています このパターンを適用することで直面する可能性のあるすべてのシナリオです。

このパターンをサポートするいくつかのライブラリもあります。私は FF4J での作業の経験がありましたJavaで入力した場合、次のように推測します。

feature toggle <whatever language you prefer>

...どの検索エンジンでも、いくつかのソリューションが得られます。

6
danidemi

これを実現する方法は複数あります。シンプルで簡単な方法は、非常に多くの記事で提供されている Feature Toggle Pattern を使用することです。次のアプローチは、プラグ可能な機能の設計に関係しています。 AndroidとIOSにはアプリ内支払いがあります。その支払いに加えて、ダウンロードの可能性があります。

サーブレット、JAMES Mailets、さらにIDEプラグインを見ると、それらはすべてプラグインアーキテクチャの概念を使用しています。

  • アプリが使用方法を知っているインターフェースを定義します。そのインターフェースは、アプリケーションのナビゲーションやプラグインタッチポイントに他のアプリにそれ自体を注入する方法を提供する必要があります。
  • 起動時にアプリが読み取るパスを設定します(実行時のプラグイン管理ははるかに困難です)
  • プラグインが存在する場合(Java Jarファイル)など)、アプリはマニフェストを読み取ってプラグインインターフェースの実装を見つけるか、インターフェースを実装するクラスをスキャンします。
  • そのクラスが見つかると、インスタンス化され、適切なメソッドが呼び出されて、新しい機能が統合されます。

これにより、さまざまなユーザーがさまざまなクラスの機能を利用できるようになります。ユーザーが支払うのは機能だけです。

アプリケーションコードは1つのコードベースとして維持され、プラグインは個別のコードベースですが、プラグインに関連する部分のみが含まれます。アプリはプラグインが存在する場合にプラグインを処理する方法を知っており、プラグインはインターフェースと対話する方法しか知りません。

1
Berin Loritsch