향후 인터페이스 사용을위한 프로그래밍


42

내 옆에 다음과 같은 인터페이스를 디자인 한 동료가 있습니다.

public interface IEventGetter {

    public List<FooType> getFooList(String fooName, Date start, Date end)
        throws Exception;
    ....

}

문제는 현재 코드의 어느 곳에서나이 "종료"매개 변수를 사용하지 않고 있다는 것입니다. 차후에 시간이 지나야 할 수도 있기 때문입니다.

우리는 지금 매개 변수를 인터페이스에 넣는 것이 좋지 않다는 것을 확신 시키려고 노력하고 있지만, 그는 "종료"날짜의 사용을 얼마간 구현하면 많은 작업을 수행해야한다고 주장합니다. 나중에 모든 코드를 수정해야합니다.

이제 제 질문은 "존중 한"코딩 전문가와 같은 주제를 다루는 소스가 있습니까?


29
"특히 미래에 대한 예측은 매우 어렵다."
Jörg W Mittag

10
문제의 일부는 시작하기에 가장 좋은 인터페이스가 아니라는 것입니다. Foos를 가져오고 필터링하는 것은 두 가지 별도의 문제입니다. 이 인터페이스는 특정 방식으로 목록을 필터링하도록합니다. 훨씬 더 일반적인 접근 방식은 foo가 포함되어야하는지 결정하는 함수를 전달하는 것입니다. 그러나 이것은 Java 8에 액세스 할 수있는 경우에만 우아합니다.
Doval

5
반대는 포인트입니다. 커스텀 타입 whos를 받아들이는 메소드는 이제 필요한 것만 포함하고 결국 end그 객체에 매개 변수를 추가하고 코드를 깨뜨리지 않기 위해 기본값을 지정할 수도 있습니다.
Rémi

3
Java 8 기본 메소드를 사용하면 불필요한 인수를 추가 할 이유가 없습니다. with로 정의 된 간단한 호출을 기본 구현으로 나중에 메소드를 추가 할 수 있습니다 null. 그런 다음 필요에 따라 클래스를 구현할 수 있습니다.
거미 보리스

1
@Doval Java에 대해 잘 모르지만 .NET IQueryable에서는 DAL 외부의 코드 에 대한 특유의 노출을 피하는 것과 같은 것을 가끔 보게 될 것입니다.
Ben Aaronson

답변:


62

그에게 YAGNI 에 대해 배우도록 권유한다 . Wikipedia 페이지의 이론적 근거는 다음에서 특히 흥미로울 수 있습니다.

YAGNI 접근 방식을 옹호하는 사람들에 따르면, 현재로서는 필요하지 않지만 앞으로있을 수있는 코드를 작성하려는 유혹에는 다음과 같은 단점이 있습니다.

  • 소요되는 시간은 필요한 기능을 추가, 테스트 또는 개선하는 데 소요됩니다.
  • 새로운 기능은 디버깅, 문서화 및 지원되어야합니다.
  • 새로운 기능은 향후 수행 할 수있는 작업에 제약을가하므로 불필요한 기능으로 인해 향후 필요한 기능이 추가되지 않을 수 있습니다.
  • 기능이 실제로 필요할 때까지 기능을 완전히 정의하고 테스트하기가 어렵습니다. 새 기능이 올바르게 정의 및 테스트되지 않은 경우 결국 필요하더라도 올바르게 작동하지 않을 수 있습니다.
  • 코드 팽창으로 이어집니다. 소프트웨어가 커지고 복잡해집니다.
  • 사양과 어떤 종류의 개정 제어가 없으면 기능을 사용할 수있는 프로그래머에게는이 기능이 알려지지 않을 수 있습니다.
  • 새로운 기능을 추가하면 다른 새로운 기능이 제안 될 수 있습니다. 이러한 새로운 기능도 구현되면 기능 크리프에 눈덩이 효과가 발생할 수 있습니다.

