9

Status:Fendy と Glen Best の回答はどちらも同じように受け入れられ、尊重されますが、一方が受け入れられて賞金が与えられる可能性があるため、Fendy の回答を選択します。

Scenario:

多くのクラス何度も再利用する必要があるコード(明らかな小さなパラメーターの変更はめったにありません) と同時実行スレッドがある場合、どのアプローチを採用しますか?

再利用する必要があるコードは、(静的および非静的コンテキストを適切に考慮し、メソッド作成手法を考慮して) 適切なものであれば何でもかまいません。これは、アルゴリズム、接続、操作、クローズを行う DB メソッドの場合があります。なんでも。

  1. いくつかのクラスを似たものにして、それらすべてのメソッドをその中MyMethods.class入れます

    1.a. メソッドを作成し、(すべてのクラスと同時スレッドによって) を次のように直接呼び出します staticMyMethods.someMethod();

    1.b. メソッド non-static作成し、それらを呼び出す時点で、instantiateクラス全体MyMethods mm = MyMethods(); mm.someMethod();

  2. https://en.wikipedia.org/wiki/Strategy_patternに記載されている戦略パターンを使用します(コードはここに添付されています)。

  3. https://en.wikipedia.org/wiki/Dependency_injection#Javaに記載されている依存性注入を使用する

Problems:

  1. 一部の人々は、ユニット テスト http://en.wikipedia.org/wiki/Unit_testing はこのアプローチでは不可能であり、後者を交換する際に問題が発生すると言うでしょう。クラスをテストし、依存関係のモック バージョンを使用する場合

    1.a. 同時呼び出しや複数のクラスに問題はありますか? 例として特別にJDBC static methods

    1.b. クラス全体が1つまたは2つのメソッドを呼び出すだけで何度も発生するため、メモリ負荷がかかりすぎると思いますinstanticated

  2. それは私の頭をはるかに超えています。それと、または利点/欠点を説明してください

  3. この質問のコンテキストでフレームワークを使用したくありません..それは私の頭をはるかに超えています。それと、または利点/欠点を説明してください

  4. もしあれば、他の戦略や推奨事項を待っています。

Request:あなたが経験豊富で、その意味を深く知っていて、あなたの答えが私とコミュニティ全体を包括的に助けることができる場合にのみ、答えてください!

Code:

/** The classes that implement a concrete strategy should implement this.
* The Context class uses this to call the concrete strategy. */
interface Strategy {
    int execute(int a, int b); 
}

/** Implements the algorithm using the strategy interface */
class Add implements Strategy {
    public int execute(int a, int b) {
        System.out.println("Called Add's execute()");
        return a + b;  // Do an addition with a and b
    }
}

class Subtract implements Strategy {
    public int execute(int a, int b) {
        System.out.println("Called Subtract's execute()");
        return a - b;  // Do a subtraction with a and b
    }
}

class Multiply implements Strategy {
    public int execute(int a, int b) {
        System.out.println("Called Multiply's execute()");
        return a * b;   // Do a multiplication with a and b
    }    
}

// Configured with a ConcreteStrategy object and maintains
// a reference to a Strategy object 
class Context {
    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public int executeStrategy(int a, int b) {
        return this.strategy.execute(a, b);
    }
}

/** Tests the pattern */
class StrategyExample {
    public static void main(String[] args) {
        Context context;

        // Three contexts following different strategies
        context = new Context(new Add());
        int resultA = context.executeStrategy(3,4);

        context = new Context(new Subtract());
        int resultB = context.executeStrategy(3,4);

        context = new Context(new Multiply());
        int resultC = context.executeStrategy(3,4);

        System.out.println("Result A : " + resultA );
        System.out.println("Result B : " + resultB );
        System.out.println("Result C : " + resultC );
    }
}
4

3 に答える 3

8

あなたの質問には、実際には2つの意味があります。

多くのクラスで何度も再利用する必要がある

これは、設計パターン (再利用可能なコンポーネント) またはメモリ コスト (クラスのインスタンス化) のコンテキストである可能性があります。2 つの異なる視点から話す:

メモリコスト(これについてはほとんど経験がありませんでしたが、私の経験を共有させてください)

このセクションでは、実際には 2 種類のインスタンス化のみを扱います。

最初は静的 (または構成ルートでの DI インスタンス化) です。

  • 積極的なインスタンス化。アプリケーションの起動時にすべてのクラスがインスタンス化されることを意味します
  • 一度だけのインスタンス化

