태그 보관물: anti-patterns

anti-patterns

스트림 요소를 수정하기 위해 peek ()를 사용하는 것이 반 패턴입니까? peek()대부분의 상황에서

내가 스트림을 가지고 있고 스트림 중간에 “풍부하게”하고 싶다고 가정 해 보겠습니다 peek(). 예를 들면 다음과 같습니다.

streamOfThings.peek(this::thingMutator).forEach(this::someConsumer);

코드에서이 시점에서 Things를 변경하는 것이 올바른 동작이라고 가정합니다. 예를 들어, thingMutator메소드는 “lastProcessed”필드를 현재 시간으로 설정할 수 있습니다.

그러나 peek()대부분의 상황에서 “보이지만 만지지 마십시오”를 의미합니다.

스트림 요소 peek()음소거 하는 데 사용 하는 것이 반 패턴이거나 잘못 권장됩니까?

편집하다:

보다 전통적인 대안은 소비자를 전환하는 것입니다.

private void thingMutator(Thing thing) {
    thing.setLastProcessed(System.currentTimeMillis());
}

매개 변수를 반환하는 함수로

private Thing thingMutator(Thing thing) {
    thing.setLastProcessed(currentTimeMillis());
    return thing;
}

사용이 map()대신 :

stream.map(this::thingMutator)...

그러나 그것은 perfunctory 코드 ( return) 를 소개 peek()하며 동일한 객체를 반환한다는 것을 알고 있기 때문에 더 명확하다고 확신 하지는 않지만 map()동일한 클래스 의 객체 라는 것을 한눈에 알 수는 없습니다 .

또한, peek()당신은 돌연변이 람다를 가질 수 있지만 map(), 기차 난파선을 만들어야합니다. 비교:

stream.peek(t -> t.setLastProcessed(currentTimeMillis())).forEach(...)
stream.map(t -> {t.setLastProcessed(currentTimeMillis()); return t;}).forEach(...)

나는 peek()버전이 더 깨끗하고 람다는 분명히 변하고 있다고 생각 하므로 “신비한”부작용은 없다. 유사하게, 방법 참조가 사용되고 방법의 이름이 돌연변이를 분명히 암시한다면, 그것도 분명하고 명백하다.

개인적으로, 나는 peek()돌연변이 를 사용하여 부끄러워하지 않습니다 . 매우 편리합니다.



답변

영어 단어의 의미에서 “피킹”은 “보이지만 만지지 마십시오”를 의미합니다.

그러나 의 JavaDoc의 상태 :

몰래 엿보다

스트림 엿보기 (소비자 행동)

이 스트림의 요소로 구성된 스트림을 리턴하고 결과 스트림에서 요소가 소비 될 때 각 요소에 대해 제공된 조치를 추가로 수행합니다.

핵심 단어 : “수행 중 … 작업”및 “소비”. JavaDoc은 peek스트림을 수정할 수있는 능력이 있어야한다는 점을 분명히 알고 있습니다.

그러나 JavaDoc은 또한 다음과 같이 말합니다.

API 참고 사항 :

이 방법은 주로 디버깅을 지원하기 위해 존재하며 파이프 라인의 특정 지점을지나 가면서 요소를 확인하려는 경우

이것은 예를 들어 스트림의 요소 로깅과 같은 관찰을위한 것임을 나타냅니다.

이 모든 것에서 취한 것은 스트림의 요소를 사용 하여 작업 수행 할 수 있지만 스트림의 요소를 변경 하지 않아야한다는 것 입니다. 예를 들어, 객체에 대해 메소드를 호출하지만 객체에 대한 조작을 피하십시오.

최소한 다음 줄을 따라 코드에 간단한 주석을 추가합니다.

// Note: this peek() call will modify the Things in the stream.
streamOfThings.peek(this::thingMutator).forEach(this::someConsumer);

이러한 의견의 유용성에 대한 의견은 다르지만이 경우 그러한 의견을 사용합니다.


답변

쉽게 잘못 해석 될 수 있으므로 이와 같이 사용하지 않는 것이 좋습니다. 아마도 가장 좋은 옵션은 람다 함수를 사용하여 필요한 두 동작을 forEach 호출에 병합하는 것입니다. 기존 객체를 변경하지 않고 새 객체를 반환하는 것을 고려할 수도 있습니다. 약간 덜 효율적이지만 더 읽기 쉬운 코드가 생성 될 수 있으며 수정 된 목록을 사용해야하는 다른 작업에 실수로 수정 ​​된 목록을 사용할 가능성이 줄어 듭니다. 원본을 받았습니다.


답변

API 노트는이 메소드가 디버깅 / 로깅 / 발사 통계 등과 같은 작업에 주로 추가되었음을 알려줍니다.

@apiNote이 방법은 주로 디버깅을 지원하기 위해 존재합니다. 여기서 파이프 라인의 특정 지점을지나 가면서 요소를 볼 수 있습니다. *

       {@암호
     * Stream.of ( "1", "2", "3", "4")
     * .filter (e-> e.length ()> 3)
     * .peek (e-> System.out.println ( "필터링 된 값 :"+ e))
     * .map (문자열 :: toUpperCase)
     * .peek (e-> System.out.println ( "매핑 된 값 :"+ e))
     * .collect (Collectors.toList ());
     *}


답변