유틸리티 클래스는 사악합니까? [닫은]


97

나는이 실을 보았다

"유틸리티"클래스가 악한 경우 일반 코드를 어디에 두어야합니까?

그리고 유틸리티 클래스가 왜 나쁜지 생각 했습니까?

수십 개의 클래스 깊이가있는 도메인 모델이 있다고 가정 해 보겠습니다. 인스턴스를 xml-ify 할 수 있어야합니다. 부모에게 toXml 메서드를 만드나요? MyDomainXmlUtility.toXml 도우미 클래스를 만들어야합니까? 이것은 비즈니스 요구가 전체 도메인 모델에 걸쳐있는 경우입니다. 실제로 인스턴스 메서드로 속합니까? 애플리케이션의 xml 기능에 대한 많은 보조 메서드가 있다면 어떨까요?


27
"악"이라는 용어의 평가 절하는 악입니다!
Matthew Lock

1
) 나는 내 질문의 기반이되는 게시물의 조건에 ... 보존 @matthew
hvgotcodes을

유틸리티 클래스는 싱글 톤과 같은 이유로 나쁜 생각입니다.
CurtainDog

1
toXML 메서드를 사용할지 여부에 대한 논쟁은 풍부한 도메인 모델과 빈약 한 도메인 모델을 중심으로 한 것입니다. codeflow.blogspot.com/2007/05/anemic-vs-rich-domain-models.html
James P.

@james, toXML은 단지 예일뿐입니다. 모든 곳에서 사용되는 정규식 기능은 어떻습니까? 마찬가지로, 도메인 모델에서 문자열로 몇 가지 작업을 수행해야하지만 하나의 수퍼 클래스 (자바에서)를 사용한 또 다른 재정의 우려로 인해 서브 클래 싱을 사용할 수
없었습니다

답변:


129

유틸리티 클래스는 정확히 악한 것은 아니지만 좋은 객체 지향 디자인을 구성하는 원칙을 위반할 수 있습니다. 좋은 객체 지향 디자인에서 대부분의 클래스는 단일 항목과 모든 속성 및 작업을 나타내야합니다. 사물에 대해 작업하는 경우 해당 메서드는 해당 사물의 구성원이어야합니다.

그러나 유틸리티 클래스를 사용하여 여러 메서드를 함께 그룹화 할 수있는 경우가 있습니다. java.util.Collections 모든 Java 컬렉션에서 사용할 수있는 여러 유틸리티를 제공하는 클래스가 있습니다. 이들은 특정 유형의 Collection에만 국한된 것이 아니라 모든 Collection에서 사용할 수있는 알고리즘을 구현합니다.

실제로해야 할 일은 디자인에 대해 생각하고 방법을 배치하는 것이 가장 적합한 위치를 결정하는 것입니다. 일반적으로 클래스 내부의 작업입니다. 그러나 때로는 실제로 유틸리티 클래스입니다. 그러나 유틸리티 클래스를 사용할 때는 임의의 메서드를 그 안에 넣지 말고 목적과 기능에 따라 메서드를 구성하십시오.


SOLID oo 원칙에 대한 유틸리티 / 도우미 클래스 검사에 대한이 기사와 잘 연결되어 있습니다. readability.com/articles/rk1vvcqy
melaos

8
언어가 클래스 이외의 네임 스페이스 메커니즘을 제공하지 않는 경우 네임 스페이스가 수행하는 클래스를 남용 할 수밖에 없습니다. C ++에서는 다른 함수와 관련이없는 독립 함수를 네임 스페이스에 넣을 수 있습니다. Java에서는 정적 (클래스) 멤버로 클래스에 넣어야합니다.
Unslander Monica