다른 가능한 주장들 :

  • “한 소프트웨어 수명의 80 %가 유지 보수에 사용 됩니다. 적시에 코드를 작성 하면 유지 관리 비용이 절감됩니다. 적은 코드를 유지 관리해야하며 실제로 필요한 코드에 집중할 수 있습니다.

  • 소스 코드는 한 번 작성되지만 수십 번 읽습니다. 어디에도 사용되지 않는 추가적인 주장은 왜 불필요한 주장이 있는지 이해하는 데 시간을 낭비하게됩니다. 이것이 몇 가지 가능한 구현이있는 인터페이스이므로 상황을 더욱 어렵게 만듭니다.

  • 소스 코드는 자체 문서화가 필요합니다. 실제 서명은 리더가 그것이 end결과 나 메소드 실행에 영향을 준다고 생각하기 때문에 오해의 소지 가 있습니다.

  • 이 인터페이스를 구체적으로 구현 한 사람은 마지막 인수를 사용해서는 안된다는 것을 이해하지 못할 수 있습니다.

    1. 필요하지 않으므로 end간단히 그 가치를 무시하고

    2. 필요하지 않으므로 end예외가 아닌 경우 예외를 처리합니다 null.

    3. 필요 end는 없지만 어떻게 든 사용하려고 시도합니다.

    4. 나중에 end필요할 때 사용할 수있는 많은 코드를 작성 하겠습니다.

그러나 동료가 옳을 수도 있습니다.

이전의 모든 요점은 리팩토링이 쉽다는 사실을 기반으로하므로 나중에 인수를 추가하는 데 많은 노력이 필요하지 않습니다. 그러나 이것은 인터페이스이며 인터페이스로서 제품의 다른 부분에 기여하는 여러 팀에서 사용할 수 있습니다. 이것은 인터페이스 변경이 특히 고통 스러울 수 있음을 의미하며,이 경우 YAGNI는 실제로 적용되지 않습니다.

hjk의 대답 은 좋은 솔루션을 제공합니다. 이미 사용 된 인터페이스에 메소드를 추가하는 것은 특별히 어렵지는 않지만 경우에 따라 상당한 비용이 발생합니다.

  • 일부 프레임 워크는 오버로드를 지원하지 않습니다. 예를 들어 기억이 잘된다면 (잘못된 경우 수정) .NET의 WCF는 과부하를 지원하지 않습니다.

  • 인터페이스에 구체적인 구현이 많은 경우 인터페이스에 메소드를 추가하려면 모든 구현을 수행하고 메소드를 추가해야합니다.


4
이 기능이 향후에 나타날 가능성을 고려하는 것도 중요하다고 생각합니다. 확실히 알고 있다면 어떤 이유로 든 지금 추가되지는 않지만 "사고"기능이 아니기 때문에 명심해야 할 좋은 주장이라고 말하고 싶습니다.
Jeroen Vannevel

3
@JeroenVannevel : 확실히, 그러나 그것은 매우 투기입니다. 내 경험상 필자가 구현했다고 생각하는 대부분의 기능은 완전히 취소되거나 지연되었습니다. 여기에는 원래 이해 관계자가 매우 중요하게 강조한 기능이 포함됩니다.
Arseni Mourzenko

26

그러나 그는 "종료"날짜의 사용을 얼마 후에 구현하고 모든 코드를 수정해야한다면 많은 작업을 수행해야한다고 주장하고있다.

(언젠가 나중에)

public class EventGetter implements IEventGetter {

    private static final Date IMPLIED_END_DATE_ASOF_20140711 = new Date(Long.MAX_VALUE); // ???

    @Override
    public List<FooType> getFooList(String fooName, Date start) throws Exception {
        return getFooList(fooName, start, IMPLIED_END_DATE_ASOF_20140711);
    }

    @Override
    public List<FooType> getFooList(String fooName, Date start, Date end) throws Exception {
        // Final implementation goes here
    }
}

