このコード構造は何らかの点で有益ですか?


8

私は最近、Java Webアプリケーションプロジェクトに夢中になり、このタイプの形式に従う多くのクラスに遭遇しました。

public class MyThingy {
   private final int p1;
   private final String p2;
   

   public MyThingy (int p1, String p2, …) {
      this.p1 = p1;
      this.p2 = p2;
      
   }

   public static void doSomething(int p1, String p2, …) throws Throwable     {
      final MyThingy myThingy = new MyThingy(p1, p2, …);
      myThingy.execute();
   }

   private void execute() throws Throwable {
      //do stuff 
   }
}

これは次のコードで達成できるように思えます。

public class MyThingy {

   public static void doSomething (int p1, String p2, …) throws Throwable {
      //do stuff 
   }

}

最初の方法でそれを実行することでわかる唯一の利点は、execute()をより小さな部分に分割しなければならない場合、明示的に渡さなくても、すべて初期パラメーターを共有できることです。しかし、リーダーがどのメソッドにどのパラメーターが必要か、いつ値が変更される可能性があるか(グローバル変数のように)を伝えるのが難しくなるため、これはレイジーコーダーにのみメリットがあります。

行方不明のものはありますか?スレッド、パフォーマンス?

編集:私は言及したはずですが、コンストラクタはパブリックですが、呼び出されません。唯一の使用法は次のとおりです。

MyThingy.doSomething(p1, p2...);

これ自体はテストに問題があることを除けば、execute()のロジックを直接doSomething()に入れない理由はありません。静的関数を取り除いたとしても、コンストラクタはまだ意味がありません。パラメータは、それらを使用するメソッドに直接渡す必要があると思います。


会社の標準が静的関数を回避することについて何か言っていると思いませんか?または、このコードではどのような自動テストが行​​われていますか?静的関数が導入されると、モッキングはほとんど不可能になります。
KChaloux 2015年

3
メモと同じようにthrows Throwable、これは悪い習慣throws Exceptionです。可能であれば、少なくとも、またはより具体的なものにする必要があります。そして、悪い習慣は通常一緒になるので、このコードテンプレートは単なるもう一つの悪い習慣だと思います。
スクリプト

静的メソッドは、1回の呼び出しでnew(...)+ を実行するためのヘルパーにすぎませんexecute()
Kasey Speakman、2015年

あなたはどちらの方法でもそれを変えることができます:これは醜い解決策です。オブジェクトの作成と消費を分離することをお勧めします。
Thomas Junk

1
また、作成者は構築されたオブジェクトが不変であることを明らかに意図していたことにも注意してください(データはですfinal)。すべてのメンバーがプライベートであるため、静的メソッドはオブジェクトへの唯一のAPIです。あなたが共有しているだけのコードでは、私が作る上で、これにどんなメリットが表示されないexecuteP1、P2、...服用静的メソッドを
ケイシースピークマン

回答:


2

この問題についてのあなたの直感は正しいと思います。

  • ここにあるのは、実装がクラスに「分解」された単純な(この場合は静的)メソッドです。
  • 「クラスとしてのメソッド」手法は、明示的に渡さなければならない変数の数を減らし、実際には、「グローバル」変数であるものを使用してデータがどのように流れるかを不明瞭にします。
  • インスタンスは使い捨てであり、その状態は一時的なものであるため、これは実際にはOOの精神に沿ったものではありません。
  • それでも、これは一般的なパターンであり、特にJavaの入門書ではよく見られ、Javaデザイナー(クラスを宣言する多くの方法を導入し、この使用法を支援するように思われる)の頭に浮かんだかもしれません。

それで、あなたはそれを使うべきですか?

  • もちろんです。

実際、問題となっているのは純粋な「クラスとしてのメソッド」だけではありません。通常のクラスでは、パブリックメソッドの呼び出しの間に状態を保持しないフィールドを作成したくなるかもしれません。これらは、プライベートメソッド間でのパラメーターの受け渡しを回避するためにのみ使用されます。以前、私はそれを絶対に避けようとしましたが、その後、長いパラメーター・リストも不便であることに気付きました。

純粋な「メソッドクラス」は、多くの精神的な手荷物(潜在的な可能性が非常に高い)に付属していると思うので、避けるようにしています。(私は通常のクラスの使い捨てフィールドにも頼っています。)しかし、渡す変数が大量にある場合は、コードのクリーン度を向上させる価値があります。

意味のあるときにクラスを使用するようにしていますが、実際にはクラスを想像するのは簡単です。おそらく将来的には、追加の実装でクラスを拡張するでしょう。おそらく、インスタンスは、初期化して渡したい関数オブジェクトと考えることができます。そして、クラスは疑似オブジェクト指向の「メソッドクラス」ではなくなりました。


