Java에서 문자열을 사용하지 않습니까? [닫은]


73

코드에 의미가 부족하도록 Java에서 Strings를 사용하지 않는 블로그 항목을 우연히 발견 했습니다. 대신 얇은 래퍼 클래스를 사용해야한다고 제안했습니다. 이것은 해당 항목이 문제를 설명하기 위해 제공하는 이전 및 이후 예제입니다.

public void bookTicket(
  String name,
  String firstName,
  String film,
  int count,
  String cinema);

public void bookTicket(
  Name name,
  FirstName firstName,
  Film film,
  Count count,
  Cinema cinema);

프로그래밍 블로그를 읽은 경험에서 90 %가 말도 안된다는 결론에 도달했지만 이것이 유효한지 궁금합니다. 어쨌든 그것은 나에게 옳은 느낌이 들지 않지만 프로그래밍 스타일로 무엇이 잘못되었는지 정확히 알 수 없었습니다.


원래 위키에 대한 관련 토론 : "의 toString ()은 무엇입니까?"
Christoffer Hammarström

5
오,와, 그 블로그 포스트는 나를 신체적으로 아프게했다
Rob

5
필자는 모든 기본 유형을 문제 도메인의 이름으로 지정하는 것을 권장하는 최소한 한 권의 책 (코드 완성의 첫 번째 버전 일 수 있음)을 읽었습니다. 같은 생각입니다.
user16764

9
"never"로 시작하는 문장은 "always"false입니다!
Florents Tselai

1
그 남자는 CTO ?!
Hyangelo 2016 년

답변:


88

캡슐화는 프로그램이 변경되지 않도록 보호합니다 . 이름의 표현이 바뀔까요? 그렇지 않으면 시간을 낭비하고 YAGNI가 적용됩니다.

편집 : 나는 블로그 게시물을 읽었으며 기본적으로 좋은 아이디어를 가지고 있습니다. 문제는 그가 너무 멀리 확장되었다는 것입니다. 아마도 유효하지 않기 때문에 같은 String orderId 것이 정말 나쁩니다 . 이는 유효한 값보다 훨씬 많은 값 을 나타냅니다 . 그러나, 같은 것을 위해 , 당신은 아마도 또는 유효한 이름이 될 수도 있고, 그렇지 않을 수도 예측할 수 없으며, 어떤은 유효하다 ."!"£$%^&*())ADAFVForderIdStringorderIdnameStringname

첫 번째 경우, 가능한 입력을 유효한 입력으로 만 (올바르게) 줄입니다. 두 번째 경우에는 가능한 유효한 입력 범위를 좁히지 않았습니다.

다시 편집 : 유효하지 않은 입력의 경우를 고려하십시오. "Gareth Gobulcoque"를 당신의 이름으로 쓰면 어리석게 보일 것입니다. 그것은 세상의 종말이 아닙니다. 잘못된 OrderID를 입력하면 단순히 작동하지 않을 가능성이 있습니다.


5
주문 ID를 사용하면 ID를 수락하고 문자열을 떠나는 코드에 검사를 추가하는 것이 좋습니다. 클래스는 데이터 작업을위한 메소드를 제공해야합니다. 일부 클래스가 일부 데이터의 유효성 만 검사 한 다음 아무 것도 수행하지 않으면 적절하지 않은 것 같습니다. OOP는 좋지만 과용해서는 안됩니다.
Malcolm

13
@ Malcolm : 그러나 반복적으로 유효성을 검사하는 것 외에 어떤 문자열이 유효성 검사되는지 알 수있는 방법이 없습니다.
DeadMG

7
"[...] 유효한 이름 일 수도 있고 아닐 수도있는 것을 예측할 수 없으며 모든 문자열이 유효한 이름입니다." 이 기술의 장점 중 하나는 매개 변수가 유형 Name인 경우 실수로 다른 관련없는 String 값을 전달할 수 없다는 것입니다. 당신이 기대하는 곳에 NameName의지 만 컴파일되며 문자열 "Iowe you $ 5"는 실수로 받아 들일 수 없습니다. (이것은 모든 종류의 이름 확인과 관련이 없습니다!)
Andres F.

