2

私は最近、Robert C. Martin (別名 Uncle Bob) の本を非常に熱心に読んでいます。彼が話していることの多くは、私にとって本当に命の恩人であることがわかりました (関数のサイズが小さい、物事の非常にわかりやすい名前など)。

まだ解決できていない問題の 1 つは、コードの結合です。私が何度も抱えている問題の 1 つは、たとえば配列をラップするようなオブジェクトを作成することです。あるクラスでいくつかの作業を行いますが、別のクラスで作業を行うには別のクラスを呼び出す必要があります。同じデータを 3 ~ 4 レベルの深さまで渡していますが、このオブジェクトが渡されるすべての場所を把握するのは難しいため、意味がないように思われます。私にはたくさんの依存関係があります。これは良い習慣ではないようです。

誰かがこれに対処するより良い方法を知っているかどうか疑問に思っていました.Bobのアドバイスは(おそらく私が誤解しているのですが)、より多くのクラスを作成する必要があるため、状況を悪化させているようです. ありがとうございます。

編集:実世界の例をリクエストする(そうでなければ、それを理解するのは難しいことに完全に同意します):

class ChartProgram () {
int lastItems [];

  void main () {
  lastItems = getLast10ItemsSold();
  Chart myChart = new Chart(lastItems);
  }
}
class Chart () {
  int lastItems[];
  Chart(int lastItems[]) {
  this.lastItems = lastItems;
  ChartCalulations cc = new ChartCalculations(this.lastItems);
  this.lastItems = cc.getAverage();
}
class ChartCalculations {
  int lastItems[];
  ChartCalculations (int lastItems[]){
  this.lastItems = lastItems;
  // Okay so at this point I have had to forward this value 3 times and this is 
  // a simple example. It just seems to make the code very brittle
  }
  getAverage() {
  // do stuff here
  }

}
4

5 に答える 5

1

サンプル コードを見ると、Chart クラスは ChartCalculation クラスと密接に結合されています (新しいインスタンスをインスタンス化しています)。Chart クラスがインターフェイス (私のサンプル コードでは IChartCalculation) を介して ChartCalculation を使用する場合、この結合を取り除くことができます。Chart クラスで使用される IChartCalculation の具体的な実装は、コンストラクタ (依存性注入)、おそらくファクトリ クラスによって Chart クラスに渡すことができます。別の方法として、 IOCフレームワークを使用することもできます(ただし、必要以上に複雑になることがよくあります)。このように、Chart クラスは IChartCalculation のさまざまな具体的な実装を使用でき、インターフェイスにコーディング/実装されている限り、両方とも独立して変更できます。

class ChartProgram () 
{
    int lastItems [];

    void main () {
    lastItems = getLast10ItemsSold();
    Chart myChart = new Chart(lastItems, new ChartCalculations());
}

class Chart
{
    int[] lastItems;

    IChartCalculation chartCalculations;

    public Chart(int[] lastItems, IChartCalculations cc)
    {
        this.lastItems = lastItems;
        chartCalculations = cc;
        int average = cc.GetAverage(lastItems);
    }
}

interface IChartCalculation
{
    int GetAverage(int[] lastItems);
}


class ChartCalculation : IChartCalculation
{
    public int GetAverage(int[] lastItems)
    {
        // do stuff here
    }
}
于 2009-11-24T16:56:19.733 に答える
1

(比較的)複雑なソフトウェア開発との結合を誤解しているようです。

クラスを介して、または複数のクラスを介して消費するためにデータを渡す場合、消費者ができることは、データを使用してクラスのパブリック インターフェイスにアクセスすることだけです。インターフェイス定数がある限り、何も壊さずにこれらのクラスを内部で実装する方法を変更できるため、カップリングに関してはこれで問題ありません。指定されたパブリック インターフェイス以外に依存しない限り、データには多くのクライアントが存在する可能性があります。

クラスのプライベート メンバーにアクセスした場合、または別のクラスが公開してサード パーティによって変更される配列の参照を渡した場合など、結合に問題が発生します。

ただし、そのパブリック インターフェイスを変更したい場合や、クラスが多くの場所で使用されている場合でも、パラメーターを渡す代わりにコンポジションを使用したとしても、いずれにせよ発生するため、最も重要なことは、パブリック インターフェイスを十分に設計することです。そのため、変更は一般的ではありません。

要約すると、これは設計上の問題を示している場合がありますが (より良い設計は、それほど多くのデータを渡す必要のないクラス階層に変換される可能性があります)、それ自体は悪いことではなく、場合によってはそれも必要です。

編集: まず第一に、CartCalculations は本当に必要ですか、それとも何らかの規則に従うためだけにそのクラスを作成していますか? 次に、CartCalculations が保証されている場合、CartItems の代わりに int[] を渡しているのはなぜですか? 最後に、それがもろいと感じるのはなぜですか?パラメーターを渡すのを忘れる可能性があるためです (コンパイル エラー、大したことはありません)。誰かがすべきではないリストを変更する可能性があるため (データをロードする唯一の CartItems を持つことである程度制御可能)? アイテムの表現方法を変更する必要がある場合 (そのような変更を行うことができるクラスで配列をラップしても大したことはありません)。

したがって、このすべての階層が保証されていると仮定すると、Chart を Cart に変更する方が理にかなっているからです。

class CartProgram () {
  CartItems lastItems;

