'유틸리티'수업이 문제의 원인입니까? [닫은]


14

때로는 다른 곳에 속하지 않는 메소드와 값을 유지하는 데 주로 사용되는 'Util'클래스를 만듭니다. 하지만이 수업 중 하나를 만들 때마다 "어, 나중에 후회하겠습니다 ..."라고 생각합니다. 왜냐하면 어딘가 나쁜 점을 읽었 기 때문입니다.

그러나 반면에, 그들에게는 두 가지 설득력있는 (적어도 나에게는)있는 것처럼 보입니다.

  1. 패키지 내의 여러 클래스에서 사용되는 구현 비밀
  2. 인터페이스를 어지럽히 지 않고 클래스를 보강하는 유용한 기능 제공

내가 파괴하는 길에 있습니까? 당신이 말하는 것 !! 리팩터링해야합니까?


11
Util수업 이름 사용 을 중지하십시오 . 문제 해결됨.
yannis 2011

3
@MattFenwick Yannis는 좋은 지적이 있습니다. 클래스를 명명하는 것은 SomethingUtil조금 게으른이며, 단지 클래스의 진정한 목적을 모호하게 -라는 이름의 클래스와 동일 SomethingManager또는 SomethingService. 그 수업에 하나의 책임이 있다면, 의미있는 이름을 부여하는 것이 쉬워야합니다. 그렇지 않다면, 그 처리 할 수있는 진짜 문제는 ...이다
MattDavey

1
@MDavey 좋은 점은, 아마 사용에 나쁜 단어 선택했다 Util분명 내가 그에 집착 얻을 것이며, 질문의 나머지는 무시 것을 기대하지 않았지만 ...

1
조작하는 객체의 종류에 따라 여러 * Util 클래스를 만들면 괜찮습니다. StringUtil, ListUtil 등에는 아무런 문제가 없습니다. C #에 있지 않으면 확장 메소드를 사용해야합니다.
marco-fiset 2011

4
나는 종종 관련 목적으로 사용되는 기능 세트를 가지고 있습니다. 그들은 실제로 수업에 잘 어울리지 않습니다. ImageResizer 클래스가 필요하지 않으며 ResizeImage 함수 만 있으면됩니다. ImageTools와 같은 정적 클래스에 관련 함수를 넣겠습니다. 아직 그룹화되지 않은 함수의 경우, Util 정적 클래스를 보유하고 있지만 한 클래스에 맞도록 서로 관련이있는 몇 가지가 있으면 이동합니다. 나는 이것을 처리하는 더 나은 OO 방법을 보지 못한다.
Philip

답변:


26

현대 OO 디자인은 모든 것이 대상 이 아니라는 것을 인정 합니다. 어떤 것은 행동이나 공식이며 어떤 것은 상태가 없습니다. 이러한 것들을 디자인의 이점을 얻기 위해 순수한 함수로 모델링하는 것이 좋습니다.

Java 및 C # (및 기타)은 util 클래스를 만들고 그 후프를 뛰어 넘습니다. 성가 시지만 세상의 끝은 아닙니다. 디자인 측면에서 문제가되지는 않습니다.


그래, 내가 생각했던 것 같아 답변 주셔서 감사합니다!

.NET은 이제이 방법의 대안으로 확장 메서드와 부분 클래스를 제공한다고 언급해야합니다. 이 항목을 최대한 활용하면 유틸리티 클래스가 거의 필요없는 언어 인 Ruby mixin이 생깁니다.
neontapir

2
@neontapir-확장 메소드 구현을 제외하고는 기본적으로 확장 메소드를 사용하여 util 클래스를 작성하도록 강요합니다.
Telastyn

진실. Util 클래스를 방해하는 장소는 두 가지가 있습니다. 하나는 클래스 자체에 있고 다른 하나는 호출 사이트입니다. 확장 된 메소드에 존재하는 것처럼 보이도록 메소드를 확장하면 콜 사이트 문제를 해결합니다. 나는 여전히 Util 클래스의 존재 문제가 있음에 동의합니다.
neontapir