6
특정 용도에 맞는 유형을 만들면 의미가 풍부 해지고 안전성이 향상됩니다. 또한 IoC 컨테이너를 사용하여 클래스를 자동 와이어 링 할 때 특정 값을 나타내는 유형을 작성하면 많은 도움이됩니다. 특정 클래스에 등록 된 유일한 Bean 인 경우 올바른 Bean을 쉽게 분석 할 수 있습니다. 등록 된 많은 문자열 중 하나 일 경우 훨씬 더 많은 노력이 필요합니다.
Dathan

3
@DeadMG 그것은 논쟁의 여지가 있으며 의견에서 해결할 수있는 것이 아닙니다. 기본 유형의 값을 잘못 바꾸면 인터페이스를 강화하는 것이 상황을 개선하는 한 가지 방법입니다.
Andres F.

46

그건 미친 짓이야


2
저도 제 의견이지만 "Code Complete"에서이 제안을 기억합니다. 물론 그 책의 모든 것이 논란의 여지가 없지만 적어도 아이디어를 거부하기 전에 두 번 생각하게합니다.
DPM

8
당신은 아무런 정당화없이 슬로건을 언급했습니다. 당신의 의견에 대해 자세히 설명해 주시겠습니까? 예를 들어, 일부 허용되는 패턴 및 언어 기능은 추가 된 복잡성으로 보이지만 값이 큰 값을 제공합니다 (예 : 정적 입력)
Andres F.

12
상식은 상식입니다.
Kaz Dragon

1
+1, 이것에 언어가 명명 된 매개 변수를 지원하고 IDE가 좋을 때 C # 및 VS2010과 같은 경우에는 미친 패턴을 가진 언어의 기능 부족을 보상 할 필요가 없습니다. . X라는 클래스와 Y라는 클래스가 필요하지 않습니다. 하나만 쓸 수 있다면 var p = new Point(x: 10, y:20);Cinema와 문자열이 다릅니다. 압력, 온도, 에너지 등 단위가 다르고 일부는 음수 일 수없는 물리량을 처리해야하는지 이해합니다. 블로그 작성자는 기능을 시도해야합니다.
직업

1
"과도한 엔지니어가 아닙니다!"+1
Ivan

23

나는 대부분 저자에 동의한다. 주문 ID의 유효성 검사와 같이 필드에 고유 동작 이 있으면 해당 유형을 나타내는 클래스를 만듭니다. 그의 두 번째 요점은 더욱 중요합니다. 주소와 같은 개념을 나타내는 필드 세트가 있으면 해당 개념에 대한 클래스를 작성하십시오. Java로 프로그래밍하는 경우 정적 입력 비용이 많이 듭니다. 당신은 또한 당신이 할 수있는 모든 가치를 얻을 수 있습니다.


2
downvoter가 댓글을 달 수 있습니까?
kevin cline

정적 타이핑에 대해 지불하는 높은 가격은 얼마입니까?
Richard Tingle

@RichardTingle : 코드를 다시 컴파일하고 모든 코드 변경에 대해 JVM을 다시 시작하는 시간입니다. 소스 호환 가능하지만 바이너리 호환 불가능한 변경을 수행 할 때 손실되는 시간과 재 컴파일해야 할 사항이 없으며 "MissingMethodException"이 발생합니다.
케빈 클라인

나는 (이 일반적으로 이미 내가 실행에 충돌 시간 컴파일의 지속적인 컴파일에 무엇을)하지만 재배포 조금 느린 것처럼 보인다되는 웹 전쟁과 공정한 점의 Java 응용 프로그램에 문제를 경험 한 적이
리처드 설렘

16

