들어오는 매개 변수를 수정하는 것이 반 패턴입니까? [닫은]


60

Java로 프로그래밍하고 있으며 항상 다음과 같은 변환기를 만듭니다.

public OtherObject MyObject2OtherObject(MyObject mo){
    ... Do the conversion
    return otherObject;
}

새로운 직장에서 패턴은 다음과 같습니다.

public void MyObject2OtherObject(MyObject mo, OtherObject oo){
    ... Do the conversion
}

들어오는 매개 변수를 변경하지 않는 데 익숙해 져서 약간 냄새가납니다. 이 수신 매개 변수 변경이 반 패턴입니까? 심각한 결점이 있습니까?


4
값별 매개 변수가 효과적으로 로컬 변수로 바뀌기 때문에 정답은 언어마다 다릅니다.
Blrfl

1
두 번째 패턴은 때때로 객체를 재사용하기위한 효율 측정으로 사용됩니다. 그것이 oo새로운 객체로 설정된 포인터가 아니라 메소드에 전달되는 객체 라고 가정 합니다. 이 경우입니까? 이것이 자바라면 아마 C ++라면 아닐 것이다
Richard Tingle

3
이것은 조기 최적화처럼 보입니다. 일반적으로 첫 번째 양식을 사용하는 것이 좋지만 성능 병목 현상이 발생하면 두 번째 양식 을 주석과 함께 사용하여 정당화 할 수 있습니다. 주석을 달면 자신과 다른 사람이 해당 코드를보고 동일한 질문을 다시 볼 수 없게됩니다.
Keen

2
두 번째 패턴은 함수가 값을 반환 할 때 유용합니다 (예 : 성공 / 실패). 그러나 실제로 '잘못된'것은 없습니다. 실제로 객체를 만들려는 위치에 따라 다릅니다.
GrandmasterB

12
이 두 가지 패턴을 구현하는 함수의 이름을 같은 방식으로 지정하지는 않겠습니다.
Casey

답변:


70

반 패턴이 아니며 나쁜 습관입니다.

안티 패턴과 단순한 나쁜 습관의 차이점은 여기에 있습니다 : 안티 패턴 정의 .

Uncle Bob의 Clean Code에 따르면, 새로운 직장 스타일은 나쁜 습관 , 흔적 또는 OOP 이전 시간입니다.

인수는 가장 자연스럽게 함수에 대한 입력으로 해석됩니다.

함수 서명을 강제로 확인하는 것은 이중 테이크와 같습니다. 인지 적 휴식이므로 피해야합니다. 객체 지향 프로그래밍 이전에는 때로는 출력 인수가 필요했습니다. 그러나 OO 언어에서는 출력 인수가 필요하지 않습니다.


7
왜 당신이 연결 한 정의가 이것을 반 패턴으로 간주하지 못하게하는지 모르겠습니다.
Samuel Edwin Ward

7
그래서이라는 이론적 근거는 "우리가 대신 우리는 우리가 하나를 재활용하고 인수로 전달해야한다, 새로운 객체를 생성하지 않음으로써 CPU / MEM을 절약하고 있습니다"고 그것 때문에 가정 분명히 잘못 에 대해 모두 심각한 OOP 배경이 그와 함께 할 수 패턴을 구성 하지 않습니다 -그래서 우리가 정의에 의해 반 패턴으로 간주 할 수있는 방법은 없습니다 ...
vaxquis

1
입력 매개 변수가 수정해야하는 대규모 개체 모음 인 경우는 어떻습니까?
Steve Chambers

옵션 1도 선호하지만 OtherObject인터페이스는 어떻게 되나요? 발신자 만 (희망적으로) 원하는 콘크리트 유형을 알고 있습니다.
user949300

@SteveChambers이 경우 클래스 또는 메서드의 디자인이 나쁘고이를 피하기 위해 리팩터링해야한다고 생각합니다.
piotr.wittchen

16

Robert C. Martin의 유명한 저서 "Clean Code"인용 :

출력 인수는 피해야합니다

함수는 적은 수의 인수를 가져야합니다

두 번째 패턴은 두 규칙, 특히 "출력 인수"규칙을 모두 위반합니다. 이런 의미에서 첫 번째 패턴보다 나쁩니다.


1
나는 그것이 첫 번째 규칙을 위반한다고 말하고 많은 논쟁은 지저분한 코드를 의미하지만 두 가지 주장은 완전히 괜찮다고 생각합니다. 깨끗한 코드에 대한 규칙은 5 또는 6 개의 수신 데이터가 필요한 경우 단일 방법으로 너무 많은 것을 원하므로 코드 가독성 및 OOP 범위를 늘리기 위해 리팩터링해야한다고 생각합니다.
CsBalazs 헝가리

2
어쨌든 나는 이것이 엄격한 법이 아니라 강한 제안이라고 생각합니다. 나는 그것이 기본 유형에서 작동하지 않는다는 것을 알고 있습니다. 프로젝트에서 광범위하게 퍼져있는 패턴이라면, 주니어는 아이디어를 int로 전달하고 객체와 같이 변경 될 것으로 기대합니다.
CsBalazs 헝가리