그게 당신이 필요한 전부입니다. 향후 추가 메소드는 기존 메소드 호출에 영향을주지 않고 투명하게 도입 할 수 있습니다.


6
변경 사항을 제외하고는 더 이상 인터페이스가 아니며 객체가 객체에서 이미 파생되어 인터페이스를 구현하는 경우에는 사용할 수 없습니다. 인터페이스를 구현하는 모든 클래스가 동일한 프로젝트에 있으면 (사소 컴파일 오류) 사소한 문제입니다. 여러 프로젝트에서 사용하는 라이브러리 인 경우이 필드를 추가하면 혼란스럽고 다음 x 개월 / 년 동안 산발적 인 시간에 다른 사람들을 위해 일하게됩니다.
Kieveli

1
OP 팀 (동료 저장)이 end매개 변수가 현재 불필요 하다고 생각하고 end프로젝트 전체에서 -less 방법을 계속 사용 했다면 인터페이스를 업데이트하는 것이 걱정할 필요가 가장 적기 때문에 구현 클래스. 제 답변은 "모든 코드를 적용하십시오"부분에 대해 구체적으로 타겟팅하고 있습니다. 왜냐하면 동료가 세 번째 기본 / 더미 매개 변수를 도입하기 위해 프로젝트 전체에서 원래 메소드의 호출자를 업데이트해야한다고 생각하는 것처럼 들리기 때문입니다. .
hjk

18

[존중] 우리가 그를 설득하기 위해 그를 연결할 수있는 "존중 한"코딩 전문가?

권위에 대한 호소는 특별히 설득력이 없다. 누가 말했는지에 상관없이 유효한 주장을 제시하는 것이 좋습니다.

다음 은 동료가 감성에 관계없이 "올바른"상태에 머물러 있음을 설득하거나 입증해야하는 reductio ad absurdum 입니다.


당신이 정말로 필요한 것은

getFooList(String fooName, Date start, Date end, Date middle, 
           Date one_third, JulianDate start, JulianDate end,
           KlingonDate start, KlingonDate end)

클링 온을 언제 국제화해야하는지 알지 못하므로 개장하는 데 많은 작업이 필요하고 클링 온은 인내심으로 알려져 있지 않기 때문에 이제 더 잘 관리해야합니다.


22
You never know when you'll have to internationalize for Klingon. 그렇기 때문에 Calendar클라이언트 코드를 전달해야 하고 클라이언트 코드에서 a GregorianCalendar또는 a 를 보낼 것인지 결정하도록하십시오 KlingonCalendar(일부 이미 수행 한 것 같습니다). 어. - P
SJuan76

3
무엇에 대한 StarDate sdStart그리고 StarDate sdEnd? 우리는 결국 연맹을 잊을 수 없다 ...
WernerCD

그것들은 분명히 서브 클래스이거나 변환 가능합니다 Date!
Darkhogg


@msw, 나는 "존중하는 코딩 전문가"에 대한 링크를 요청할 때 "권한에 대한 이의 제기"를 요구하고 있는지 확실하지 않습니다. 당신이 말했듯이, 그는 "누가 그것을 말했는지에 상관없이 유효한 주장"이 필요합니다. "구루"형 사람들은 많은 것을 알고 경향이 있습니다. 또한 당신은 잊었 HebrewDate startHebrewDate end.
trysis

12

소프트웨어 엔지니어링 관점에서 이러한 종류의 문제에 대한 적절한 솔루션 이 빌더 패턴에 있다고 생각합니다 . 이것은 동료 http://en.wikipedia.org/wiki/Builder_pattern 'guru'저자의 링크입니다 .

빌더 패턴에서 사용자는 매개 변수가 포함 된 오브젝트를 작성합니다. 그런 다음이 매개 변수 컨테이너가 메소드로 전달됩니다. 이를 통해 향후 동료가 필요로하는 매개 변수의 확장 및 과부하를 처리하면서 변경이 필요할 때 모든 것을 매우 안정적으로 유지할 수 있습니다.