非静的

  • 遅延インスタンス化。クラスが必要な場合にのみインスタンス化されることを意味します
  • 使用ごとに 1 回のインスタンス化

つまり、クラスが多い場合は static のコストが高くなり、リクエストが多い場合 (for ループ内など) は非 static のコストが高くなります。ただし、アプリケーションが重くなるべきではありません。ただし、Java / csharp のほとんどの操作はオブジェクトを作成します。

クラスの再利用性

1 - メガ モノリシック コード (ほぼすべてを実行できる 1 つの神のクラス)

利点:

  1. コードを簡単に検索できます (ただし、状況によって異なります)。すべてのロジックがそこにあることがわかっているので、その大きなクラスを確認するだけで済みます。
  2. 静的な場合は、インスタンス化を気にせずにどこでも呼び出すことができます

短所:

  1. 1 つのメソッドを変更すると、他の場所でエラーが発生するリスクが生じます
  2. SRP に違反しています。つまり、このクラスは 1 つの理由だけでなく、さまざまな理由で変更される可能性があります。
  3. 特にバージョニングでは、別々のブランチで変更が発生するとマージが難しくなり、コードの同期に手間がかかります

1a / 静的クラス / シングルトン パターン

利点:

  1. 使いやすい
  2. どこでも使用可能 (参照して呼び出すだけで完了)
  3. オブジェクトをインスタンス化する必要はありません

短所:

  1. 単体テストが難しい (モック化が難しく、後でテスト環境を準備するのに時間がかかることがわかります。特にデータの場合)
  2. ステートフル (状態がある) 場合、デバッグ中に現在の状態を判断するのは困難です。さらに、どの関数が状態を変更するかを判断するのは難しく、どこからでも変更できます
  3. 多くのパラメータを持つ傾向があります (おそらく 5 ~ 11 程度)

静的クラスに関するいくつかのポイント:この質問を参照してください

2 作戦パターン

実はこれは 3 や と同じデザインcomposition over inheritanceです。

3 依存性注入

利点:

  • モックと単体テストが簡単
  • Must be stateless. クラスがステートレスであると、デバッグと単体テストが容易になります
  • リファクタリングされるサポート

不利な点:

  • インターフェイスに慣れていない人にとってはデバッグが困難です (メソッドにリダイレクトするたびに、インターフェイスに移動します)
  • マッピングにつながるレイヤリングを作成します

ステート/ステートレス

状態は、アプリケーションの設計において重要なルールを果たしていると思います。通常、開発者は、ビジネス ロジック コードに次のような状態を持たないようにします。

// get data
if(request.IsDraft){
  // step 1
  // step 2
}
else{
  // step 1
  // step 3
}

stateless開発者は、ロジックを他のクラス、または少なくとも次のようなメソッドに配置する傾向があります。

// get data
if(request.IsDraft){
    draftRequestHandler.Modify(request);
}
else{
    publishedRequestHandler.Modify(request);
}

可読性が向上し、変更や単体テストが容易になります。state pattern or hierarchial state machine pattern特にこのような場合を処理するための設計パターンも 1 つあります。

単一責任の原則

IMHO、この原則に従えば、最もメリットがあります。利点は次のとおりです。

  1. バージョン管理では、どのクラスが変更されたか、およびその理由について変更が明確です
  2. DI では、いくつかの小さなクラスを結び付けることができるため、使用法と単体テストの両方で柔軟性が生まれます。
  3. モジュール性、低カップリング、高結束の向上

TDD (テスト駆動開発)

この設計は、コードにバグがないことを保証するものではありません。設計段階で時間がかかり、レイヤー化の手間がかかるという短所がありますが、次の利点があります。

  1. 単体テスト用のオブジェクトを簡単にモックできる
  2. 単体テストとモジュール性によりリファクタリングが容易
  3. 保守/拡張が容易

いくつかの有用な情報源

サービス ロケータ アンチ パターン

分野横断的な関心事にデコレーターを使用する

私の2セント:

インターフェイスを使用する利点 (継承の構成にも適用されます)

トップダウン設計・DI設計を行う

最終的な考え

これらの設計と戦略は、アプリケーションの構造を決定する鍵ではありません。それを決定するのはやはり建築家です。最適な構造を決定するよりも、SOLID、KISS、GRASP などのいくつかの原則に従うことを好みます。Dependency Injection はこれらの原則のほとんどに従っていると言われていますが、抽象化が多すぎてコンポーネントの設計が正しくないと、シングルトン パターンの誤用と同じ結果になります。

