質問

何度か遭遇したことの1つは、ヘルパー内部クラスのために過度に大きくなったサービスクラス(JBossサービスなど)です。私はクラスを打ち破る良い方法をまだ見つけていません。これらのヘルパーは通常スレッドです。次に例を示します。

/** Asset service keeps track of the metadata about assets that live on other
 * systems. Complications include the fact the assets have a lifecycle and their
 * physical representation lives on other systems that have to be polled to find
 * out if the Asset is still there. */
public class AssetService
{
  //...various private variables
  //...various methods

  public AssetService()
  {
    Job pollerJob = jobService.schedule( new AssetPoller() );
    Job lifeCycleJob = jobService.schedule( AssetLifecycleMonitor() );
  }

  class AssetPoller
  {
    public void run()
    { 
      // contact remote systems and update this service's private variables that
      // track the assets.
    }
  }

  class AssetLifecycleMonitor
  {
    public void run()
    {
      // look for assets that have meet criteria for a lifecycle shift
      // and update this service's private variables as relevant.
    }
  }
}

したがって、ヘルパーが2人いて、それらがまったく複雑な場合、クラスファイル全体が非常に大きくなる可能性があります。クラスがサービスによって完全に所有され、そのサービスを支援するためだけに存在することを明確にするという点で、内部クラスが好きです。クラスを分割して、親サービスを参照として渡すことを試みましたが、これは主に機能しますが、私が嫌いなものは次のとおりです:  

  • 最終的には、内部クラスが直接アクセスできるためセッターをまったく公開しなかったのに対し、壊れたクラスが変数にアクセスできるようにパッケージレベルのアクセサーを公開します。  
  • さらに、基礎となる変数ではなくアクセッサを常に呼び出しているため、少し冗長になります。軽微な許可。  
  • 便利なメソッド(例:checkAssetIsValid()など)は、ヘルパークラスがそれらを呼び出すことができるように、パッケージレベルの公開が必要になりました。  
  • さらに悪いことに、サービス実装クラスをヘルパークラスコンストラクターに渡す必要があります。これらのヘルパーメソッドは、サービスを実装するインターフェイスで公開したくないためです。これにより、ユニットテスト/モックの問題が発生する可能性があります。  
  • さらに悪いことに、私がやりたい同期は、外部の便利なメソッド(たとえば、ポーラーの更新中のlockDownAssets()など)を通じてリークされます。以前は、内部クラスはプライベートロックにアクセスできました。

    つまり、要するに、クラスを破壊すると、私が好きなカプセル化の一部が失われます。しかし、それらをそのままにしておくと、いくつかの大きなJavaファイルにつながる可能性があります。これに対処する良い方法をまだ見つけていません。 C ++には「友人」という概念がありました。私はめったに見逃していませんが、実際にはこの場合に役立ちます。

    思考?

        
  • 役に立ちましたか?

    解決

    バイトコードレベルでは、内部クラスは単なるJavaクラスです。 Javaバイトコード検証はプライベートメンバーへのアクセスを許可しないため、使用するプライベートフィールドごとに合成アクセサメソッドを生成します。また、内側のクラスをそれを囲むインスタンスにリンクするために、コンパイラーは外側の「this」に合成ポインターを追加します。

    これを考慮すると、内部クラスは単なる構文シュガーのレイヤーです。それらは便利であり、あなたはいくつかの良い点をリストしているので、あなたが考慮したいかもしれないいくつかの否定的な側面をリストします:

    • 内部クラスには、 whole 親クラスへの非表示の依存関係があります。これにより、受信インターフェイスが難読化されます。パッケージプライベートクラスとして抽出すると、デザインを改善し、メンテナンスしやすくすることができます。最初はより冗長ですが、多くの場合、次のことがわかります。
      • 10個のアクセサを公開する代わりに、実際に単一の値オブジェクトを共有したい。多くの場合、外部クラス全体への参照は実際には必要ないことがわかります。これはIoCでもうまく機能します。
      • 明示的なロックのためのメソッドを提供する代わりに、別のクラスでコンテキストを使用して操作をカプセル化する(または外側または以前の内側の2つのクラスのいずれかに移動する)方が保守しやすいです。
      • コンビニエンスメソッドは、パッケージプライベートユーティリティクラスに属します。 Java5の静的インポートを使用して、ローカルとして表示することができます。
    • 外部クラスは保護レベルをバイパスして、内部クラスのプライベートメンバーに直接アクセスできます。これ自体は悪いことではありませんが、デザインを表現する言語手段の1つを奪います。
    • 内部クラスは厳密に1つの外部クラスに埋め込まれているため、再利用する唯一の方法は外部クラスをサブクラス化することです。別の方法は、外部クラスが実装するパッケージプライベートインターフェイスへの明示的な参照を渡すことです。これにより、外部クラスをモックし、内部クラスをより適切にテストできます。
    • 最近のデバッガは非常に優れていますが、以前に内部クラスのデバッグで問題が発生しました(条件付きブレークポイントスコープの混乱、ブレークポイントで停止しないなど)
    • プライベートクラスはバイトコードを膨張させます。私の最初の段落を参照してください-多くの場合、使用して合成クラフの数を減らすことができるAPIがあります。

    PS私は非自明な内部クラス(特に、インターフェイスを実装しないクラス)について話しています。 3行のリスナー実装が適しています。

    他のヒント

    大規模なクラスを解散しようとしている理由を考慮することを忘れないでください。ソフトウェアエンジニアリング用ですか?例えば。それはプログラミングのホットスポットであり、開発者チームに複雑なマージを引き起こすほど大きなファイルがあるのですか?

    大規模なクラスを避けるのは単なる一般的な要望ですか?その場合、お持ちのコードの改善に時間を費やす方が良いかもしれません。

    コードの管理が難しくなっていますか?意図しない副作用のデバッグと回避を確実にすることはより困難になります。

    継続的な一貫した動作を保証するために単体テストを使用することに関するリックのコメントは非常に価値があり、良いアイデアです。現在の設計ではリファクタリングが排除されているだけで、元のインターフェイスから同じ動作を再実装した方がよい場合があります。たくさんの回帰テストの準備をしてください!

    カプセル化と分離の境界線は歩きにくい場合があります。ただし、ここでの主な問題は、クラスを分離するための基礎として使用するために、ある種の堅実な相互作用モデルが必要だと思います。

    副作用がない限り、多くの場所で使用される外部ヘルパーユーティリティクラスを持つことは合理的だと思います。また、よく整理されている限り、checkAssetIsValid()などのよく使用されるメソッドを含む静的ヘルパークラスを使用することも合理的です。これは、checkAssetIsValidが、渡しているオブジェクト以外の外部状態にアクセスする必要がないことを前提としています。

    分離のための最も重要なことは、これらのクラスの多くで永続的な参照を共有するオブジェクトを持たないことです。私は、ガイダンスのために関数型プログラミングに目を向けたいです。各クラスが他のクラスの内臓に到達して状態を変えてはなりません。代わりに、動作する各クラスがコンテナオブジェクトを生成および消費する必要があります。

    視覚化も非常に役立ちます。 Java Visualizationツールのトピックに関するスレッドに気づいたこちら。理想的には、クラスの相互作用図はグラフよりもツリーのように見えるはずです。

    また、大きなクラスを小さなクラスにリファクタリングすることは非常に難しいことに注意してください。何かを壊した場合にすぐに明らかになるように、少なくともパブリックインターフェイス用の単体テストのスイートを構築することをお勧めします。過去のテストで何時間も節約できたことは知っています。

    うまくいけば、これのいくつかが役に立つでしょう。私はちょっとここでとりとめのないようにしています。

    私は、内部クラスを過度に使用することを好みません。コードを通常のクラスに入れることで得られる利点(極端に使用した場合)は実際には何の利点ももたらさず、クラスファイルを不必要に大きくして従うのを難しくするだけです。

    いくつかのメソッドの可視性を高める必要がある場合、どのような害がありますか?それはあなたの抽象化やインターフェースを完全に破壊するでしょうか?プログラマーはデフォルトですべてをプライベートにする傾向がありすぎると思います。他のクラスにメソッドを呼び出させてもそれほど害はありません。デザインが本当にオブジェクト指向ベースの場合、つまり

    「内部ヘルパークラス」のすべてが同じメソッドのいくつかにアクセスする必要がある場合は、継承を介して共有できるように、それらを基本クラスに入れることを検討してください。

    はい。おそらく、それらのヘルパーをリファクタリングする必要があり、それらをすべてそのまま移動する必要はありません。いくつかのものはサービスに属し、他のいくつかはヘルパーに属します。データをカプセル化するには、おそらく新しいクラスを使用する必要があります。

    使用できる可能性の1つは、AOPを使用してきめ細かなアクセスを提供し、「友人」からのみメソッドを呼び出すことをポイントカットに含めることです。クラス。それでもメソッドは公開されます:(

    これには簡単な解決策はないと思います。

    ライセンス: CC-BY-SA帰属
    所属していません StackOverflow
    scroll top