静的メソッドを悪用していますか?


13

数ヶ月前、私は新しいプロジェクトで働き始めました。コードを調べてみると、使用されている静的メソッドの量が多くなりました。としてのユーティリティメソッドだけcollectionToCsvString(Collection<E> elements)でなく、多くのビジネスロジックも保持されます。

私はこの背後にある理論的根拠の責任者に尋ねたとき、彼はそれが春の専制政治から逃げる方法だと言った。この思考プロセスに何かがあります:顧客の領収書作成方法を実装するために、サービスがあります

@Service
public class CustomerReceiptCreationService {

    public CustomerReceipt createReceipt(Object... args) {
        CustomerReceipt receipt = new CustomerReceipt();
        // creation logic
        return receipt;
    }
}

さて、この男は、基本的にはクライアントクラスがSpring Beanでなければならないという制限を課しているため、Springによって不必要に管理されるクラスを持つことを嫌うと言いました。最終的にすべてをSpringで管理することになります。これにより、手順を踏まずにステートレスオブジェクトを操作する必要が生じます。ここに記載されている内容は多かれ少なかれhttps://www.javacodegeeks.com/2011/02/domain-driven-design-spring-aspectj.html

したがって、上記のコードの代わりに、彼は

public class CustomerReceiptCreator {

    public static CustomerReceipt createReceipt(Object... args) {
        CustomerReceipt receipt = new CustomerReceipt();
        // creation logic
        return receipt;
    }
}

私は可能な限りSpringがクラスを管理することを避ける点まで議論することができましたが、私が見ないのはすべてが静的であることの利点です。これらの静的メソッドもステートレスなので、あまりオブジェクト指向ではありません。私は何かにもっと快適に感じるだろう

new CustomerReceiptCreator().createReceipt()

彼は、静的メソッドにはいくつかの追加の利点があると主張しています。すなわち:

  • 読みやすい。静的メソッドをインポートすると、アクションを気にするだけで、どのクラスがそれを行っているのかは気にしません。
  • 明らかに、DB呼び出しのないメソッドなので、パフォーマンス面では安価です。潜在的なクライアントがコードを調べて確認する必要があるように、明確にするのは良いことです。
  • テストを簡単に記述できます。

しかし、これには完全に正しくない何かがあると感じているので、これに関するベテランの開発者の考えを聞きたいと思います。

だから私の質問は、このプログラミング方法の潜在的な落とし穴は何ですか?




4
上記のstaticメソッドは、通常のファクトリメソッドです。ファクトリメソッドを静的にすることは、多くの説得力のある理由から、一般的に受け入れられている慣習です。ここでファクトリメソッドが適切かどうかは、別の問題です。
ロバートハーヴェイ

回答:


23

違いは何だnew CustomerReceiptCreator().createReceipt()とはCustomerReceiptCreator.createReceipt()?ほとんどありません。唯一の大きな違いは、最初のケースの構文がかなり厄介であることです。どういうわけか静的メソッドを回避することでコードのオブジェクト指向が改善されるという信念の最初に従えば、あなたはひどく間違っています。単一のメソッドを呼び出すためだけにオブジェクトを作成することは、鈍い構文による静的メソッドです。

物事が異なるのは、注入するCustomerReceiptCreator代わりに注入するときですnew。例を考えてみましょう:

class OrderProcessor {
    @Inject CustomerReceiptCreator customerReceiptCreator;

    void processOrder(Order order) {
        ...
        CustomerReceipt receipt = customerReceiptCreator.createReceipt(order);
        ...
    }
}

これを静的メソッドバージョンと比較してみましょう。

void processOrder(Order order) {
    ...
    CustomerReceipt receipt = CustomerReceiptCreator.createReceipt(order);
    ...
}

静的バージョンの利点は、システムの他の部分とどのように相互作用するかを簡単に説明できることです。そうではありません。グローバル変数を使用していなければ、システムの残りの部分は何も変更されていないことがわかります。ここでは、システムの他の部分がレシートに影響を与えないことを知っています。不変オブジェクトを使用した場合、順序は変更されておらず、createReceiptは純粋な関数です。その場合、他の場所でランダムな予測不可能な影響を心配することなく、この呼び出しを自由に移動/削除/などできます。

を注入した場合、同じ保証を行うことはできませんCustomerReceiptCreator。呼び出しによって内部状態が変更される可能性があり、他の状態の影響を受けるか、変更される可能性があります。順序を変更すると驚くべきバグが発生するように、関数内のステートメント間に予測できない関係がある場合があります。

一方、CustomerReceiptCreator突然新しい依存関係が必要になった場合はどうなりますか?機能フラグをチェックする必要があるとしましょう。注入する場合、次のようなことができます。

public class CustomerReceiptCreator {
    @Injected FeatureFlags featureFlags;

    public CustomerReceipt createReceipt(Order order) {
        CustomerReceipt receipt = new CustomerReceipt();
        // creation logic
        if (featureFlags.isFlagSet(Flags::FOOBAR)) {
           ...
        }
        return receipt;
    }
}

これで、呼び出しコードにaが挿入され、a CustomerReceiptCreatorが自動的に挿入されますFeatureFlags

静的メソッドを使用していた場合はどうなりますか?

public class CustomerReceiptCreator {
    public static CustomerReceipt createReceipt(Order order, FeatureFlags featureFlags) {
        CustomerReceipt receipt = new CustomerReceipt();
        // creation logic
        if (featureFlags.isFlagSet(Flags::FOOBAR)) {
           ...
        }
        return receipt;
    }
}

ちょっと待って!呼び出しコードも更新する必要があります。

void processOrder(Order order) {
    ...
    CustomerReceipt receipt = CustomerReceiptCreator.createReceipt(order, featureFlags);
    ...
}

もちろん、これはまだどこの質問葉processOrderの取得FeatureFlagsからを。運がよければ、トレイルはここで終わります。そうでなければ、FeatureFlagsを通過する必要性がさらにスタックに押し上げられます。

ここにはトレードオフがあります。依存関係を明示的に渡す必要がある静的メソッドは、より多くの作業をもたらします。注入されたメソッドは作業を減らしますが、依存関係を暗黙的にするため、コードを推論するのが難しくなります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.