  void main () {
    lastItems = getLast10ItemsSold();
    Cart myChart = new Cart(lastItems);
  }
}
class Cart () {
  CartItems lastItems;
  Cart(CartItems lastItems) {
  this.lastItems = lastItems;
  CartCalulations cc = new CartCalculations(this.lastItems);
  cc.getAverage();
}

class CartCalculations {
  CartItems lastItems;
  CartCalculations (CartItems lastItems){
  this.lastItems = lastItems;
  // Okay so at this point I have had to forward this value 3 times and this is 
  // a simple example. It just seems to make the code very brittle
  }
  getAverage() {
  // do stuff here
  }
}

class CartItems {
   private List<Item> itemList;

   public static CartItems Load(int cartId) {
       //return a new CartItems loaded from the backend for the cart id cartId
   }

   public void Add(Item item) {
   }

   public void Add(int item) {
   }

   public int SumPrices() {
       int sum = 0;
       foreach (Item i in itemList) {
          sum += i.getPrice()
       }
       return sum;
   } 
}

class Item 
{
    private int price;
    public int getPrice() { return price; }
}

適切に設計されたチャート ライブラリについては、http://www.aditus.nu/jpgraph/jpgarchitecture.phpを参照してください。

于 2009-11-24T06:47:53.970 に答える
1

結合を避けるために、あなたの例では、データポイントをグラフにフェッチできるある種の DataProvider を作成します。このようにして、グラフにしたいデータの種類に応じて変更する、いくつかの異なるタイプのデータソースを持つことができます。例えば

interface DataProvider
{
  double[] getDataPoints(); 
}

class Chart 
{
 void setDataProvider(DataProvider provider)
 {
   this.provider = provider;
 }
}

次に、ChartCalculation クラスを完全にスキップして、チャート クラスで計算を行います。コードを再利用して計算を行う必要がある場合は、平均、中央値、合計など、必要なものを計算できるツールキット クラス (Java の Math クラスに似ています) を作成します。このクラスは一般的であり、グラフや最後の項目の意味については何も知りません。

于 2009-11-24T09:39:19.093 に答える
0

いくつかの結合はアプリケーションで役立ちます。必要だと思います。型を変更するとコンパイラエラーが発生するという事実は、必ずしも脆弱になるとは限りません。

これをどのように見るかは、データをどのように使用するか、およびタイプがどのように変化するかによって決まると思います。

たとえば、常にを使用するとは限らないint[]が、後で必要になる場合ComplexDataType[]は、このデータを独自のクラスに入れ、そのクラスをパラメータとして渡すことができます。そうすれば、変更が必要な場合は、整数データを複素数にマッピングするだけでなく、新しいセッターとゲッターを作成して、アプリケーションからデータを分離し、実装を自由に変更できます。あなたがすでに同意した契約を守る限り、あなたが少しの注意を払って望むすべて。したがって、int配列をコンストラクターで、またはセッターとして使用できると述べた場合は、それを許可しますが、他のタイプ、たとえばdouble、も許可します。ComplexDataType

注目すべきことの1つは、クラスがデータのすべてのインスタンスに影響を与えるために行う可能性のある変換が必要かどうかです。つまり、データ転送オブジェクトがあり、2つのスレッドがあります。1つは3D球面グラフを作成し、もう1つは2D円グラフを作成します。各グラフにあるDTOを変更して、グラフ用の形式にしたいと思うかもしれませんが、データを球面座標に変更すると、円グラフにさまざまな問題が発生します。したがって、その時点で、データを渡すときfinalに、パラメーターのキーワードを利用してDTOを不変にすることができます。これにより、渡すものはすべてコピーになりますが、明示的に情報を置き換えることができます。要求されました(彼らはセッターを呼び出します)。

これは、いくつかのクラスを深く呼び出すときに問題を減らすのに役立つと思いますが、変更された場合はDTOを返す必要があるかもしれませんが、最終的にはおそらくより安全な設計です。

于 2009-11-24T20:09:59.047 に答える
0

Code Complete はそれを「パラメータの受け渡しを合理化する」と呼んでいます:

複数のルーチン間でパラメーターを渡す場合は、これらのルーチンを、パラメーターをオブジェクト データとして共有するクラスに分解する必要があることを示している可能性があります。
パラメータの合理化はそれ自体が目標ではありませんが、大量のデータをやり取りすることは、別のクラス組織がよりうまく機能する可能性があることを示唆しています。

于 2009-11-24T06:47:25.873 に答える