コンシステンシとUse Right Tool For Right Jobのどちらが重要ですか?

6820 ワード

この争いはテストの時に発生した.
背景は次のとおりです.
1つのインタフェースには、めちゃくちゃなビジネス関連の方法がたくさんあります.その中には、次の4つの方法があります.

interface TaxLawBuckets {
  double getRemaining401k();
  double getRemaining403g();
  void apply401k(double amount);
  void apply403g(double amount);
}

もちろん、このデザインはちょっとよくありません.もっと良いのは、

interface TaxLawBuckets {
  TaxLawBucket get401k();
  TaxLawBucket get403g();
}
interface TaxLawBucket {
  double getRemaining();
  void apply(double amount);
}

しかし、様々な理由で、この設計を採用することはできません(主にこのように変更すると、変更が大きくなります.このシステムにはwsdlコードジェネレータがあり、このジェネレータはインタフェースの長さに変態の要求があります).だから間に合わせにします.
PerAccountTaxLawBucketsクラスを作成します.このクラスは401 kと403 gに追加の論理処理があります.

interface TaxLawBuckets {
  private double bucket_401k;
  private double bucket_403g;
  private TaxLawBuckets globalBuckets;

  public double getRemaining401k(){
    return Math.min(bucket_401k, globalBuckets.getRemaining401k());
  }
  public double getRemaining403g() {
    return Math.min(bucket_403g, globalBuckets.getRemaining403g());
  }
  public void apply401k(double amount) {
    bucket_401k -= amount;
    if(bucket_401k<0) throw new SystemException(...);
    globalBuckets.apply401k(amount);
  }
  public void apply403g(double amount) {
    bucket_403g -= amount;
    if(bucket_403g<0) throw new SystemException(...);
    globalBuckets.apply403g(amount);
  }
  // globalBuckets。
}

うん.いくつかのコードが重複しています.主にこのインタフェースの設計が悪いからです.幸いなことに、重複が少ないので、間に合いましょう.
次はテストコードを書きます.まず401 kに対するコードの書き方を見てみましょう.pairはjmockを使っています.

public class PerAccountTaxLawBucketsTest extends MockObjectTestCase {
  Mock bucketsMock = mock(TaxLawBuckets.class);
  TaxLawBuckets globalBuckets = bucketMock.proxy();
  PerAccountTaxLawBuckets perAccount = new PerAccountTaxLawBuckets(100, 100, globalBuckets);
  public void testGetRemaining401kReturnsTheMinimal() {
    bucketsMock.expects(once()).method("getRemaining401k").will(return(200));
    assertEquals(100, perAccount.getRemaining401k());
  }
  ...
}

他のテストもjmockでexpectationを設定し、perAccountオブジェクトから対応する関数を呼び出します.
次に,401 kと403 gの論理はほぼそっくりであり,メソッド名のみが異なることに気づく.だからコードの重複を避けるために、私とpairはabstract classで共通性を抽出することにしました.次に、異なる点を2つのサブクラスで表します.pairのコードはこうです.

public abstract class AbstractPerAccountTaxLawBucketsTest extends MockObjectTestCase {
  Mock bucketsMock = mock(TaxLawBuckets.class);
  TaxLawBuckets globalBuckets = bucketMock.proxy();
  PerAccountTaxLawBuckets perAccount = new PerAccountTaxLawBuckets(100, 100, globalBuckets);
  public void testGetRemainingReturnsTheMinimal() {
    bucketsMock.expects(once()).method(getRemainingName()).will(return(200));
    assertEquals(100, getRemaining(perAccount));
  }
  ...
  abstract String getRemainingName();
  abstract double getRemaining(TaxLawBuckets buckets);
  abstract String getApplyName();
  abstract void apply(TaxLawBuckets buckets, double amount);
  ...  
}

public class PerAccount401kTestCase extends AbstractPerAccountTaxLawBucketsTest {
  String getRemainingName() {
    return "getRemaining401k";
  }
  double getRemaining(TaxLawBuckets buckets) {
    return buckets.getRemaining401k();
  }
  String getApplyName() {
    return "apply401k";
  }
  void apply(TaxLawBuckets buckets, double amount) {
    buckets.apply401k(amount);
  }
}