당신의 예는 다음과 같습니다 :

public interface IEventGetter {
    public List<FooType> getFooList(ISearchFooRequest req) {
        throws Exception;
    ....
    }
}

public interface ISearchFooRequest {
        public String getName();
        public Date getStart();
        public Date getEnd();
        public int getOffset();
        ...
    }
}

public class SearchFooRequest implements ISearchFooRequest {

    public static SearchFooRequest buildDefaultRequest(String name, Date start) {
        ...
    }

    public String getName() {...}
    public Date getStart() {...}
    ...
    public void setEnd(Date end) {...}
    public void setOffset(int offset) {...}
    ...
}

3
이것은 좋은 일반적인 접근 방법이지만이 경우 문제를에서 IEventGetter로 밀어 넣는 것 같습니다 ISearchFootRequest. 대신 항목을 포함할지 여부를 반환하는 public IFooFilter멤버 와 같은 것을 제안하고 싶습니다 public bool include(FooType item). 그러면 개별 인터페이스 구현에서 필터링을 수행하는 방법을 결정할 수 있습니다.
Ben Aaronson

@ Ben Aaronson 방금 코드를 편집하여 Request 매개 변수 객체가 생성되는 방법을 보여줍니다
InformedA

빌더는 여기에서 불필요합니다 (그리고 솔직히 말해서, 일반적으로 남용 / 과다 추천 패턴). 매개 변수 설정 방법에 대한 복잡한 규칙이 없으므로, 복잡하거나 자주 변경되는 메소드 매개 변수에 대한 정당한 우려가있는 경우 매개 변수 오브젝트 가 완벽하게 적합합니다. 그리고 @BenAaronson에 동의하지만 매개 변수 객체를 사용하는 요점은 불필요한 매개 변수를 즉시 포함 하지 않지만 나중에 여러 인터페이스 구현이 있어도 추가하기가 매우 쉽습니다.
Aaronaught

7

지금은 필요하지 않으므로 추가하지 마십시오. 나중에 필요하면 인터페이스를 확장하십시오.

public interface IEventGetter {

    public List<FooType> getFooList(String fooName, Date start)
         throws Exception;
    ....

}

public interface IBoundedEventGetter extends IEventGetter {

    public List<FooType> getFooList(String fooName, Date start, Date end)
        throws Exception;
    ....

}

기존 (아마도 이미 테스트 / 배송) 인터페이스를 변경하지 않은 경우 +1
Sebastian Godelet

4

디자인 원칙이 절대적인 것은 아니기 때문에 나는 대부분 다른 답변에 동의하지만, 나는 악마의 옹호자 역할을하고 동료의 솔루션을 받아들이는 조건을 논의 할 것이라고 생각했습니다.

  • 공개 API 인 경우 내부에서 사용하지 않더라도이 기능이 타사 개발자에게 유용 할 것으로 예상됩니다.
  • 미래의 이익뿐만 아니라 상당한 즉각적인 이점이 있다면. 암시 적 종료 날짜가 Now()인 경우 매개 변수를 추가하면 부작용이 제거되어 캐싱 및 단위 테스트에 이점이 있습니다. 아마도 더 간단한 구현이 가능할 것입니다. 또는 코드의 다른 API와 더 일관성이있을 수 있습니다.
  • 개발 문화에 문제가있는 경우 프로세스 나 영토가 인터페이스와 같은 중심적인 것을 변경하기가 너무 어려워지면 인터페이스를 변경하는 대신 클라이언트 측 해결 방법을 구현하는 사람들이 이전에 나타났습니다. 여러 개의 임시 종료 날짜를 유지하려고합니다. 하나 대신 필터. 이런 종류의 일이 회사에서 많이 발생하는 경우 향후 교정에 약간의 노력을 기울이는 것이 좋습니다. 날 오해하지 마세요, 그건 더 나은 개발 프로세스를 변경할 수 있지만, 일반적으로 쉽게 말하기는한다.