27
Clean Code가 얼마나 정확한지에 관계없이 이것이 왜 피해야 하는지를 설명하지 않고 가치있는 답변이라고 생각하지 않습니다 . 그들은 돌판에 내려 오는 계명이 아니므로 이해가 중요합니다. 이 답변은이 책의 추론에 대한 요약과 장 참조를 제공함으로써 개선 될 수 있습니다.
Daenyth

3
글쎄, 어떤 사람이 책에 그것을 쓴다면 그것은 상상할 수있는 모든 상황에 대해 좋은 조언 이 되어야합니다 . 생각할 필요가 없습니다.
케이시

2
@emodendroket 그러나 친구, 그건 사람!
Pierre Arlaud

15

두 번째 관용구는 더 빠를 수 있습니다. 호출자가 새 인스턴스를 생성 할 때마다 긴 루프 대신 하나의 변수를 재사용 할 수 있기 때문입니다.

나는 일반적으로 사용하지 않지만 예를 들어. 게임 프로그래밍에서 그 자리가 있습니다. 예를 들어 JavaMonkey의 Vector3f 많은 연산을 통해 수정하고 결과로 반환 해야하는 인스턴스를 전달할 수 있습니다.


12
글쎄, 나는 이것이 병목 현상인지에 동의 할 수 있지만, 내가 일하는 곳마다 병목 현상은 일반적으로 객체 복제 또는 생성이 아닌 효율성이 낮은 알고리즘입니다. 나는 게임 개발에 대해 덜 알고있다.
CsBalazs 헝가리

4
@CsBalazsHungary 객체 생성이 중요한 문제는 메모리 할당 및 가비지 수집과 관련이있는 경향이 있으며, 이는 알고리즘 복잡성 (특히 스마트 폰 등의 제한된 메모리 환경에서)에 따른 다음 병목 현상 일 수 있습니다. .
JAB

JMonkey는 종종 성능이 중요하기 때문에 이것을 많이 보았습니다. 그래도 "JavaMonkey"라고 들었습니다
Richard Tingle

3
@JAB : JVM의 GC는 임시 개체의 경우에 맞게 특별히 조정되었습니다. 대량의 매우 짧은 수명의 물체는 수집하기가 쉽지 않으며 많은 경우 단일 포인터 이동으로 전체 임시 생성을 수집 할 수 있습니다.
Phoshi

2
사람이해야하는 경우 실제로 객체를 생성 , 그것은 되지 않습니다 성능 효과; 코드 블록을 속도에 맞게 최적화 해야하는 경우 대신 기본 형식과 기본 배열을 사용해야합니다. 그 때문에, 나는이 답변의 진술이 유지되지 않는다고 생각합니다.
vaxquis

10

나는 그것들이 두 개의 동등한 코드 조각이라고 생각하지 않습니다. 첫 번째 경우을 만들어야합니다 otherObject. 두 번째에서 기존 인스턴스를 수정할 수 있습니다. 둘 다 사용하고 있습니다. 코드 냄새가 다른 것을 선호합니다.


나는 그들이 동등하지 않다는 것을 안다. 당신이 옳습니다, 우리는 이러한 것들을 유지하고 있기 때문에 변화를 가져올 것입니다. 반 패턴이 아닌 것은 유스 케이스의 문제 일 뿐입니 까?
CsBalazs 헝가리

@CsBalazsHungary 예라고 말할 것입니다. 제공 한 작은 코드 조각으로 판단하십시오.
Euphoric 2016 년

@claasz와 같이 들어오는 기본 매개 변수가 업데이트되지 않을 경우 더 심각한 문제가되는 것으로 추측합니다. 필요하지 않으면 피해야합니다.
CsBalazs 헝가리

1
@CsBalazsHungary 원시적 인 인수의 경우 함수가 작동하지 않습니다. 따라서 인수를 변경하는 것보다 더 나쁜 문제가 있습니다.
Euphoric 2016 년

주니어가 프리미티브 유형을 출력 매개 변수로 만들려는 함정에 빠질 것이라고 말하고 싶습니다. 따라서 가능한 경우 첫 번째 변환기가 선호되어야한다고 말하고 싶습니다.
CsBalazs 헝가리

7

언어에 따라 다릅니다.

Java에서 두 번째 형식은 안티 패턴 일 수 있지만 일부 언어는 매개 변수 전달을 다르게 처리합니다. 대신 패스 값 또는 참조하여 예 및 아다 VHDL에서는 파라미터 모드를 가질 수있다 in, out또는 in out.

in매개 변수 를 수정하거나 매개 변수를 읽는 것은 오류 out이지만 매개 변수의 변경 사항 in out은 호출자에게 다시 전달됩니다.

따라서 Ada의 두 가지 형식 (이것은 합법적 인 VHDL입니다)

function MyObject2OtherObject(mo : in MyObject) return OtherObject is
begin
    ... Do the conversion
    return otherObject;
end MyObject2OtherObject;