public class PerAccount403gTestCase extends AbstractPerAccountTaxLawBucketsTest {
  String getRemainingName() {
    return "getRemaining403g";
  }
  double getRemaining(TaxLawBuckets buckets) {
    return buckets.getRemaining403g();
  }
  String getApplyName() {
    return "apply403g";
  }
  void apply(TaxLawBuckets buckets, double amount) {
    buckets.apply403g(amount);
  }
}

私はgetRemainingName()とgetRemainingの重複があまり好きではありません.easymockで書くことをお勧めします.


public abstract class AbstractPerAccountTaxLawBucketsTest extends TestCase {
  MockControl bucketsMock = MockControl.createControl(TaxLawBuckets.class);
  TaxLawBuckets globalBuckets = bucketMock.getMock();
  PerAccountTaxLawBuckets perAccount = new PerAccountTaxLawBuckets(100, 100, globalBuckets);
  public void testGetRemainingReturnsTheMinimal() {
    bucketsMock.expectsAndReturn(getRemaining(globalBuckets), 200);
    bucketsMock.replay();
    assertEquals(100, getRemaining(perAccount));
    bucketsMock.verify();
  }
  ...
  abstract double getRemaining(TaxLawBuckets buckets);
  abstract void apply(TaxLawBuckets buckets, double amount);
  ...  
}

public class PerAccount401kTestCase extends AbstractPerAccountTaxLawBucketsTest {
  double getRemaining(TaxLawBuckets buckets) {
    return buckets.getRemaining401k();
  }
  void apply(TaxLawBuckets buckets, double amount) {
    buckets.apply401k(amount);
  }
}

public class PerAccount403gTestCase extends AbstractPerAccountTaxLawBucketsTest {
  double getRemaining(TaxLawBuckets buckets) {
    return buckets.getRemaining403g();
  }
  void apply(TaxLawBuckets buckets, double amount) {
    buckets.apply403g(amount);
  }
}

これにより、重複が減少し、コードがよりきれいに感じられます.
実は、この例は問題を簡略化した.実際のそのプログラムは、getRemaining()、apply()のほかに、restore()や他のいくつかの方法もあり、401 kと403 gは方法の名前が違う以外は同じです.
しかし、pairはeasymockで直接この案を銃殺したと聞いた.pairの観点:
一貫性を保つためにjmockを使うべきです.easymockの使用は、会社の他のプログラマーに理解上の困難をもたらします.△私のいくつかのtest caseはeasymock亜を使っていると思います.
この言い方に対して、私は少しどう言ったらいいか分かりません.私のこれまでの観点は、use the right tool for the right jobです.
ツール甲が機能1,2,3,4を行うことができれば、ツール乙は機能3,4,5,6を行うことができる.では、私は一致性を保つために甲だけを使うか、乙だけを使うように強制することはありません.一方、1つの会社の標準的なComplexToolが機能1,2,3,4,5,6をすることができ、1つの業界標準のSimpleTool(java.util.HashMapなど)が機能1をすることができ、私は機能2,3,4,5,6を必要としないので、SimpleToolを選択します.
まして、技術を強調する会社では、easymockやjmockを使わないのではないかと心配しています.なにしろjmockもeasymockも30分で身につくはずの冬冬だろ?
実は、このような一致性を強調する問題について、私はもう同僚と何度も意見が分かれています.もう1つは、社内で作ったMyPropertyFactory frameworkでclass pathのpropertyファイルの内容を読み取ることが多いことです.私が独立して開発したモジュールはClassLoaderを直接使用しましたloadResourceAsInputStream().そこで、同僚は一貫性を理由に私にMyPropertyFactoryを使用するためにコードを変更するように要求した.具体的な詳細は次のdisagreeで説明します.
では、一致性とuse the right toolは本当に矛盾しているのだろうか.どうやって両者のバランスを取るのですか?