16

"절대 절대로"말하지 마십시오

나는 그것이 반드시 나쁘다고 생각하지 않습니다, 당신이 그것을 잘못하고 남용하는 경우에만 나쁩니다.

우리 모두는 도구와 유틸리티가 필요합니다

우선, 우리 모두는 때때로 거의 어디에나 있고 꼭 필요한 것으로 간주되는 일부 라이브러리를 사용합니다. 예를 들어, Java 세계에서 Google Guava 또는 일부 Apache Commons ( Apache Commons Lang , Apache Commons Collections 등)가 있습니다.

따라서 이들에 대한 필요성이 분명히 있습니다.

하드 워드, 복제 및 버그 소개 피하기

당신이 생각하는 경우 꽤 많은 이들의 단지 매우 큰 무리입니다 Util누군가가 그들 (상대적으로) 권리를 얻기 위해 많은 노력을했다를 제외하고, 당신이 설명하는 클래스, 그들은 봤는데 시간 - 테스트 와 크게 다른 사람에 의해 눈이-고사리.

Util클래스 를 작성하는 가려움을 느낄 때 가장 먼저 경험할 수있는 규칙은 클래스가 Util실제로 존재하지 않는지 확인하는 것 입니다.

내가 본 유일한 반론은 다음과 같은 이유로 종속성을 제한하려고 할 때입니다.

  • 종속성의 메모리 풋 프린트를 제한하려고합니다.
  • 또는 개발자가 사용할 수있는 것을 엄격하게 제어하려고합니다 (강박 한 대규모 팀에서 발생하거나 특정 프레임 워크가 이상한 슈퍼 크 래핑 클래스를 사용하여 어딘가를 피하는 것으로 알려진 경우).

그러나 두 가지 모두 ProGuard 또는 이와 동등한 것을 사용하여 lib를 다시 패키징 하거나 스스로 분리 하여 해결할 수 있습니다 ( Maven 사용자의 경우 maven-shade-plugin 은 빌드의 일부로 이것을 통합하기 위해 필터링 패턴 을 제공합니다 ).

따라서 lib에 있고 사용 사례와 일치하고 다른 벤치 마크가 없으면 사용하십시오. 그것이 당신과 약간 다를 경우, 그것을 확장하거나 (가능한 경우) 확장하거나 마지막 수단으로 다시 작성하십시오.

명명 규칙

그러나 지금 까지이 답변에서 나는 그들을 Util당신처럼 부릅니다 . 그 이름을 말하지 마십시오.

그들에게 의미있는 이름을 부여하십시오. Google Guava를해야 할 일에 대한 아주 좋은 예라고 생각하고 com.google.guava네임 스페이스가 실제로 util루트 라고 상상해보십시오 .

util클래스가 아닌 최악 의 패키지를 호출하십시오 . String문자열 구조의 객체와 조작을 다루는 경우 호출 Strings하지 마십시오 StringUtils( 아파치 Apache Commons Lang- 여전히 좋아하고 사용합니다!). 특정 작업을 수행하는 경우 특정 클래스 이름 (예 : Splitter또는 Joiner)을 선택하십시오.

단위 테스트

이 유틸리티를 작성해야한다면 반드시 단위 테스트를 수행하십시오. 유틸리티의 장점은 일반적으로 특정 입력을 받고 특정 출력을 리턴하는 자체 포함 된 구성 요소라는 것입니다. 이것이 개념입니다. 따라서 단위 테스트를하지 않는다는 변명의 여지가 없습니다.

또한 단위 테스트를 통해 API 계약을 정의하고 문서화 할 수 있습니다. 테스트가 실패하면 잘못된 방식으로 변경 했거나 API 계약을 변경하려고 한 것입니다 (또는 원래 테스트는 문제를 학습 한 후 다시하지 마십시오) .

API 디자인

이러한 API에 대해 설계 결정을 내리면 오랜 시간이 걸릴 것입니다. 따라서 Splitter-clone 을 작성하는 데 몇 시간을 소비하지 않지만 문제에 어떻게 접근하는지주의하십시오.

