리팩토링 목적으로 속성 만있는 클래스를 갖는 것이 괜찮습니까?


79

30 개의 매개 변수를 사용하는 메서드가 있습니다. 매개 변수를 가져 와서 하나의 클래스에 넣어서 하나의 매개 변수 (클래스)를 메소드에 전달할 수있었습니다. 리팩토링의 경우 모든 매개 변수가 포함되어 있더라도 모든 매개 변수를 캡슐화하는 개체를 전달하는 것이 완벽하게 괜찮습니까?


아래 답변이 좋습니다. 저는 항상이 목적을 위해 수업을 마련했습니다. 자동 속성을 사용하고 있습니까? msdn.microsoft.com/en-us/library/bb384054.aspx
Harv 2011

그렇지 않으면 POD (plain-old-data) 유형이라고도합니다.
new123456 2011

17
주어진 답변 중 상당수가 훌륭하고 이러한 매개 변수를 좀 더 쉽게 처리 할 수있는 것으로 리팩토링하는 것이 참으로 좋은 생각이지만,이 방법을 사용하려면 30 개의 개별 데이터가 필요하다는 사실은 신호라고 말할 수 있습니다. 너무 많이하고 있습니다. 그러나 다시 한번, 우리는 문제의 방법을 보지 못했거나 그 목적을 알지 못했습니다.
사용자

1
그래서 모든 매개 변수를 취한 메서드를 새 클래스로 옮겼습니까? 아니면 이전 메소드가 새 클래스를 단일 매개 변수로 사용합니까? 의미가 있다면 새 클래스에 메서드를 넣는 방법을 살펴 보겠습니다. 데이터 만 있고 메서드가없는 클래스를 빈혈 클래스라고하며 OO가 조금 덜 느껴집니다. 하지만 단단하고 빠른 규칙은 아닙니다.
Kurt

@Michael : 동의합니다. 또 다른 요점-내부가 메서드 외부에 일종의 관계가 있지 않는 한 메서드 전용 개체를 만들지 말아야한다고 생각합니다.
Saturn Technologies

답변:


75

좋은 생각입니다. 예를 들어 일반적으로 WCF에서 데이터 계약이 수행되는 방식입니다.

이 모델의 한 가지 장점은 새 매개 변수를 추가하면 클래스의 소비자가 매개 변수를 추가하기 위해 변경할 필요가 없다는 것입니다.

David Heffernan이 언급했듯이 코드를 자체 문서화하는 데 도움이 될 수 있습니다.

FrobRequest frobRequest = new FrobRequest
{
    FrobTarget = "Joe",
    Url = new Uri("http://example.com"),
    Count = 42,
};
FrobResult frobResult = Frob(frobRequest);

큰. 나는 속성이 있고 메서드가없는 클래스를 갖는 것만으로는 쓸모가 없다는 인상을 받았다.
Xaisoft

2
데이터 멤버 만 포함 할 경우 구조화하는 것을 선호합니다. struct를 사용하면 데이터 일 뿐이라는 인상을줍니다.
Yousf

28
구조체를 사용하면 의미론 전달시 복사와 같은 부작용이 있습니다. 그런 결정을 내리기 전에 알고 있는지 확인하십시오
Adrian Zanescu

이것이 자바 스크립트 모듈이 일반적으로 작성되는 방식이며, 모듈이 성숙함에 따라 다소 매개 변수를 받아들이는 가장 좋은 방법 인 것 같습니다. someFunction ({myParam1 : 'something', myParam2 : 'somethingElse'});
Kristian

63

여기에있는 다른 답변은 클래스의 인스턴스를 전달하는 것이 30 개의 매개 변수를 전달하는 것보다 낫다는 것을 올바르게 지적하고 있지만, 많은 수의 매개 변수가 근본적인 문제의 증상 일 수 있음을 유의하십시오.

예를 들어, 정적 메서드는 매개 변수 수가 증가하는 경우가 많습니다. 그 이유는 모두 인스턴스 메서드 여야했기 때문이며 해당 클래스의 인스턴스에서 더 쉽게 유지 관리 할 수있는 많은 정보를 전달하고 있기 때문입니다.