1
방법으로 그룹화하는 것은 OOP 관점에서 표준 일 수 있습니다. 그러나 OOP가 해결책이되는 경우는 드뭅니다 (상속에 의한 코드 재사용의 엄청나게 깨어진 의미가 실제로 적합한 인스턴스 중 하나 인 상위 수준 아키텍처와 위젯 툴킷은 예외입니다). 일반적으로 메서드는 결합과 종속성을 증가시킵니다. 간단한 예 : 프린터 / 스캐너 메서드를 클래스에 메서드로 제공하면 클래스를 프린터 / 스캐너 라이브러리에 연결합니다. 또한 가능한 구현 수를 효과적으로 하나로 수정합니다. 인터페이스를 던져서 종속성을 더 늘리지 않는 한.
Jo So

한편, "목적 및 기능별 그룹화"에 동의합니다. 프린터 / 스캐너 예제를 다시 살펴보고 공통 목적을위한 디자인 인 콘서트 클래스 세트부터 시작해 보겠습니다. 디버그 코드를 작성하고 클래스에 대한 텍스트 표현을 디자인 할 수 있습니다. 모든 클래스와 프린터 라이브러리에 의존 하는 단일 파일 에서 디버그 프린터를 구현할 수 있습니다 . 디버깅하지 않는 코드는이 구현의 종속성에 부담을주지 않습니다.
Jo So

1
Util 및 Helper 클래스는 그러한 제약의 불행한 결과물이며 OOP를 이해하지 못하거나 문제 영역을 이해하지 못하는 사람들은 계속해서 절차 코드를 작성했습니다.
bilelovitch

57

일반적인 합의는 유틸리티 클래스가 그 자체로 악하지 않다는 것 입니다. 다음과 같이 신중하게 사용해야합니다.

  • 정적 유틸리티 메소드를 일반적이고 재사용 가능하도록 설계하십시오. 상태 비 저장인지 확인하십시오. 즉, 정적 변수가 없습니다.

  • 유틸리티 메서드가 많으면 개발자가 쉽게 찾을 수 있도록 클래스로 분할합니다.

  • 도메인 클래스의 정적 또는 인스턴스 메서드가 더 나은 솔루션이 될 수있는 유틸리티 클래스를 사용하지 마십시오. 예를 들어, 추상 기본 클래스 또는 인스턴스화 가능한 도우미 클래스의 메서드가 더 나은 솔루션인지 고려하십시오.

  • Java 8 이상에서는 인터페이스의 "기본 메소드"가 유틸리티 클래스보다 더 나은 옵션 일 수 있습니다. ( 예를 들어 Java 8의 기본 메소드와 추상 클래스가있는 인터페이스를 참조하십시오 .)


이 질문을 보는 또 다른 방법은 인용 된 질문에서 "유틸리티 클래스가"악한 "경우" 라는 말이 밀려 오는 주장 이라는 것을 관찰하는 것입니다 . 나 같은 질문 :

"돼지가 날 수 있다면 우산을 가져 가야하나요?"

위의 질문에서 나는 실제로 돼지가 날 수 있다고 말하는 것이 아닙니다 ... 또는 그들이 날 있다는 제안에 동의합니다 .

전형적인 "xyz is evil" 진술은 극단적 인 관점을 제시함으로써 생각하게 만드는 수사적 장치입니다. 그것들은 거의 (만약 있다면) 문자 그대로 사실의 진술로 의도 된 것이 아닙니다.


16

유틸리티 클래스는이를 지원하는 데이터로 책임을 그룹화하지 못하기 때문에 문제가 있습니다.

그러나 그것들은 매우 유용하며 나는 더 철저한 리팩터링을하는 동안 영구적 인 구조 또는 디딤돌로 항상 구축합니다.

A로부터 클린 코드의 관점 유틸리티 클래스는 단일 책임과 오픈 폐쇄 원칙을 위반. 변경해야 할 이유가 많으며 설계 상 확장 할 수 없습니다. 리팩토링하는 동안에는 중간 부분으로 만 존재해야합니다.