몇 가지 질문을 해보십시오.

  • 유틸리티 메소드가 클래스를 자체적으로 보증합니까, 아니면 정적 메소드가 유사하게 유용한 메소드 그룹의 일부로 이해되는 경우 충분합니까?
  • 객체를 생성하고 API를 더 읽기 쉽게하기 위해 팩토리 메소드 가 필요 합니까?
  • 가독성에 대해 Fluent API , 빌더 등 이 필요 합니까?

이러한 유틸리티가 광범위한 사용 사례를 포괄하고 강력하고 안정적이며 잘 문서화되어 있고 가장 놀랍지 않은 원칙에 따라 자체적으로 포함되기를 원합니다. 이상적으로, utils의 각 하위 패키지 또는 전체 util 패키지는 재사용하기 쉽도록 번들로 내보낼 수 있어야합니다.

평소와 같이 여기에서 거인들로부터 배우십시오.

그렇습니다.이 중 많은 부분이 수집 및 데이터 구조에 중점을두고 있지만 대부분의 유틸리티를 직간접 적으로 구현할 수있는 곳이나 장소가 아니라고 말하지 마십시오.


+1If deals with String objects and manipulation of string constructs, call it Strings, not StringUtils
Tulains Córdova

+1 .Net의 API 디자인에 대해서는 .Net의 건축가가 쓴 책을 읽으십시오. 프레임 워크 디자인 가이드 라인 : 재사용 가능한 .NET 라이브러리에 대한 규칙, 숙어 및 패턴
MarkJ

왜 StringUtil 대신 Strings라고 부릅니까? 나에게 문자열은 컬렉션 객체처럼 들립니다.
bdrx

@bdrx : StringsCollectionTypeHere구체적인 구현을 원한다면 컬렉션 객체가 될 것 입니다. 또는이 문자열이 앱의 맥락에서 특정 의미를 갖는 경우보다 구체적인 이름입니다. 이 경우 Guava는 Commons Lang Strings과 달리 문자열 관련 도우미에 사용 합니다 StringUtils. 나는 그것이 완벽하게 수용 가능하다는 것을 알았습니다. 그것은 클래스가 String객체를 처리 하거나 문자열을 관리하는 범용 클래스 라는 것을 의미합니다 .
haylem

@bdrx : 일반적으로 NameUtils명확하게 레이블이 지정된 패키지 이름 아래에 있으면 유틸리티 클래스라는 것을 이미 알고있을 것입니다 (그렇지 않으면 API를보고 신속하게 파악할 수 있기 때문에). 그것은 사람들이 같은 물건을 선언대로 나에게 성가신로의 SomethingInterface, ISomething또는 SomethingImpl. C로 코딩하고 IDE를 사용하지 않을 때 그러한 인공물에 대해서는 괜찮 았습니다. 오늘날 나는 일반적으로 그런 것들이 필요하지 않습니다.
haylem

8

Util 클래스는 응집력이 없으며 일반적으로 클래스가 변경해야 할 단일 이유 (단일 책임 원칙)를 가져야하기 때문에 나쁜 설계입니다.

그러나, 내가 좋아하는 매우 자바 API의 클래스, "util을"본 적이 : Math, CollectionsArrays.

실제로는 유틸리티 클래스이지만 모든 메소드는 단일 테마와 관련이 있으며 하나는 수학 연산, 하나는 콜렉션 조작 방법 및 다른 하나는 배열 조작 방법입니다.

유틸리티 클래스에 전혀 관련없는 메소드가 없어보십시오. 이 경우, 그들이 실제로 속한 곳에 다른 곳에 놓을 수도 있습니다.

클래스 util을해야합니다 경우 그이 테마로 구분하려고 자바처럼 Math, CollectionsArrays. 최소한 네임 스페이스 일 경우에도 디자인 의도를 보여줍니다.

나는 하나, 항상 유틸리티 클래스를 피하고 그것을 만들 필요 가 없었습니다 .