또는 매개 변수를 더 높은 추상화 수준의 개체로 그룹화하는 방법을 찾습니다. 관련되지 않은 여러 매개 변수를 단일 클래스로 덤프하는 것은 최후의 수단 IMO입니다.

매개 변수가 너무 많습니까?를 참조하십시오 . 이것에 대한 더 많은 아이디어를 위해.


이러한 매개 변수는 어떤 의미에서 관련이 없습니까? 모두이 방법으로 사용됩니다. 그것은 매우 강력한 관계입니다. 또한 일반적으로 스택의 매개 변수가 명시하는 것이 좋습니다. 멀티 스레딩에 대해 생각해보십시오.
David Heffernan 2011

12
OP의 코드를 보지 않고는 답할 수 없지만 두 값이 하나의 메서드에서 함께 사용되기 때문에 강력한 관계가 있다는 데 동의하지 않습니다. 이것이 사실이라면 OO 디자인은 결국 애플리케이션에서 사용되는 모든 가능한 속성을 보유하는 하나의 큰 클래스를 만드는 것을 의미합니다.
D' Arcy Rittich 2011

아니 당신이 옳고 반드시 관련이 없습니다. 그러나 반드시 무관 한 것은 아닙니다. 그래서, 당신이 말했듯이 확실히 코드를 볼 필요가 있습니다.
David Heffernan 2011

23
매개 변수 30 개? 나는 아마도 관련이 없을 것이고 아마도 너무 길고 순환 복잡성이 높으며 버그의 피난처 인 방법을 나타낼 수도 있습니다. 동작이 변할 수있는 최소 30 차원을 갖는 방법에 대해 이야기하고 있다고 생각해보십시오. 이 방법에 대한 단위 테스트가 있다면 TBH를 작성하는 사람이되고 싶지 않을 것입니다.
Steve Rowbotham

3
또 다른 가능한 시나리오는 모든 매개 변수가 관련되어 있지만 제대로 구성되지 않은 경우입니다. 매개 변수 그룹은 단편적으로 전달되지 않고 별개의 객체로 묶어야 할 가능성이 높습니다. "사람", "주소", "처방"과 같은 정보에 대해 작동하는 방법이 있다고 가정 해 보겠습니다.이 방법은 각각의 개별 정보가 내 방법에 개별적으로 전달되면 매개 변수가 30 개가 될 수 있습니다.
Nate CK

25

좋은 시작입니다. 하지만 이제 새로운 클래스가 생겼으므로 코드를 뒤집어 보는 것을 고려하십시오. 해당 클래스를 매개 변수로 사용하는 메서드를 새 클래스로 이동합니다 (물론 원래 클래스의 인스턴스를 매개 변수로 전달). 이제 클래스에서 혼자 큰 방법을 얻었으며 더 작고 관리하기 쉽고 테스트 가능한 방법으로 분리하는 것이 더 쉬울 것입니다. 이러한 메서드 중 일부는 원래 클래스로 돌아갈 수 있지만 공정한 청크는 아마도 새 클래스에 남아있을 것입니다. 매개 변수 개체 소개를 넘어 메서드를 메서드 개체바꾸기 로 이동했습니다 .

30 개의 매개 변수가있는 메소드가 있다는 것은 메소드가 너무 길고 복잡하다는 매우 강력한 신호입니다. 디버깅하기가 너무 어렵고 테스트하기가 너무 어렵습니다. 그래서 당신은 그것에 대해 뭔가를해야하고 Introduce Parameter Object는 시작하기에 좋은 곳입니다.


4
이것은 절대적으로 중요합니다! 이러한 속성 번들을 만드는 것은 자체 메서드를 사용하여 새 클래스를 만드는 첫 번째 단계이기 때문에 유용하며 거의 항상 새 클래스에 속하는 메서드를 찾을 수 있습니다. 나는 이것이이 그룹에서 가장 중요한 대답이라고 생각합니다.
Bill K

15

매개 변수 객체로 리팩토링하는 것이 그 자체로 나쁜 생각은 아니지만 다른 곳에서 제공되는 30 개의 데이터가 필요한 클래스가 여전히 코드 냄새가 될 수 있다는 문제를 숨기는 데 사용해서는 안됩니다. Introduce Parameter Object 리팩토링은 해당 절차의 끝이 아니라 광범위한 리팩토링 프로세스의 한 단계로 간주되어야합니다.