내 경험에 따르면, YAGNI에 대한 가장 큰 추론은 YDKWFYN입니다. 당신은 당신이 어떤 형태를 필요로할지 모르겠습니다 (예, 나는 그 약어를 만들었습니다). 일부 한계 매개 변수가 상대적으로 예측 가능 하더라도 페이지 한계 또는 일 수 또는 사용자 환경 설정 테이블의 종료 날짜 또는 여러 가지를 사용하도록 지정하는 부울 형식 일 수 있습니다.

아직 요구 사항이 없으므로 해당 매개 변수의 유형을 알 방법이 없습니다. 요구 사항에 가장 적합하지 않은 어색한 인터페이스가 있거나 어쨌든 변경해야하는 경우가 종종 있습니다.


2

이 질문에 대한 답변이 충분하지 않습니다. getFooList실제로 수행하는 작업과 수행 방법 에 따라 다릅니다 .

다음은 사용 여부에 관계없이 추가 매개 변수를 지원해야하는 방법의 명백한 예입니다.

void CapitalizeSubstring (String capitalize_me, int start_index);

컬렉션의 시작은 지정할 수 있지만 컬렉션의 끝은 지정할 수없는 컬렉션에서 작동하는 메서드를 구현하는 것은 종종 바보입니다.

실제로 문제 자체를보고 전체 인터페이스의 맥락에서 매개 변수가 무의미한 지 여부와 추가 매개 변수가 부과하는 부담이 얼마나되는지 물어봐야합니다.


1

당신의 동료가 실제로 매우 유효한 지적을 가지고있는 것이 두렵습니다. 그의 해결책은 실제로 최고는 아니지만.

그의 제안 된 인터페이스에서

public List<FooType> getFooList(String fooName, Date start, Date end) throws Exception;

일정 시간 내에 발견 된 인스턴스를 리턴합니다. 클라이언트가 현재 end 매개 변수를 사용하지 않는 경우 일정 시간 내에 인스턴스가 발견된다는 사실을 변경하지 않습니다. 그것은 단지 현재 모든 클라이언트가 열린 시작 간격을 사용한다는 것을 의미합니다 (시작부터 영원까지)

더 나은 인터페이스는 다음과 같습니다.

public List<FooType> getFooList(String fooName, Interval interval) throws Exception;

정적 팩토리 방법으로 간격을 제공하는 경우 :

public static Interval startingAt(Date start) { return new Interval(start, null); }

그러면 고객은 종료 시간을 지정할 필요조차 느끼지 못할 것입니다.

동시에 인터페이스가 수행하는 작업을보다 정확하게 전달 getFooList(String, Date)합니다. 이는 간격을 처리한다는 것을 알리지 않기 때문입니다.

내 제안은 방법이 현재 수행하는 방식, 향후 수행해야 할 것과 수행 할 수있는 것이 아니라 YAGNI 원칙 (실제로 유효 함)에는 적용되지 않습니다.


0

사용하지 않는 매개 변수를 추가하는 것이 혼란 스럽습니다. 이 기능이 작동한다고 가정하면 해당 메소드를 호출 할 수 있습니다.

나는 그것을 추가하지 않을 것입니다. 나중에 리팩토링을 사용하여 호출 사이트를 기계적으로 수정하여 추가하는 것이 쉽지 않습니다. 정적으로 입력 된 언어에서는이 작업이 쉽습니다. 열심히 추가 할 필요는 없습니다.

해당 매개 변수를 가질 이유 있는 경우 혼동을 막을 수 있습니다. 해당 인터페이스 구현에 어설 션을 추가하여이 매개 변수가 기본값으로 전달되도록하십시오. 누군가 실수로 해당 매개 변수를 사용하는 경우 테스트 중에이 기능이 구현되지 않았 음을 즉시 알 수 있습니다. 이로 인해 버그가 프로덕션에 빠질 위험이 사라집니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.