1
ここの最後の段落であなたが提案していることが好きです。execute()メソッドがパブリックの場合、オブジェクトを作成(および初期化)して、後で何かを実行させることができます。これは理にかなっており、テスト容易性も向上させるでしょう。その場合、それは実際には無関係な静的関数です。
ミシェル2015年

これが、この質問が待ち望んでいた答えです。+1
cbojar 2015年

6

静的メソッド呼び出しを介した追加の間接参照により、オブジェクトの作成方法に関する懸念が、オブジェクトを使用するコードから分離されます。ここにあるものは、単純なファクトリメソッドに非常に似ています(実際にはオブジェクトを返しませんが、オブジェクト作成の間接指定は同じです)。

これは、作成されるオブジェクトのタイプを制御する必要がある場合に役立ちます。この単純な例では決定を下す必要はありませんが、そのロジックを追加するのは簡単です。

静的コードが呼び出し元に意味することは、「この状態で何かをする必要があるが、委任する方法がわからない」ということです。静的メソッドは、提供されたオブジェクトに基づいて適切な実装に委任できます。

おそらく単体テストで実行しているときに、モックオブジェクトが使用されています。おそらく、パラメータに基づいて、必要な処理を実行するための異なるアルゴリズムを実装する別のオブジェクトを入れ替えることができます。そのコードを1つの場所にローカライズすることにより、多くの場所ではなく、1つの場所で決定を下すことができます。


あなたの質問がどのように表現されているか、そして静的メソッドで構築決定が行われていないという事実を考えると、これは単なる過剰設計の場合かもしれないと感じています。ほとんどのコードは、ファクトリーに委任する必要はありません。静的なファクトリーのようなメソッドが行うことを期待しているように、何も決定を行わないこのようなコードに遭遇し続けるという事実は、誰かがGoFの本を読んで無茶苦茶に実行したことを教えてくれます。


1
Factoryメソッドはどこにありますか?OPの例は何も返しません。
Robert Harvey

@RobertHarvey:事実上、ラップされたファクトリメソッドです。コールサイトに公開されていないからといって、工場ではないというわけではありません。
オービットのライトネスレース

1
@RobertHarveyそれは言葉の選択の問題だと思います。ファクトリメソッドと同様に動作しますが、実際には何も返しません。それから、私はそれがそうしたと主張したことはありません、それが委任したことだけです、それはまだ正確な声明です。

@RobertHarveyに同意します。メソッド内で完全に構築および使用されるオブジェクトは、ファクトリー製品ではなく、実装の詳細です。
cbojar 2015年

面白い。私は私の答えの用語を変更しましたが、私がした点は同じです。奇妙なことが起こった...

0

さて、2番目の例(完全なコードを表示)が次のようになっているとします。

public static void doSomething(int p1, String p2, …) throws Throwable     {
      final MyThingy myThingy = new MyThingy(p1, p2, …);
      myThingy.execute();
   }

その後、コンストラクタが必要になります

public MyThingy (int p1, String p2, …) {
      this.p1 = p1;
      this.p2 = p2;
      
   }

順番に設定します

   private final int p1;
   private final String p2;

そして今、あなたはあなたが始めたところに戻っています。

もちろん、コンストラクタを削除して、代わりにゲッターとセッターを使用することもできます

private final int p1;
private final String p2;

public void SetP1(int p1)
{
    this.p1 = p1;
}

public int GetP1()
{
    return p1;
}
// repeat for p2

...しかし、静的メソッドを呼び出す前に最初にセッターを呼び出すための数行のコードが必要になるため、これは静的メソッドの呼び出しを複雑にします。コンストラクター呼び出しを使用する1行で十分です。

もちろん、これはメンバー変数がそうでない場合にのみ機能します final。したがって、引き続きコンストラクタが必要になります。


final変数にパブリックセッターを設定しても意味がありません。初期化できるのは1回だけです。
Kasey Speakman

1
@KaseySpeakman:Ergo、まだコンストラクタが必要です。
Robert Harvey

私はこの答えが質問に話すことを完全に確信していません...
cbojar

さらに、それは有効ではありません(コンパイルエラーがあります):ideone.com/0nDRgY ---最終値は、他のどこにも設定できないことが証明できなければなりません。セッターがあると、それが不可能になり、コンパイルエラーが発生します。

@MichaelT:私はすでに私の答えで(最後の段落で)言った。
Robert Harvey

0

通常、クラスには多くのインスタンスメソッドがあり、オブジェクトが作成されると、executeの実装はこれらすべてのインスタンスメソッドを利用できます。オブジェクトの作成を拒否すると、これらのインスタンスメソッドはすべて役に立たなくなります。


OPは以外にインスタンスメソッドはないと言っていると思いますexecute。したがって、なぜ全体をインライン化しないのかと尋ねます。
cbojar 2015年

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