실제로 해결되지 않는 문제 중 하나는 Feature Envy입니다. Parameter Object가 전달되는 클래스가 다른 클래스의 데이터에 너무 관심이 있다는 사실이 해당 데이터에 대해 작동하는 메서드가 데이터가있는 곳으로 이동해야한다는 것을 의미하지 않습니까? 함께 속하는 메서드와 데이터의 클러스터를 식별하고 클래스로 그룹화하여 캡슐화를 늘리고 코드를 더 유연하게 만드는 것이 정말 좋습니다.

동작과 데이터를 분리하는 작업을 여러 번 반복 한 후 별도의 단위로 작동하는 클래스가 더 이상 존재하지 않는다는 사실을 발견해야합니다. 이는 코드를 더 유연하게 만들 수 있기 때문에 항상 더 나은 최종 결과입니다.


10

그것은 훌륭한 아이디어이며 문제에 대한 매우 일반적인 해결책입니다. 매개 변수가 2 개 또는 3 개 이상인 메서드는 기하 급수적으로 더 어려워지고 이해하기가 더 어려워집니다.

이 모든 것을 단일 클래스로 캡슐화하면 코드가 훨씬 더 명확 해집니다. 속성에 이름이 있기 때문에 다음과 같이 자체 문서화 코드를 작성할 수 있습니다.

params.height = 42;
params.width = 666;
obj.doSomething(params);

당연히 많은 매개 변수가있을 때 위치 식별을 기반으로 한 대안은 단순히 끔찍합니다.

또 다른 이점은 모든 호출 사이트에서 변경을 강제하지 않고도 인터페이스 계약에 추가 매개 변수를 추가 할 수 있다는 것입니다. 그러나 이것은 보이는 것처럼 항상 사소한 것은 아닙니다. 다른 호출 사이트에 새 매개 변수에 대해 다른 값이 필요한 경우 매개 변수 기반 접근 방식보다이를 추적하기가 더 어렵습니다. 매개 변수 기반 접근 방식에서 새 매개 변수를 추가하면 각 호출 사이트에서 새 매개 변수를 제공하도록 강제로 변경되며 컴파일러가 모든 매개 변수를 찾는 작업을 수행하도록 할 수 있습니다.



5

30 개의 매개 변수는 엉망입니다. 속성을 가진 클래스를 갖는 것이 훨씬 더 예쁘다고 생각합니다. 동일한 범주에 맞는 매개 변수 그룹에 대해 여러 "매개 변수 클래스"를 만들 수도 있습니다.


3

클래스 대신 구조를 사용할 수도 있습니다.

그러나 당신이하려는 것은 매우 일반적이고 훌륭한 아이디어입니다!


실제로 구조체를 사용하는 것에 대해 생각했지만 구조체를 사용하면 단점이 있는지 궁금합니다.
Xaisoft

왜 David? 필드의 수는 무엇과 관련이 있습니까? 그냥 궁금해.
Tad Donaghe

2
성능 문제로. 의미 론적으로는 각 필드의 값만 중요하고 참조 ID는 관련이 없기 때문에 여기서 구조체를 사용하는 것이 더 합리적이지만 30 개 정도의 필드는 복사 할 것이 더 많기 때문입니다 (대부분 참조 유형이라고 말하면 32 비트에서는 120 바이트, 32 비트에서는 240 바이트). 64) 클래스 (4 또는 8 바이트)보다. 그러나 구조체의 값별 복사 특성은 액세스를 통해 복사되면 참조 유형보다 더 빠르므로 구조체가 덜 효율적인 임계 값은 위의 1 포인터 크기보다 높습니다. > 4에서 포인터 크기는 고려할 시간입니다.
Jon Hanna

대박. 설명 해주셔서 감사합니다! :)
Tad Donaghe

3

리팩토링 여부에 관계없이 Plain Old Data 클래스 를 사용하는 것이 합리적 일 수 있습니다 . 왜 그렇게 생각하지 않았는지 궁금합니다.