procedure MyObject2OtherObject(mo : in MyObject; oo : out OtherObject) is
begin
    ... Do the conversion
    oo := ... the conversion result;
end MyObject2OtherObject;

둘 다 그들의 용도가있다; 프로시 저는 여러 Out매개 변수 로 여러 값을 리턴 할 수 있지만 함수는 단일 결과 만 리턴 할 수 있습니다. 두 번째 매개 변수의 목적은 프로 시저 선언에 명확하게 명시되어 있으므로이 양식에 반대 할 수 없습니다. 가독성을 선호하는 경향이 있습니다. 그러나 호출자가 이미 객체를 만든 경우와 같이 절차가 더 나은 경우가 있습니다.


3

그것은 실제로 냄새가 나는 것처럼 보이며 더 많은 맥락을 보지 않으면 확실히 말할 수 없습니다. 두 가지 방법이 있지만 두 가지 이유가있을 수 있습니다.

첫째, 부분 변환을 구현하거나 변환이 실패 할 경우 결과를 기본값으로 유지하는 간결한 방법입니다. 즉, 당신은 이것을 가질 수 있습니다 :

public void ConvertFoo(Foo from, Foo to) {
    if (can't convert) {
        return;
    }
    ...
}

Foo a;
Foo b = DefaultFoo();
ConvertFoo(a, b);
// If conversion fails, b is unchanged

물론 일반적으로 예외를 사용하여 처리됩니다. 그러나 어떤 이유로 든 예외를 피해야하는 경우에도 TryParse 패턴 이 하나의 옵션 인 더 좋은 방법이 있습니다.

또 다른 이유는 순전히 일관성있는 이유 일 수 있기 때문입니다. 예를 들어이 메소드는 어떤 이유로 든 모든 변환 함수 (예 : 다중 출력을 갖는 다른 변환 함수)에 사용되는 공용 API의 일부입니다.

Java는 여러 출력을 처리하는 데별로 좋지 않습니다. 일부 언어와 같은 출력 전용 매개 변수를 사용하거나 다른 언어와 같은 여러 반환 값을 가질 수는 없지만 여전히 반환 객체를 사용할 수 있습니다.

일관성 이유는 다소 절충 적이지만 슬프게도 가장 일반적 일 수 있습니다.

  • 직장 또는 코드베이스의 스타일 경찰은 Java 이외의 배경에서 왔으며 변경하기를 꺼려했습니다.
  • 귀하의 코드는이 스타일이 더 관용적 인 언어의 포트 일 수 있습니다.
  • 조직은 여러 언어에 걸쳐 API 일관성을 유지해야 할 수 있으며 이는 가장 낮은 공통 분모 스타일입니다 (불분명하지만 Google에서도 발생합니다 ).
  • 또는 스타일이 먼 과거에 더 의미가 있었고 현재 형식으로 변형되었습니다 (예 : TryParse 패턴 일 수는 있지만 의도가 좋은 전임자는 아무도 그것을 확인하지 않은 것을 발견 한 후 반환 값을 제거했습니다).

2

두 번째 패턴의 장점은 호출자가 생성 된 객체에 대한 소유권과 책임을지게한다는 것입니다. 메소드가 오브젝트를 작성했는지 또는 재사용 가능한 풀에서 가져 왔는지 여부는 의문의 여지가 없습니다. 호출자는 새 객체의 수명과 폐기에 대한 책임이 있음을 알고 있습니다.

이 방법의 단점은 다음과 같습니다.

  1. 팩토리 메소드로 사용할 수 없으므로 호출자는 OtherObject필요한 서브 타입을 정확히 알고 미리 구성해야합니다.
  2. 해당 매개 변수가 생성 된 경우 생성자에 매개 변수가 필요한 개체와 함께 사용할 수 없습니다 MyObject. OtherObject에 대해 몰라도 구성 할 수 있어야합니다 MyObject.

대답은 C #에서의 경험을 기반으로하며, 논리가 Java로 변환되기를 바랍니다.


나는 당신이 언급 한 책임으로, 그것은 "보기 힘들다"라는 생명주기를 만든다고 생각합니다. 복잡한 방법 시스템에서는 전달하는 모든 방법을 살펴보기보다는 객체를 직접 수정하는 것이 좋습니다.
CsBalazs 헝가리

이것이 내가 생각했던 것입니다. 의존성 주입
Lyndon White

다른 장점은 OtherObject가 추상이거나 인터페이스 인 경우입니다. 이 함수는 어떤 유형을 작성할지 모르지만 호출자는 할 수 있습니다.
user949300

2

의미가 느슨 하면 첫 번째 개체에서 두 번째 개체 myObjectToOtherObject일부 데이터 를 이동 하거나 완전히 새로 변환한다는 의미 일 수 있습니다. 두 번째 방법이 더 적합 해 보입니다.

그러나 메소드 이름이 Convert( "... 변환을 수행하십시오"부분을 보면) 두 번째 메소드가 의미가 없다고 말할 수 있습니다. 값을 이미 존재하는 다른 값인 IMO로 변환하지 않습니다.

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