2
예를 들어 Java에서는 클래스의 메서드가 아닌 함수를 만들 수 없으므로 실용적인 문제라고 부릅니다. 이러한 언어에서 "변수가없고 정적 메서드 만있는 클래스"는 유틸리티 함수의 네임 스페이스를 나타내는 관용구입니다. Java에서 이러한 클래스에 직면하면 IMHO가 유효하지 않으며 클래스에 대해 말할 수 없습니다. , class키워드를 사용 하더라도 . 그것은 ... 하나입니다 - 그것은 네임 스페이스처럼 걷고, 네임 스페이스처럼 꽥꽥
Unslander 모니카

2
무뚝뚝하게 미안하지만 "OOP 시스템의 상태 비 저장 정적 메소드 [...] 이상한 것 [...] 철학적 문제"는 극도로 잘못되었습니다. 올바른 코드를 작성하기 쉽기 때문에 상태를 피할 수 있다면 훌륭합니다. x.equals(y)1) 이것이 전달되지 않은 어떤 상태도 수정해서는 안된다는 사실 (그리고 구현을 알지 못하는 상태에 의존 할 수 없다는 사실) 을 작성해야 할 때 깨졌습니다. 2) x" 선호하는 "또는"행동 된 "위치와 비교하여 y3) 나는 x또는의 "정체성 "을 고려하지 않고 y단지 그들의 가치에 관심이 있습니다.
Jo So

4
실제로 현실 세계에서 대부분의 기능은 정적, 상태 비 저장, 수학 함수로 가장 잘 표현됩니다. Math.PI는 변경되어야하는 객체입니까? 숫자의 사인을 얻기 위해 IAbstractRealOperation을 구현하는 AbstractSineCalculator를 실제로 인스턴스화해야합니까?
Jo So

1
@JoSo 저는 무국적 상태와 직접 계산의 가치에 완전히 동의합니다. 나이브 OO는 철학적으로 이에 반대하고 있으며, 그것이 심각한 결함이라고 생각합니다. 필요하지 않은 것의 추상화를 장려하고 (시연했듯이) 실제 계산에 의미있는 추상화를 제공하지 못합니다. 그러나 OO 언어 사용에 대한 균형 잡힌 접근 방식은 모듈 성과 유지 관리 성을 촉진하기 때문에 불변성과 상태 비 저장으로 향합니다.
Alain O'Dea

2
@ AlainO'Dea : 귀하의 의견을 상태 비 저장 기능에 반대하는 것으로 잘못 읽은 것을 알고 있습니다. 저는 제 말투에 대해 사과합니다. 저는 현재 제 첫 Java 프로젝트에 참여하고 있으며 (제가 10 년간의 프로그래밍에서 대부분 OOP를 피한 후) 불분명 한 상태와 의미를 가진 추상적 인 층 아래에 ​​묻혀 있습니다. 그리고 나는 신선한 공기가 필요합니다 :-).
Jo So

9

나는 그것이 악해지기 시작한다고 생각합니다.

1) 너무 커집니다 (이 경우 의미있는 범주로 그룹화).
2) 정적 메서드가 아니어야하는 메서드가 있습니다.

그러나 이러한 조건이 충족되지 않는 한 매우 유용하다고 생각합니다.


"정적 메서드가 아니어야하는 메서드가 있습니다." 이것이 어떻게 가능한지?
Koray Tugay

@KorayTugay 비 정적 Widget.compareTo(Widget widget)대 정적을 고려하십시오 WidgetUtils.compare(Widget widgetOne, Widget widgetTwo). 비교는 정적으로 수행해서는 안되는 일의 예입니다.
ndm13

5

경험의 법칙

이 문제는 두 가지 관점에서 볼 수 있습니다.

  • 사무용 겉옷 *Util 방법은 종종 잘못된 코드 디자인 또는 지연 명명 규칙의 제안입니다.
  • 재사용 가능한 교차 도메인 상태 비 저장 기능을위한 합법적 인 설계 솔루션입니다. 거의 모든 일반적인 문제에 대해 기존 솔루션이 있습니다.

예 1. 올바른 사용법 util 클래스 / 모듈 . 외부 라이브러리 예