하지 마라. 그것은 너무 복잡하게 될 것이고 당신은 그것을 필요로하지 않을 것입니다

... 2 년 전 제가 여기에 쓴 답입니다. 하지만 지금은 확실하지 않습니다. 사실, 최근 몇 달 동안 나는 더 나은 방법이 없기 때문에가 아니라 새로운 기능을 구현하거나 기존 코드를 변경하기 위해 진정으로 필요했기 때문에 오래된 코드를이 형식으로 마이그레이션하기 시작했습니다. 나는 다른 사람들이 그 코드를 보는 자동 혐오를 이해하지만 이것이 진지한 생각이 필요하다고 생각합니다.


혜택

이점 중 가장 중요한 것은 코드 를 수정하고 확장 하는 기능입니다. 당신이 사용하는 경우

class Point {
    int x,y;
    // other point operations
}

불행히도 많은 인터페이스가 수행하는 몇 가지 정수를 전달하는 대신 나중에 다른 차원을 추가하는 것이 훨씬 쉬워집니다. 또는 유형을로 변경하십시오 double. 나중에 List<Author> authors또는 List<Person> authors대신 사용 List<String> authors하면 저자가 나타내는 내용에 더 많은 정보를 추가하기가 훨씬 쉬워집니다. 이와 같이 적어두면 명백한 것을 말하는 것처럼 느껴지지만 실제로는 문자열을 여러 번 사용하는 것이 유죄였습니다. 특히 시작시 명확하지 않은 경우에는 더 이상 필요합니다. 문자열.

현재 코드 전체에 얽혀있는 문자열 목록을 리팩터링하려고합니다. 추가 정보가 필요하기 때문에 고통 스럽습니다.

그 외에도 블로그의 저자 는 더 의미적인 정보 를 제공하므로 독자가 이해하기 쉽도록 동의합니다 . 매개 변수에는 종종 의미있는 이름이 지정되고 전용 문서 라인이 제공되지만 필드 나 로컬의 경우에는 그렇지 않습니다.

마지막 이점은 명백한 이유로 유형 안전성입니다 . 그러나 제 눈에는 이것이 사소한 것입니다.

단점

쓰는 데 시간이 더 걸립니다 . 작은 수업을 작성하는 것은 빠르고 쉬운 일이지만 쉽지는 않습니다 . 특히 이러한 수업이 많이 필요한 경우에는 더욱 그렇습니다. 새로운 래퍼 클래스를 작성하기 위해 3 분마다 멈 추면 집중력을 떨어 뜨릴 수 있습니다. 그러나이 노력 상태는 일반적으로 코드를 작성하는 첫 단계에서만 발생한다고 생각하고 싶습니다. 나는 일반적으로 어떤 주체가 관여해야하는지에 대한 좋은 아이디어를 빨리 얻을 수 있습니다.

여기에는 많은 중복 세터 (또는 구성) 및 게터가 포함될 수 있습니다 . 블로그 작성자의 진정한 추한 예를주는 new Point(x(10), y(10))대신을 new Point(10, 10), 그리고 내가 사용도 같은 물건을 포함 할 수 있음을 추가 할 Math.max(p.x.get(), p.y.get())대신 Math.max(p.x, p.y). 그리고 긴 코드는 종종 읽기 어려운 것으로 간주됩니다. 그러나 모든 솔직히 말해서, 많은 코드가 객체를 옮기는 느낌 이 들며 선택 방법만으로 객체를 생성하고 거친 세부 사항에 액세스 할 필요가 거의 없습니다 (어쨌든 OOPy는 아닙니다).

논쟁의 여지가있는

이것이 코드 의 가독성 에 도움이되는지 여부 는 논쟁의 여지가 있습니다. 더 의미적인 정보이지만 더 긴 코드입니다. 예, 각 지역의 역할을 이해하는 것이 더 쉽지만 문서를 읽지 않으면 할 수있는 일을 이해하기가 더 어렵습니다.