于 2013-06-26T12:53:42.347 に答える
3

1.a. メソッドを静的にし、MyMethods.someMethod(); として (すべてのクラスと同時実行スレッドによって) 直接呼び出します。

1.b. メソッドを非静的にし、呼び出し時にクラス全体を MyMethods mm = MyMethods(); でインスタンス化します。mm.someMethod();

これら 2 つのどちらを選択するかは、 の機能によって異なりますMyMethods.classMyMethodsステートレスであることが想定されている場合は、staticメソッドを使用するのが適切なアプローチです。それ以外の場合、あるメソッド呼び出しが別のメソッド呼び出しに依存し、MyMethods状態 (つまり、非最終フィールド) がある場合は、2 番目のオプションを使用します。

https://en.wikipedia.org/wiki/Strategy_patternに記載されている戦略パターンを使用します(コードはここに添付されています)。

MyMethodsさまざまな目的のためにさまざまなクラスで拡張する場合、およびコンテキストに応じて実行するコードを選択する場合は、このパターンを使用します。ウィキが言うように、使用するアルゴリズムがランタイムの前にわからない場合 (いくつかの条件によって異なります)、これが進むべき道です。あなたの仕様によると、MyMethodsそのような問題はありません。

https://en.wikipedia.org/wiki/Dependency_injection#Javaに記載されている依存性注入を使用する

上記と同じ答え。依存性注入とは、制御の反転にあります。を使用するクラスはMyMethods、 の実際の実装については知りませんが、実際の実装のMyMethods注入は、より高いレベルの権限に委任されます。使用されるコンテキストから外部依存関係を抽象化します。繰り返しにMyMethodsなりますが、ステートレスで一定である (変更する予定がなく、クラス内のメソッドの動作が使用されるコンテキストに依存しない) 場合、これらのパターンは必要ありません。オーバー エンジニアリングを意味するだけだからです。

ロジックが実行元のコンテキストに依存する場合は、戦略またはDIパターンを使用する必要があると結論付けます。MyMethodsこれが一定である場合 (たとえば、Java のクラスは、誰が、どのようなコンテキストで を呼び出すか、またはMathを呼び出すかを気にしない)、静的メソッドが適しています。sqrt()max()pow()


問題について:

MyMethodsメソッドで使用する場合、あなたが述べた問題は存在しませんstatic。メソッドが特定の引数に対して正しい値を返すかどうかをテストする必要があります。それだけです。Strategy パターンStrategyの実際の実装や、Dependency Injection によって注入されるインターフェイスの実装をテストするのに、これ以上問題があるとは思いません。ストラテジーを使用するクラスをテストするのは難しいかもしれません。ユーザー入力に依存することが多いため、特定のストラテジーが実行されるコンテキストを再作成するのは簡単ではない場合があるためですが、不可能ではないことは間違いありません。私の知る限り、依存性注入はテストに最適です。なぜなら、テスト対象のユニットを、簡単にモックできる依存性から分離できるからです。