답변 주셔서 감사합니다. 따라서 util 클래스가 패키지 전용 클래스 인 경우에도 여전히 디자인 냄새입니까?

모든 것이 클래스에 속하는 것은 아닙니다. 당신이 그것을 주장하는 언어에 갇혀 있다면 utils 수업이 일어날 것입니다.
jk.

1
글쎄, 유틸리티 클래스에는 응집력이 없기 때문에 얼마나 많은 메소드가 너무 많은지를 알려주는 디자인 원칙이 없습니다. 어떻게 멈출 수 있는지 알 수 있습니까? 30 가지 방법 후에? 40? 100? 반면에 OOP 원칙은 메서드가 특정 클래스에 속하지 않는 경우와 클래스가 너무 많은 시간에 대한 아이디어를 제공합니다. 이를 응집력이라고합니다. 그러나 내가 대답했듯이 Java를 디자인 한 사람들은 유틸리티 클래스를 사용했습니다. 당신도 할 수 없습니다. 나는 유틸리티 클래스를 만들지 않고 어떤 것이 일반 클래스에 속하지 않는 상황에 있지 않았습니다.
Tulains Córdova

1
Utils 클래스는 일반적으로 클래스가 아니며 단지 순수한 함수를 포함하는 네임 스페이스 일뿐입니다. 순수한 기능은 가능한 한 응집력이 있습니다.
jk.

1
@jk 나는 Utils를 의미했다. 그런데 적어도 디자인의 의도 와 같은 이름을 나타내 Math거나 Arrays최소한의 의도를 보여준다.
Tulains Córdova

3

라는 용어를 선호하지만 util 클래스를 사용하는 것은 완벽하게 허용됩니다 ClassNameHelper. .NET BCL에는 도우미 클래스도 있습니다. 기억해야 할 가장 중요한 것은 클래스의 목적과 각 개별 헬퍼 메소드를 철저하게 문서화하고 유지 보수 가능한 고품질 코드를 작성하는 것입니다.

그리고 헬퍼 클래스를 가지고 다니지 마십시오.


0

나는 2 계층 접근법을 사용합니다. "util"패키지 (폴더)의 "Globals"클래스 "Globals"클래스 또는 "util"패키지로 들어가려면 다음과 같아야합니다.

  1. 단순한,
  2. 변함없는,
  3. 응용 프로그램의 다른 것에 의존하지 않음

이러한 테스트를 통과 한 예 :

  • 시스템 전체 날짜 및 10 진수 형식
  • EARLIEST_YEAR (시스템이 지원하는) 또는 IS_TEST_MODE와 같은 불변의 글로벌 데이터 또는 시스템이 실행되는 동안 진정한 글로벌 및 불변의 값.
  • 언어, 프레임 워크 또는 툴킷의 차이를 해결하는 매우 작은 도우미 메서드

다음은 응용 프로그램의 나머지 부분과 완전히 독립적 인 매우 작은 도우미 메서드의 예입니다.

public static String ordinal(final int i) {
    return (i == 1) ? "1st" :
           (i == 2) ? "2nd" :
           (i == 3) ? "3rd" :
           new StringBuilder().append(i).append("th").toString();
}

이것을 보면 21이 "21st", 22가 "22nd"등이어야하는 버그를 볼 수 있습니다. 그러나 그것은 요점입니다.

이러한 도우미 메서드 중 하나가 커지거나 복잡해지면 util 패키지에서 자체 클래스로 이동해야합니다. util 패키지의 둘 이상의 도우미 클래스가 서로 관련되어 있으면 자체 패키지로 옮겨야합니다. 상수 또는 도우미 메서드가 응용 프로그램의 특정 부분과 관련된 것으로 판명되면 이동해야합니다.

Globals 클래스 또는 util 패키지에 넣은 모든 것이 더 좋은 곳이없는 이유를 정당화 할 수 있어야하며 위의 테스트를 사용하여 주기적으로 정리해야합니다. 그렇지 않으면, 당신은 혼란을 만들고 있습니다.

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