다른 대부분의 프로그래밍 학교와 마찬가지로, 이것을 하나의 극단으로 가져가는 것은 건강하지 않다고 생각합니다. 나는 x와 y 좌표를 각각 다른 유형으로 분리하는 것을 보지 못했습니다. 생각하지 않는다 나는 Count때 필요 int충분합니다. 나는 unsigned intC 의 사용법을 싫어한다 -이론적으로는 좋지만 충분한 정보를 제공하지는 않으며 나중에 마법의 -1을 지원하도록 코드를 확장하는 것을 금지한다. 때로는 단순성이 필요합니다.

블로그 게시물은 극단적 인 부분이라고 생각합니다. 그러나 전반적으로, 나는 고통스러운 경험에서 그 기본 아이디어가 올바른 것들로부터 만들어 졌다는 것을 배웠다.

과도하게 엔지니어링 된 코드에 대한 혐오가 있습니다. 나는 정말로한다. 그러나 올바르게 사용하면이 과도하게 생각하지 않습니다.


5

그것은 과잉 일이지만, 내가 본 대부분의 물건은 대신 엔지니어링되지 않은 것으로 생각합니다.

"안전성"만이 아닙니다. Java의 가장 좋은 점 중 하나는 주어진 라이브러리 메소드 호출에 필요한 것만 기억하고 구성하는 데 많은 도움이된다는 것입니다.

내가 작업했던 WORST (지금까지) Java 라이브러리는 Smalltalk를 매우 좋아하는 누군가가 작성했으며, Smalltalk처럼 작동하도록 GUI 라이브러리를 모델링하여 모든 메소드가 동일한 기본 개체를 가져야한다는 문제를 그러나 실제로 기본 객체가 캐스트 할 수있는 모든 것을 사용할 수는 없었기 때문에 메소드에 전달할 항목을 추측하고 런타임까지 실패했는지 알지 못했습니다. (저는 매번 처리해야했습니다. C에서 일하고 있었다).

또 다른 문제-객체가없는 문자열, 정수, 컬렉션 및 배열을 전달하면 아무런 의미가없는 데이터 볼만 있으면됩니다. "일부 앱"이 사용할 라이브러리 측면에서 생각할 때 자연스럽게 보이지만 전체 앱을 디자인 할 때 데이터가 정의되어 있고 생각 만하는 곳에서 모든 데이터에 의미 (코드)를 할당하는 것이 훨씬 도움이됩니다. 이러한 고급 개체가 상호 작용하는 방식에 대한 용어. 객체 대신 프리미티브를 전달하는 경우 정의에 따라 정의 된 위치와 다른 곳에서 데이터를 변경하는 것입니다 (이것은 또한 실제로 Setter 및 Getter를 좋아하지 않는 이유와 동일합니다). 귀하가 아닌 데이터).

마지막으로, 모든 것에 대해 별도의 객체를 정의하는 경우 항상 모든 것을 검증 할 수있는 좋은 장소가 있습니다. 예를 들어 우편 번호에 대한 객체를 생성 한 후 나중에 우편 번호에 항상 4 자리 확장자가 포함되어 있는지 확인해야합니다. 그것을 넣어 완벽한 장소.

실제로 나쁜 생각은 아닙니다. 그것에 대해 생각할 때 나는 그것이 너무 과도하게 엔지니어링되었다고 확신조차하지 못합니다. 거의 모든 방식으로 작업하는 것이 더 쉽습니다. 한 예외는 작은 클래스의 확산이지만 Java 클래스는 너무 가볍고 쉽습니다. 이것은 비용이 거의 들지 않는다는 것입니다 (생성 할 수도 있음).

실제로 너무 많은 클래스가 정의되어있는 잘 작성된 Java 프로젝트 (프로그래밍하기가 어려워진 곳)를 보는 데 관심이 있으니 너무 많은 클래스를 가질 수 없다고 생각하기 시작했습니다.


3