于 2013-06-26T08:51:40.387 に答える
3
  1. 主な質問: コードの再利用

    多くのクラスで何度も再利用する必要があるコード (明らかな小さなパラメーターの変更はめったにありません) と同時スレッドがある場合、どのアプローチを採用しますか?

    カット&ペーストを考えていないので、次のことを意味していると思います:

    ... 多くのクラスで何度も再利用されます ...

    あなたが求めているのは、特別なことでも具体的なことでもありません。単一のアプリ内または複数のアプリ間で、コードが再利用されるのはよくあることです。一般的な答えは、オブジェクト指向の設計/プログラミングを使用することです。コードをクラスに入れ、オブジェクトをインスタンスとして作成し、オブジェクトを呼び出します...

    1a. 静的メソッドによる再利用:

    メソッドを静的にし、MyMethods.someMethod() として直接 (すべてのクラスと同時スレッドによって) 呼び出します。

    • クラスがステートレス (インスタンス変数なし) の場合、これは優れたアプローチです。
    • クラスにクラスレベルの状態 (静的インスタンス変数のみ) があるが、変数が読み取り専用 (不変) である場合、これは適切なアプローチです。
    • クラスにクラスレベルの状態 (静的インスタンス変数のみ) があり、変数の値が変化する (変更可能) 場合、これは適切なアプローチです。ただし、複数のスレッドからクラスにアクセスできるようにする場合は、クラスをスレッドセーフにする必要があります。つまり、メソッドを同期させるか、すべてのデータの読み取りと書き込みに対して同期 (相互排他的なスレッド アクセス) を行う内部コードを用意します。
    • コードにオブジェクト レベルの状態 (非静的インスタンス変数) がある場合、このアプローチは機能しません。オブジェクトをインスタンス化せずに非静的インスタンス変数にアクセスすることは不可能です。

    1b. オブジェクトのインスタンス化による非静的メソッドによる再利用:

    メソッドを非静的にし、呼び出し時にクラス全体を MyMethods mm = MyMethods(); でインスタンス化します。mm.someMethod();

    • クラスに静的インスタンス変数しかない場合、オブジェクトをインスタンス化しても何も達成されないため、これは不適切なアプローチです。
    • クラスに非静的インスタンス変数がある場合 - これが唯一のアプローチです。変数にアクセスするには、オブジェクトをインスタンス化する必要があります。
    • インスタンス化されたオブジェクトを複数のスレッドで使用する場合は、次のようにする必要があります (優先順)。
      • ステートレス (インスタンス変数なし) - 実際には 1a のオプションです - インスタンス化する必要はありません
      • 不変 (読み取り専用の非静的インスタンス変数)
      • すべてのデータの読み取りと書き込みで同期
  2. 戦略パターンを使用する

    戦略パターンは良い練習になります。しかし、それはあなたの全体的な質問とはほとんど関係がありません. 戦略パターンは特定の理由で使用されます。呼び出し元に影響を与えることなく、アルゴリズム/処理ロジックの実装を「オンザフライ」で交換するためです。

  3. 依存性注入を使用する

    依存性注入は、次の理由で使用されます。

    • ファクトリおよびオブジェクト キャッシュ機能: オブジェクトの作成、キャッシュ、およびルックアップに対するコードの責任を取り除きます
    • オブジェクト共有の仲介: さまざまなクラスが同じオブジェクト インスタンス (特定のスコープ/コンテキストに格納されている) を共有できるようにするため、2 つのクラス間でオブジェクトを直接受け渡す必要はありません。
    • オブジェクト インスタンス間の「ワイヤリング コントロール」 - オブジェクトの関連付けを設定し、CDI の下で、インターセプター、デコレーター、およびオブザーバー パターンをサポートします。

    適切に使用すれば、これは非常に良い習慣になります。あなたの場合、これはオプション1bでのみ適用できます。依存性注入は、オブジェクトのインスタンス化と変数へのプロビジョニングに関するものです。

問題:

  1. 一部の人々は、単体テストは不可能だと言うでしょう

    • モック フレームワーク (およびハンド コーディングされた単体テスト) は、常にクラスをモック ロジックに置き換える処理を行います。ごく普通のシナリオです。最終的なパブリック メソッドがない場合は、クラスを拡張してそのロジックをモックできます。また、メソッド宣言をインターフェイスに転送し、クラスにインターフェイスを実装させてから、インターフェイスを別のクラスで実装してモックすることもできます。
    • 言い換えれば、これはあなたのオプションに影響を与える制約/力ではありません

    1a. 上記を参照

    1b. メモリ負荷

    1つまたは2つのメソッドを呼び出すだけでクラス全体が何度もインスタンス化されるため、メモリ負荷が大きくなりすぎると思います

    小さな問題。各オブジェクト インスタンス (インスタンス変数) 内のデータに応じて、各オブジェクト インスタンスは 12 バイトから数メガバイトまで小さくなりますが、通常は下端に向かって傾斜します (多くの場合 < 1kB)。クラスコード自体のメモリ消費量は、クラスがインスタンス化されるたびにレプリケートされません。

    もちろん、必要に応じてオブジェクトの量を最小限に抑えることをお勧めします。使用可能なインスタンスが既にある場合は、新しいインスタンスを作成しないでください。より少ないオブジェクト インスタンスを作成し、それらをアプリ全体で共有して、コンストラクター メソッドとセッター メソッドに渡します。依存性注入は、オブジェクト インスタンスをコンストラクター/セッターに渡すことなく "自動的に" 共有するための良い方法です。

  2. 上記を参照

  3. 上記を参照

于 2013-07-04T05:06:51.307 に答える