일관성 및 Use Right Tool For Right Job 중 무엇이 중요합니까?

7040 단어 작업
이 논쟁은 테스트 때 발생했다.
배경은 다음과 같습니다.
하나의 인터페이스에는 여러 가지 엉망진창인 업무와 관련된 방법이 있는데 그 중에서 네 가지 방법이 있다.

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 클래스를 만듭니다. 이 클래스는 401k와 403g에 대한 논리적 처리를 추가합니다.

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。
}

그래.몇몇 코드가 중복되다.주로 이 인터페이스를 잘 설계하지 못했기 때문이다.다행히 중복이 많지 않으니 아쉬운 대로 하자.
다음은 테스트 코드를 쓰겠습니다.먼저 401k에 대한 코드를 어떻게 쓰는지 봅시다.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 대상에서 대응하는 함수를 호출합니다.
다음은 401k와 403g의 논리가 거의 똑같고 방법명만 다르다는 것을 알아차렸다.그래서 코드가 중복되는 것을 피하기 위해서, 나와pair는 abstract class로 공통성을 추출하기로 결정했다.그리고 두 개의 자류로 서로 다른 점을 대표한다.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()와 다른 몇 가지 방법이 있는데 401k와 403g은 방법 이름이 다른 것을 제외하고는 모두 같다.
그러나pair는 easymock으로 이 방안을 총살했다는 말을 듣자마자 곧바로 총살했다.pair의 관점:
일치성을 유지하기 위해서 우리는 반드시 jmock을 써야 한다.easymock을 사용하면 회사의 다른 프로그래머들에게 이해에 어려움을 줄 수 있다.(나는 내 테스트 케이스 몇 개가 모두 easymock아를 사용한다고 매우 겸허하게 생각한다.)
이 견해에 대해 나는 어떻게 말해야 할지 좀 모르겠다.나의 지금까지의 관점은 모두use the right tool for the right job이다.
만약 공구갑이 기능 1, 2, 3, 4를 할 수 있다면 공구을은 기능 3, 4, 5, 6을 할 수 있다.그럼 일치성을 유지하기 위해 갑만 쓰거나 을만 쓰도록 강요하지는 않겠습니다.만약에 회사 표준의ComplexTool이 기능 1,2,3,4,5,6, 업계 표준의SimpleTool(예를 들어java.util.HashMap)을 할 수 있다면 나는 기능 2,3,4,5,6을 필요로 하지 않는다. 그러면 나는SimpleTool을 선택할 것이다.
더군다나 기술을 강조하는 회사에서 여러분들이 easymock이나 jmock을 사용하지 않을까 봐 걱정되는 것은 정말 필요합니까?아무래도 jmock이든 easymock이든 30분이면 파악할 수 있는 겨울이죠?
사실 일치성을 강조하는 문제에 관해서 나는 이미 동료들과 몇 차례 의견 차이가 있었다.또 한 번은 회사에서 내부적으로 만들어진 MyProperty Factory 프레임워크로class path 안의property 파일 내용을 읽는 경우가 대부분이다.제가 한 번 독립적으로 개발한 모듈은 ClassLoader를 직접 사용했습니다.loadResourceAsInputStream().그래서 동료는 일치성을 이유로 MyPropertyFactory를 사용하기 위해 코드를 변경해 달라고 요구했다.세부 사항은 다음 disagree에서 설명할게요.
그렇다면 일치성과use the right tool은 정말 모순되는 것일까?너는 어떻게 둘 사이의 균형을 이룰 수 있니?

좋은 웹페이지 즐겨찾기