이 개념을 다른 출발점에서 봐야한다고 생각합니다. 데이터베이스 디자이너의 관점에서 살펴보십시오. 첫 번째 예제에서 전달 된 유형은 유용한 방법은 물론 고유 한 방식으로 매개 변수를 정의하지 않습니다.

public void bookTicket(
  String name,
  String firstName,
  String film,
  int count,
  String cinema);

티켓을 예약하는 실제 고객을 지정하려면 두 개의 매개 변수가 필요합니다. 이름이 동일한 두 개의 다른 영화 (예 : 리메이크)가 있고 이름이 다른 동일한 영화가있을 수 있습니다 (예 : 번역). 예를 들어, 사용중인 (영화관의 특정 체인은 다른 지점이있을 수 있습니다, 그래서 당신은 어떻게 문자열과 일관성있는 방법으로 그와 거래를하려고 $chain ($city)하거나 $chain in $city또는 뭔가 다른 방법이이 있는지 확인하려고 최악의 경우 실제로 두 개의 매개 변수를 사용하여 고객을 지정하는 것입니다. 이름과 성이 모두 제공된다는 사실은 유효한 고객을 보장하지 않으며 두 가지를 구별 할 수 없습니다 John Doe.

이것에 대한 대답은 유형을 선언하는 것이지만, 위에서 보여준 것처럼 얇은 래퍼는 거의 없습니다. 대부분 데이터 저장소로 작동하거나 일종의 데이터베이스와 연결될 것입니다. 따라서 Cinema객체에는 이름, 위치 등이있을 수 있으며 이러한 방식으로 이러한 모호성을 제거 할 수 있습니다. 그것들이 얇은 포장지라면 우연의 일치입니다.

따라서 IMHO 블로그 게시물은 "올바른 유형을 전달해야합니다"라고 말하고 있으며 저자는 특히 기본 데이터 유형 (잘못된 메시지)을 선택하기 위해 지나치게 제한적인 선택을했습니다.

제안 된 대안이 더 좋습니다.

public void bookTicket(
  Name name,
  FirstName firstName,
  Film film,
  Count count,
  Cinema cinema);

다른 한편으로, 나는 블로그 게시물이 모든 것을 감싸는 데 너무 멀다고 생각합니다. Count너무 일반적이기 때문에 사과 또는 오렌지를 계산하고 추가 할 수 있으며 유형 시스템이 무의미한 작업을 수행 할 수있는 상황이 여전히 있습니다. 물론 블로그에서와 같은 논리를 적용하고 유형 CountOfOranges등을 정의 할 수 있지만, 그것은 또한 어리석은 일입니다.

그만한 가치가 있기 때문에 실제로는

public Ticket bookTicket(
  Person patron,
  Film film,
  int numberOfTickets,
  Cinema cinema);

간단히 말해서 : 당신은 무의미한 변수를 전달해서는 안됩니다. 실제 객체를 결정하지 않는 값으로 객체를 실제로 지정하는 유일한 경우는 쿼리를 실행할 때 (예 :) public Collection<Film> findFilmsWithTitle(String title)또는 개념 증명을 정리할 때입니다. 유형 시스템을 깨끗하게 유지하십시오. 너무 일반적인 유형 (예 :로 표시되는 영화 String) 또는 너무 제한적 / 특정 / 구분 된 유형 (예 : Count대신 int)을 사용하지 마십시오 . 가능하고 실행 가능할 때마다 객체를 고유하고 명확하게 정의하는 유형을 사용하십시오.

편집 : 더 짧은 요약. 소규모 어플리케이션 (예 : 개념 증명)의 경우 : 복잡한 설계로 귀찮게하는 이유는 무엇입니까? 그냥 사용 String하거나 사용 int하십시오.

대규모 응용 프로그램의 경우 : 기본 데이터 형식의 단일 필드로 구성된 클래스가 많이있을 가능성이 있습니까? 그러한 클래스가 거의 없다면 "정상적인"객체 만 있고 특별한 것은 없습니다.

나는 문자열을 캡슐화한다는 아이디어를 느낍니다. ... 불완전한 디자인입니다. 소규모 응용 프로그램에는 지나치게 복잡하지만 큰 응용 프로그램에는 충분하지 않습니다.


나는 당신의 요점을 알지만 저자가 말한 것보다 더 많이 가정하고 있다고 주장합니다. 우리 모두가 알고 있듯이, 그의 모델에는 후원자 용 String, 영화 용 String 등 만 있습니다. 그 가설적인 추가 기능은 실제로 문제의 핵심이므로, 그는 자신의 주장을 할 때 생략하거나 "우리가 더 의미 론적 힘을 제공해야한다고 생각한다"고 생각했다. 다시 한 번, 우리가 알고있는 모든 것은 그가 생각한 후자입니다.
DPM

@ Jubat : 실제로 저자가 말하는 것 이상을 가정합니다. 그러나 내 요점은 당신이 간단한 응용 프로그램을 가지고 있다는 것입니다.이 경우 어느 쪽이든 지나치게 복잡합니다. 이러한 규모에서 유지 관리 성은 문제가되지 않으며 의미 론적 구분으로 인해 코딩 속도가 저하됩니다. 반면에 응용 프로그램이 크면 유형을 올바르게 정의하는 것이 좋습니다 (단순 래퍼는 아닐 것입니다). IMHO의 그의 사례는 그가 설득하려는 시점을 넘어 설득력이 없거나 주요한 설계 결함이 없습니다.
Egon

2

나 에게이 작업은 C #에서 영역을 사용하는 것과 같습니다. 일반적으로 코드를 읽을 수있게하기 위해 이것이 필요하다고 생각되면 더 큰 문제가 있습니다.


2
원인이 아닌 증상의 치료를 암시하는 +1
직업

2

나는 typedef와 같은 강력한 typedef 기능을 가진 언어에서 정말 좋은 아이디어라고 말합니다.

Java에서는 이것이 없기 때문에 이러한 것들에 대한 완전히 새로운 클래스를 만들면 비용이 아마도 이점보다 중요합니다. 변수 / 매개 변수 이름 지정에주의하여 80 %의 이점을 얻을 수도 있습니다.


0

IF String (정수 정수 및 ... String에 대해서만 말하기)이 최종적이지 않았 으므로이 클래스는 의미가있는 일부 (제한 된) String 일 수 있으며 처리 방법을 알고있는 독립적 인 객체로 보낼 수 있습니다 기본 유형 (앞뒤로 대화하지 않고).

그리고 그것의 "goodnes"는 예를 들어 증가 할 때 증가합니다. 모든 이름에 대한 제한.

그러나 라이브러리가 아닌 일부 앱을 만들 때 항상 리팩터링 할 수 있습니다. 그래서 나는 그것없이 시작하는 prefare.


0

예를 들어, 현재 개발 된 시스템에는 여러 종류의 식별자 (외부 시스템의 사용으로 인해)로 식별 할 수있는 여러 가지 다른 개체가 있으며 때로는 같은 종류의 개체 일 수도 있습니다. 모든 식별자는 문자열이므로 누군가가 어떤 종류의 ID를 매개 변수로 전달해야하는지 혼합하면 컴파일 시간 오류가 표시되지 않지만 런타임에 프로그램이 종료됩니다. 이것은 자주 발생합니다. 따라서이 원칙의 주요 목적은 변화로부터 보호하는 것이 아니라 (그것도 마찬가지 임) 버그로부터 자신을 보호하는 것입니다. 어쨌든 누군가 API를 디자인하는 경우 도메인의 개념을 반영해야하므로 개념적으로 도메인 별 클래스를 정의하는 것이 유용합니다. 모든 것은 개발자의 마음에 순서가 있는지 여부에 따라 다릅니다.


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