대출과 신용 카드를 관리하는 애플리케이션을 작성하고 있다고 가정 해 보겠습니다. 이러한 모듈의 데이터는 웹 서비스를 통해 json형식으로 노출 됩니다. 이론적으로는 객체를에있는 문자열로 수동으로 변환 할 수 json있지만, 이는 바퀴를 재발 명 할 것입니다. 올바른 솔루션은 Java 객체를 원하는 형식으로 변환하는 데 사용되는 외부 라이브러리를 두 모듈에 모두 포함하는 것입니다. (예제 이미지에서는 gson 을 표시 했습니다 )

여기에 이미지 설명 입력


예 2. util클래스 / 모듈 의 올바른 사용법 . 나만의 글쓰기util다른 팀원들에게 변명없이

유스 케이스로 우리는 두 개의 애플리케이션 모듈에서 몇 가지 계산을 수행해야한다고 가정하지만 둘 다 폴란드에 공휴일이 언제 있는지 알아야합니다. 이론상 모듈 내에서 이러한 계산을 수행 할 수 있지만이 기능을 추출하여 모듈을 분리하는 것이 좋습니다.

여기에 작지만 중요한 세부 사항이 있습니다. 작성한 클래스 / 모듈은라고 부르지 HolidayUtil않고 PolishHolidayCalculator. 기능적으로는 util클래스이지만 일반적인 단어는 피할 수있었습니다.

여기에 이미지 설명 입력


1
내가 보는 yEd 팬;) 놀라운 앱.
Sridhar Sarnobat

3

유틸리티 클래스는 더 나은 이름을 생각하기에는 너무 게으르다는 것을 의미하기 때문에 나쁩니다. :)

즉, 나는 게으르다. 때때로 당신은 일을 끝내야하고 당신의 마음은 공란 .. "유틸리티"클래스가 들어 오기 시작하는 때입니다.


3

지금이 질문을 되돌아 보면 C # 확장 메서드가 유틸리티 클래스의 필요성을 완전히 파괴한다고 말하고 싶습니다. 그러나 모든 언어가 이러한 천재적 구조를 가지고있는 것은 아닙니다.

기존 객체에 바로 새 함수를 추가 할 수있는 JavaScript도 있습니다.

하지만 C ++과 같은 오래된 언어에서이 문제를 해결하는 우아한 방법이 있는지 잘 모르겠습니다.

좋은 OO 코드는 작성하기가 다소 어렵고, 좋은 OO를 작성하려면 적절한 기능 코드를 작성하는 것보다 더 많은 시간 / 지식이 필요하기 때문에 찾기가 어렵습니다.

그리고 예산이 부족할 때 상사는 하루 종일 여러 수업을 작성하는 데 항상 기뻐하지 않습니다 ...


3

나는 유틸리티 클래스가 악하다는 데 전적으로 동의하지 않습니다.

유틸리티 클래스는 어떤면에서 OO 원칙을 위반할 수 있지만 항상 나쁜 것은 아닙니다.

예를 들어 value와 일치하는 모든 하위 문자열의 문자열을 정리하는 함수를 원한다고 가정 해보십시오 x.

stl c ++ (현재)는 이것을 직접 지원하지 않습니다.

다음과 같은 다형성 확장을 만들 수 있습니다. std::string .

그러나 문제는 프로젝트에서 사용하는 모든 문자열이 확장 된 문자열 클래스가되기를 정말로 원합니까?

OO가 말이 안되는 때가 있는데, 이것이 바로 그 중 하나입니다. 우리는 프로그램이 다른 프로그램과 호환되기를 원하므로 계속 std::string해서 클래스 StringUtil_(또는 무언가)를 만들 것입니다.

클래스 당 하나의 유틸리티를 고수하는 것이 가장 좋습니다. 모든 클래스에 대해 하나의 유틸리티를 사용하거나 하나의 클래스에 대해 여러 유틸리티를 사용하는 것은 어리석은 일입니다.


2

디자이너가 코드를 넣을 적절한 장소를 생각할 수 없기 때문에 유틸리티를 브랜드화하는 것은 매우 쉽습니다. 진정한 "유틸리티"는 거의 없습니다.