정확히 기억이 나지는 않지만 얼마 전 여기 어딘가에서 속성 만있는 클래스를 만들고 있다고 언급했을 때 당시에는 무시했던 것 같습니다. 두 번째로, 변경되지 않는 매개 변수 만 메소드에 전달되어야한다는 것입니다. 즉, 메소드가 매개 변수를 변경하더라도 매개 변수로 전달해서는 안된다는 것입니다.
Xaisoft

속성 만있는 클래스는 대신 "전체"클래스 여야하는 것을 나타내는 나쁜 냄새 일 수 있습니다 (Steve Rowbotham의 답변 참조). 좋은 징조는 비슷한 클래스를 두 번 이상 사용하는 경우입니다. 그러나 이것이 항상 그런 것은 아니며 30 필드 클래스와 30 파라미터 메서드 사이에 던져 질 때 전자에 의지해야하는 좋은 호출이 있습니다. 게다가, 그것은 "전체"클래스를 구축하는 시작이 될 수 있습니다.
Jon Hanna

... 불변성에 대한 내 의견을 제거하려고합니다.이 클래스가 공용 메서드 ( "요청"개체가 호출자의 의도를 설명하는 경우) 일 뿐이라고 생각했기 때문입니다. 여기서 "요청"을 변경하면 혼동이 발생할 수 있습니다. 일회성의 경우 변경 가능하고 진행하면서 구축하는 것이 더 편리합니다. 불변성은 30 인수 호출을 30 인수 생성자로 교체 한 다음 호출을 의미하므로 이길 수 없습니다.
Jon Hanna

3

아마도 C # 4.0의 선택적 매개 변수와 명명 된 매개 변수가 이것에 대한 좋은 대안일까요?

어쨌든, 당신이 설명하는 방법은 프로그램 동작을 추상화하는데도 좋습니다. 예를 들어 SaveImage(ImageSaveParameters saveParams)인터페이스에 하나의 표준 기능이있을 수 있으며 , 인터페이스 ImageSaveParameters이기도하며 이미지 형식에 따라 추가 매개 변수를 가질 수 있습니다. 예를 들어 JpegSaveParametersQuality동안 -property을 PngSaveParametersA가 들어BitDepth -property을.

이것은 Paint.NET의 저장 저장 대화 상자가 수행하는 방법이므로 매우 실제적인 예입니다.


3

앞서 언급했듯이, 올바른 단계이지만 다음 사항도 고려하십시오.

  • 메서드가 너무 복잡 할 수 있습니다 (더 많은 메서드로 나누거나 별도의 클래스로 전환하는 것을 고려해야합니다).
  • 매개 변수에 대한 클래스를 생성하는 경우 변경 불가능 하게 만드십시오.
  • 많은 매개 변수가 널이거나 일부 기본값을 가질 수있는 경우 클래스에 빌더 패턴 을 사용할 수 있습니다 .

0

여기에 많은 훌륭한 답변이 있습니다. 2 센트를 더하고 싶습니다.

매개 변수 개체는 좋은 시작입니다. 하지만 할 수있는 일이 더 많습니다. 다음을 고려하십시오 (루비 예제).

/ 1 / 단순히 모든 매개 변수를 그룹화하는 대신 의미있는 매개 변수 그룹화가 있는지 확인하십시오. 둘 이상의 매개 변수 오브젝트가 필요할 수 있습니다.

def display_line(startPoint, endPoint, option1, option2)

될 수 있습니다

def display_line(line, display_options)

/ 2 / 매개 변수 개체는 원래 매개 변수 수보다 속성 수가 적을 수 있습니다.

def double_click?(cursor_location1, control1, cursor_location2, control2)

될 수 있습니다

def double_click?(first_click_info, second_click_info) 
                       # MouseClickInfo being the parameter object type 
                       # having cursor_location and control_at_click as properties

이러한 사용은 이러한 매개 변수 개체에 의미있는 동작을 추가 할 가능성을 발견하는 데 도움이됩니다. 당신은 그들이 당신의 편안함을 위해 초기 데이터 클래스 냄새를 더 빨리 털어내는 것을 발견 할 것 입니다. :-)

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