경험상 일반적으로 처음 사용되는 패키지에 코드를 보관 한 다음 나중에 다른 곳에서 실제로 필요하다고 판단되는 경우에만 더 일반적인 위치로 리팩토링합니다. 유일한 예외는 유사 / 관련 기능을 수행하는 패키지가 이미 있고 코드가 여기에 가장 적합한 경우입니다.


2

상태 비 저장 정적 메서드를 포함하는 유틸리티 클래스가 유용 할 수 있습니다. 이들은 종종 단위 테스트가 매우 쉽습니다.



1

대부분의 Util 클래스는 다음과 같은 이유로 좋지 않습니다.

  1. 그들은 방법의 범위를 넓 힙니다. 그들은 그렇지 않으면 비공개가 될 코드를 공개합니다. util 메서드가 별도의 클래스에서 여러 호출자에 의해 필요하고 안정적이라면 (즉, 업데이트가 필요하지 않음) 개인 도우미 메서드를 호출 클래스에 복사하여 붙여 넣는 것이 좋습니다. 일단 API로 노출하면 jar 구성 요소에 대한 공개 진입 점이 무엇인지 이해하기가 더 어려워집니다 (메서드 당 하나의 부모가있는 계층 구조라는 트리 구조를 유지합니다. 이것은 다음과 같은 경우 더 어려운 구성 요소로 정신적으로 분리하는 것이 더 쉽습니다. 여러 부모 메서드에서 호출 된 메서드가 있습니다.)
  2. 그들은 죽은 코드를 초래합니다. 시간이 지남에 따라 Util 메서드는 앱이 발전함에 따라 사용되지 않으며 결국 사용되지 않는 코드로 인해 코드 기반이 오염됩니다. 그것이 비공개로 남아 있었다면 컴파일러는 메서드가 사용되지 않았으며 제거 할 수 있다고 알려줄 것입니다 (가장 좋은 코드는 코드가 전혀 없다는 것입니다). 이러한 방법을 개인용이 아닌 것으로 만들면 사용하지 않는 코드를 제거하는 데 도움이되는 컴퓨터가 무력해질 것입니다. 모든 컴퓨터가 알고있는 다른 jar 파일에서 호출 될 수 있습니다.

정적 라이브러리와 동적 라이브러리에는 몇 가지 비유가 있습니다.


0

클래스에 메서드를 추가 할 수없는 경우 (예 Account: Jr. 개발자의 변경에 대해 잠겨 있음) 다음과 같이 몇 가지 정적 메서드를 Utilities 클래스에 추가합니다.

public static int method01_Account(Object o, String... args) {
    Account acc = (Account)o;
    ...
    return acc.getInt();
}  

1
당신은 저에게 주니어 사람인 것 같습니다. : P이 작업을 수행하는 악의가없는 방법은 "Jr Developer"에게 Account파일 잠금을 해제하도록 정중하게 요청하는 것 입니다. 또는 비 잠금 소스 제어 시스템을 사용하는 것이 좋습니다.
서딥

1
나는 그가 Account파일이 잠겨있어서 Jr. 개발자가 파일을 변경할 수 없다는 것을 의미한다고 가정 합니다. 주니어 개발자로서, 그는 그것을 우회하기 위해 위와 같이했습니다. 즉, 파일에 대한 쓰기 액세스 권한을 얻고 올바르게 수행하는 것이 더 좋습니다.
Doc

아무도 당신이 답변을 제공하는 이유의 전체 맥락이있는 경우 downvotes 거친 것 때문에 여기에 하나를 추가
joelc

0

유틸리티 클래스가 항상 악한 것은 아닙니다. 그러나 광범위한 기능에 공통적 인 메서드 만 포함해야합니다. 제한된 수의 클래스 중에서 만 사용할 수있는 메서드가있는 경우 추상 클래스를 공통 부모로 만들고 그 안에 메서드를 넣는 것이 좋습니다.

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