콜 체인에서 여러 수준으로 만 사용되는 전달 매개 변수의 (안티) 패턴 이름이 있습니까?


209

일부 레거시 코드에서 전역 변수 사용에 대한 대안을 찾으려고했습니다. 그러나이 질문은 기술적 대안에 관한 것이 아니며 주로 용어 에 대해 우려하고 있습니다.

확실한 해결책은 전역을 사용하는 대신 매개 변수를 함수에 전달하는 것입니다. 이 레거시 코드베이스에서는 값이 사용될 지점과 매개 변수를 먼저받는 함수 사이에서 긴 콜 체인의 모든 함수를 변경해야합니다.

higherlevel(newParam)->level1(newParam)->level2(newParam)->level3(newParam)

경우는 newParam이전에 내 예제에서 전역 변수했지만, 대신 이전에 하드 코딩 값 수 있었다. 요점은 이제 newParam의 가치는에서 얻어 higherlevel()지고 "여행"해야한다는 것 level3()입니다.

이런 종류의 상황 / 패턴에 대한 이름 이 있는지 궁금해서 수정하지 않은 값을 "전달"하는 많은 함수에 매개 변수를 추가해야합니다.

적절한 용어를 사용하면 재 설계를위한 솔루션에 대한 더 많은 리소스를 찾고이 상황을 동료들에게 설명 할 수 있기를 바랍니다.


94
이것은 전역 변수를 사용 하는 것 보다 개선 된 것 입니다. 그것은 각 기능이 어떤 상태에 의존 하는지를 명확하게합니다 (그리고 순수한 기능으로가는 길입니다). 매개 변수를 통해 "스레딩"이라는 매개 변수를 들었지만이 용어가 얼마나 일반적인지 잘 모르겠습니다.
gardenhead

8
이것은 특정한 대답을하기에는 너무 넓은 스펙트럼입니다. 이 레벨에서는 이것을 "코딩"이라고 부릅니다.
Machado

38
"문제"는 단순함이라고 생각합니다. 이것은 기본적으로 의존성 주입입니다. 좀 더 깊게 중첩 된 멤버가 있으면 함수의 매개 변수 목록을 팽창시키지 않고 체인을 통해 종속성을 자동으로 주입하는 메커니즘이있을 수 있습니다. 어쩌면보고 세련의 서로 다른 수준의 의존성 주입 전략은 존재하는 경우, 당신이 찾고있는 용어로 이어질 수 있습니다.
null

7
좋은 패턴 / 반 패턴 / 개념 / 솔루션인지에 대한 토론에 감사하지만, 실제로 알고 싶은 것은 그 이름이 있는지 알고 싶습니다.
ecerulm

3
또한 호출 스택 전체에서 연통 선 을 낮추는 것처럼 가장 일반적으로 스레딩 이라고 하지만 배관 작업 이라고 들었습니다 .
wchargin

답변:


202

데이터 자체를 "트램프 데이터"라고 합니다. "코드 냄새"는 하나의 코드가 중개자를 통해 먼 거리에서 다른 코드와 통신하고 있음을 나타냅니다.

  • 특히 콜 체인에서 코드의 강성을 증가시킵니다. 콜 체인에서 메소드를 리팩토링하는 방법에는 훨씬 더 제한적입니다.
  • 데이터 / 방법 / 아키텍처에 대한 지식을 가장 신경 쓰지 않는 장소에 배포합니다. 방금 지나가는 데이터를 선언해야하고 선언에 새 가져 오기가 필요한 경우 네임 스페이스를 오염시킨 것입니다.

전역 변수를 제거하기위한 리팩토링은 어렵고, 부정기 데이터는이를 수행하는 한 가지 방법이며 종종 가장 저렴한 방법입니다. 비용이 듭니다.


73
"트램프 데이터"를 검색하여 Safari 구독에서 "Code Complete"책을 찾을 수있었습니다. 이 책에는 "글로벌 데이터를 사용하는 이유"라는 섹션이 있으며 "글로벌 데이터를 사용하면 부정기 데이터를 제거 할 수 있습니다"라는 이유 중 하나가 있습니다. :). "트램프 데이터"를 통해 전 세계를 다루는 것에 대한 더 많은 문헌을 찾을 수있을 것 같습니다. 감사!
ecerulm

9
@JimmyJames, 그 기능은 물론 일을합니다. 이전에는 전역에 해당되는 특정 새 매개 변수를 사용하지 않았습니다.
ecerulm

174
20 년간의 프로그래밍에서, 나는이 용어를 문자 적으로 들어 본 적이없고 그것이 무엇을 의미하는지 즉시 명백하지도 않았습니다. 나는 그 대답에 대해 불평하지 않고 그 용어가 널리 사용되거나 알려지지 않았 음을 암시합니다. 어쩌면 나일지도 몰라
Derek Elkins

6
일부 글로벌 데이터는 괜찮습니다. "글로벌 데이터"라고 부르는 대신 "환경"이라고 부를 수 있습니다. 그것이 바로 그것이 기 때문입니다. 환경에는 예를 들어 appdata (Windows)의 문자열 경로 또는 현재 프로젝트에서 모든 구성 요소에서 사용하는 전체 GDI + 브러쉬, 펜, 글꼴 등이 포함 될 수 있습니다.
Robinson

7
@Robinson 그렇지 않습니다. 예를 들어, 이미지 작성 코드가 % AppData %에 닿도록 하시겠습니까, 아니면 작성 위치에 대한 인수가 필요합니까? 그것이 세계적인 상태와 논쟁의 차이입니다. "환경"은 환경과의 상호 작용을 담당하는 사람들에게만 존재하는 것처럼 쉽게 주입되는 종속성 일 수 있습니다. GDI + 브러쉬 등이 더 합리적이지만 실제로는 API를 제공하지 못하는 환경에서 리소스 관리의 경우가 더 많습니다. 기본 API 및 / 또는 언어 / 라이브러리 / 런타임이 거의 없습니다.
Luaan

102

나는 이것이 그 자체로 반 패턴이라고 생각하지 않습니다. 문제는 실제로 각 기능을 독립적 인 블랙 박스로 생각해야 할 때 함수를 체인으로 생각하고 있다는 것입니다 ( 참고 : 재귀 적 방법은이 조언에서 예외입니다).

예를 들어, 두 달력 날짜 사이의 일 수를 계산하여 함수를 만들어야한다고 가정 해 보겠습니다.

int daysBetween(Day a, Day b)

이를 위해 새 함수를 만듭니다.

int daysSinceEpoch(Day day)

그런 다음 첫 번째 기능은 간단하게됩니다.

int daysBetween(Day a, Day b)
{
    return daysSinceEpoch(b) - daysSinceEpoch(a);
}

이것에 대한 반 패턴은 없습니다. daysBetween 메소드의 매개 변수는 다른 메소드로 전달되고 메소드에서 달리 참조되지 않지만 해당 메소드가 수행해야하는 작업을 수행하는 데 여전히 필요합니다.

내가 추천하는 것은 각 기능을 살펴보고 몇 가지 질문으로 시작하는 것입니다.

  • 이 기능이 명확하고 집중된 목표를 가지고 있습니까? 아니면 "일을하는"방법입니까? 일반적으로 함수의 이름이 여기에 도움이되며 이름으로 설명되지 않은 내용이 있으면 빨간색 플래그입니다.
  • 너무 많은 매개 변수가 있습니까? 때로는 메소드가 많은 입력을 합법적으로 필요로 할 수 있지만 매개 변수가 너무 많으면 사용하거나 이해하기에 부담이됩니다.

메소드에 단일 목적으로 묶이지 않은 복잡한 코드를 살펴 보려면 먼저이를 풀어야합니다. 이것은 지루할 수 있습니다. 가장 쉬운 것부터 시작하여 별도의 방법으로 옮기고 일관된 것을 가질 때까지 반복하십시오.

매개 변수가 너무 많은 경우 Method to Object 리팩토링을 고려하십시오 .


2
글쎄, 나는 (anti-)와 논란의 여지가 없다. 그러나 여전히 많은 기능 서명을 업데이트해야하는 "상황"에 대한 이름이 있는지 궁금합니다. 반 패턴보다 "코드 냄새"에 가깝습니다. 전역을 제거하기 위해 6 개의 함수 서명을 업데이트 해야하는 경우이 레거시 코드에서 수정해야 할 것이 있음을 알려줍니다. 그러나 매개 변수 전달은 일반적으로 괜찮다고 생각하며 근본적인 문제를 해결하는 방법에 대한 진심으로 감사드립니다.
ecerulm

4
@ecerulm 나는 하나를 알지 못하지만 내 경험에 따르면 전역을 매개 변수로 변환하는 것이 매개 변수를 제거하는 가장 좋은 방법이라고 말합니다. 이를 통해 공유 상태가 제거되므로 추가 리팩터링이 가능합니다. 이 코드에 더 많은 문제가 있다고 생각하지만 설명에 문제가 무엇인지 알 수 없습니다.
JimmyJames

2
나는 보통 그 접근법도 따르며,이 경우에도 그렇게 할 것입니다. 나는 단지 이것에 대한 나의 어휘 / 용어를 향상시키고 싶었 기 때문에 이것에 대해 더 연구하고 더 나은, 집중된 질문을 할 수있게되었다.
ecerulm

3
@ecerulm 나는 이것에 대한 하나의 이름이 없다고 생각합니다. 그것은 많은 질병뿐만 아니라 '건조한 입'과 같은 비 질병 상태에 공통적 인 증상과 같습니다. 코드 구조에 대한 설명을 구체화하면 특정 내용을 가리킬 수 있습니다.
JimmyJames

@ecerulm 수정해야 할 것이 있다는 것을 알려줍니다. 이제 전역 변수 일 때보 다 수정해야한다는 것이 훨씬 더 분명합니다.
immibis

61

BobDalgleish는 이 (반) 패턴을 " 트램프 데이터 " 라고 언급 했습니다 .

필자의 경험에 따르면 과도한 트램프 데이터의 가장 일반적인 원인은 객체 또는 데이터 구조에 실제로 캡슐화되어야하는 연결된 상태 변수가 많이 있기 때문입니다. 때로는 데이터를 올바르게 구성하기 위해 많은 개체를 중첩해야 할 수도 있습니다.

간단한 예를 들어, 같은 특성을 가진 사용자 정의 플레이어 캐릭터가있는 게임을 고려 playerName, playerEyeColor등등합니다. 물론 플레이어는 게임 맵에서 물리적 위치와 현재 및 최대 건강 수준 등과 같은 다양한 다른 속성을 가지고 있습니다.

이러한 게임의 첫 번째 반복에서는 이러한 모든 속성을 전역 변수로 만드는 것이 합리적 일 수 있습니다. 결국 플레이어는 하나 뿐이며 게임의 거의 모든 것이 플레이어와 관련이 있습니다. 따라서 전역 상태에는 다음과 같은 변수가 포함될 수 있습니다.

playerName = "Bob"
playerEyeColor = GREEN
playerXPosition = -8
playerYPosition = 136
playerHealth = 100
playerMaxHealth = 100

그러나 언젠가는 게임에 멀티 플레이어 모드를 추가하고 싶기 때문에이 디자인을 변경해야 할 수도 있습니다. 첫 번째 시도로 모든 변수를 로컬로 만들어 필요한 함수에 전달할 수 있습니다. 그러나 게임의 특정 액션에 다음과 같은 함수 호출 체인이 포함될 수 있습니다.

mainGameLoop()
 -> processInputEvent()
     -> doPlayerAction()
         -> movePlayer()
             -> checkCollision()
                 -> interactWithNPC()
                     -> interactWithShopkeeper()

...이 interactWithShopkeeper()기능은 상점 주인에게 플레이어 이름을 지정하므로 이제 모든 기능을 playerName통해 갑자기 부정기 데이터 를 전달해야 합니다. 물론 상점 주인이 파란 눈을 가진 플레이어가 순진하고 더 높은 가격을 책정한다고 생각 하면 전체 기능 체인 등 을 통과해야합니다 .playerEyeColor

적절한 솔루션이 경우, 이름을 캡슐화하는 플레이어 객체를 정의하는 물론, 눈 색깔, 위치, 건강과 플레이어 캐릭터의 다른 특성이다. 그렇게하면 플레이어와 관련된 모든 기능에 단일 객체를 전달하기 만하면됩니다.

또한, 위의 기능 중 일부는 자연스럽게 해당 플레이어 객체의 메소드로 만들어 질 수 있으며,이를 통해 플레이어의 속성에 자동으로 액세스 할 수 있습니다. 어쨌든 이것은 객체의 메소드를 호출하면 객체 인스턴스를 숨겨진 매개 변수로 메소드에 효과적으로 전달하기 때문에 구문상의 설탕 일뿐입니다. 그러나 올바르게 사용하면 코드가 더 명확하고 자연스럽게 보입니다.

물론, 전형적인 게임은 단순한 플레이어보다 훨씬 "전세계적인"상태를 가질 것입니다. 예를 들어, 게임이 진행되는지도와 플레이어가 아닌 캐릭터 목록,지도 위에 놓인 아이템 등이있을 것입니다. 주위의 모든 것을 tramp 객체로 전달할 수도 있지만 메소드 인수를 다시 혼란스럽게 만듭니다.

대신, 해결책은 오브젝트가 영구적 또는 임시 관계가있는 다른 오브젝트에 대한 참조를 저장하도록하는 것입니다. 따라서, 예를 들어, 플레이어 객체는 같은 방법 있도록 현재 수준 /지도에 대한 참조를 가질 것 "게임 세계"개체에 대한 참조를 저장해야합니다 아마 (아마 어떤 NPC가 너무 객체) player.moveTo(x, y)을 필요로하지 않는다 맵을 매개 변수로 명시 적으로 제공해야합니다.

마찬가지로 플레이어 캐릭터가 애완견을 따라 다니는 경우 개를 설명하는 모든 상태 변수를 자연스럽게 단일 개체로 그룹화하고 플레이어 개체에 개에 대한 참조를 제공합니다 (플레이어가 예를 들어, 개를 이름으로 부르고 그 반대의 경우도 있습니다 (개가 플레이어의 위치를 ​​알 수 있도록). 물론 플레이어와 개 개체를보다 일반적인 "actor"개체의 하위 클래스로 만들면지도에서 두 코드를 이동하는 데 동일한 코드를 재사용 할 수 있습니다.

추신. 게임을 예로 사용했지만 이러한 문제가 발생하는 다른 종류의 프로그램이 있습니다. 그러나 내 경험상 근본적인 문제는 항상 같은 경향이 있습니다. 하나 이상의 상호 연결된 객체로 함께 묶고 싶은 별도의 변수 (로컬 또는 글로벌 여부)가 있습니다. 함수에 침입하는 "트램프 데이터"가 수치 시뮬레이션에서 "글로벌"옵션 설정 또는 캐시 된 데이터베이스 쿼리 또는 상태 벡터로 구성되어 있는지 여부에 관계없이 솔루션은 데이터가 속하는 자연 컨텍스트 를 식별하고 이를 객체로 만드는 것입니다. (또는 선택한 언어에서 가장 가까운 것).


1
이 답변은 존재할 수있는 문제에 대한 해결책을 제공합니다. 다른 솔루션을 나타내는 글로벌이 사용 된 상황이있을 수 있습니다. 플레이어 클래스의 메소드를 메소드로 만드는 것은 객체를 메소드에 전달하는 것과 같습니다. 이것은 이런 방식으로 쉽게 복제되지 않는 다형성을 무시합니다. 예를 들어, 움직임과 다른 유형의 속성에 대해 다른 규칙을 가진 다른 유형의 플레이어를 만들려면 이러한 객체를 하나의 메서드 구현으로 전달하면 많은 조건부 논리가 필요합니다.
JimmyJames

6
@JimmyJames : 다형성에 대한 당신의 요점은 좋은 것이고, 나는 그것을 스스로 만드는 것에 대해 생각했지만 대답이 더 길어지지 않도록 남겨 두었습니다. 내가하려고했던 (아마도 열악한) 요점은 순수한 데이터 흐름 측면에서 foo.method(bar, baz)와 사이에는 거의 차이가 없지만 method(foo, bar, baz)이전을 선호하는 다른 이유 (다형성, 캡슐화, 지역 등)가 있다는 것입니다.
Ilmari Karonen

@IlmariKaronen : 또한 객체의 미래 변경 / 추가 / 삭제 / 리팩토링 (예 : playerAge)으로부터 함수 프로토 타입을 미래에 보장한다는 매우 명백한 이점입니다. 이것만으로도 귀중합니다.
smci

34

나는 이것에 대한 특정 이름을 알지 못하지만 설명하는 문제는 그러한 매개 변수의 범위에 대한 최상의 타협점을 찾는 문제 일 뿐이라고 언급 할 가치가 있다고 생각합니다.

  • 전역 변수로서 프로그램이 특정 크기에 도달하면 범위가 너무 큽니다.

  • 순전히 로컬 매개 변수로서, 호출 체인에 많은 반복 매개 변수 목록이 생길 때 범위가 너무 작을 수 있습니다.

  • 트레이드 오프로서, 종종 하나 이상의 클래스에서 이러한 매개 변수를 멤버 변수로 만들 수 있습니다. 이것이 바로 적절한 클래스 디자인 이라고하는 것입니다 .


10
적절한 수업 디자인을 위해 +1 이것은 OO 솔루션을 기다리는 고전적인 문제처럼 들립니다.
l0b0

21

나는 당신이 묘사하는 패턴이 정확히 의존성 주입 이라고 생각합니다 . 여러 댓글이는 것을 주장했다 패턴 이 아니라 안티 패턴 , 내가 동의하는 경향이있다.

또한 @JimmyJames의 답변과 동의합니다. 여기서 각 함수를 모든 입력을 명시 적 매개 변수 로 사용하는 블랙 박스로 취급하는 것이 좋은 프로그래밍 관행이라고 주장합니다 . 즉, 땅콩 버터와 젤리 샌드위치를 ​​만드는 기능을 작성하는 경우 다음과 같이 작성할 수 있습니다.

Sandwich make_sandwich() {
    PeanutButter pb = get_peanut_butter();
    Jelly j = get_jelly();
    return pb + j;
}
extern PhysicalRefrigerator g_refrigerator;
PeanutButter get_peanut_butter() {
    return g_refrigerator.get("peanut butter");
}
Jelly get_jelly() {
    return g_refrigerator.get("jelly");
}

하지만 것 보다 연습 의존성 주입을 적용하고 대신이처럼 쓸 수 :

Sandwich make_sandwich(Refrigerator& r) {
    PeanutButter pb = get_peanut_butter(r);
    Jelly j = get_jelly(r);
    return pb + j;
}
PeanutButter get_peanut_butter(Refrigerator& r) {
    return r.get("peanut butter");
}
Jelly get_jelly(Refrigerator& r) {
    return r.get("jelly");
}

이제 모든 의존성을 명확하게 함수 서명에 문서화하는 함수가 있으므로 가독성에 좋습니다. 결국,이다 진정한 순서에 있음을 make_sandwich당신은에 액세스 할 수 있어야합니다 Refrigerator; 따라서 이전 기능 서명은 냉장고를 입력의 일부로 사용하지 않음으로써 기본적으로 불분명했습니다.

보너스로 클래스 계층 구조를 올바르게 수행하고 슬라이싱을 피하는 등의 작업을 수행 make_sandwich하면 MockRefrigerator! (단위 테스트 환경에 액세스 할 수 없기 때문에이 방법으로 단위 테스트를 수행해야 할 수도 있습니다 PhysicalRefrigerator.)

모든 의존성 주입의 모든 사용이 비슷한 이름의 매개 변수를 호출 스택에서 여러 수준으로 배관 해야 한다는 것을 이해하지 못 하므로 요청한 질문에 정확하게 대답 하지는 않지만 ...이 주제에 대해 더 자세히 읽으려면 "종속성 주입"은 확실히 당신에게 적절한 키워드입니다.


10
이것은 매우 분명한 안티 패턴입니다. 냉장고를 통과 할 필요는 전혀 없습니다. 이제 일반적인 IngredientSource를 전달하는 것이 효과가있을 수 있지만 빵을 빵통, 큰 참치, 냉장고 치즈에서 얻을 수 있다면, 재료의 출처에 의존하여 재료를 만들지 않는 관련 작업에 주입함으로써 재료를 샌드위치에 담아 우려를 분리하고 코드 냄새를 맡았습니다.
Dewi Morgan

8
@DewiMorgan : 분명히 당신이 일반화 더 리팩토링 수 RefrigeratorIngredientSource, 심지어는 "샌드위치"로 개념을 일반화 template<typename... Fillings> StackedElementConstruction<Fillings...> make_sandwich(ElementSource&); 이를 "일반 프로그래밍"이라고하며 합리적으로 강력하지만 OP가 실제로 원하는 것보다 훨씬 더 간결합니다. 샌드위치 프로그램에 대한 적절한 추상화 수준에 대한 새로운 질문을 자유롭게여십시오. ;)
Quuxplusone

11
실수가 없으면 권한이없는 사용자 는에 액세스 할 수 없습니다make_sandwich() .
dotancohen

2
@Dewi-XKCD 링크
Gavin Lock

19
코드에서 가장 심각한 오류는 땅콩 버터를 냉장고에 보관하는 것입니다.
Malvolio

15

이것은 커플 링에 대한 교과서 정의와 유사합니다 . 하나의 모듈은 다른 모듈에 크게 영향을 미치는 종속성이 있으며 변경 될 때 파급 효과를 만듭니다. 다른 의견과 답변은 이것이 전 세계에 비해 개선되었다는 것입니다. 이제 커플 링이 파괴적이기보다 더 명확하고 프로그래머가 쉽게 볼 수 있기 때문입니다. 그렇다고 수정해서는 안된다는 의미는 아닙니다. 커플 링을 제거하거나 줄이려면 리팩토링 할 수 있어야합니다.


3
level3()필요한 경우 newParam확실하게 결합하지만 코드의 다른 부분이 서로 통신해야합니다. 해당 함수가 매개 변수를 사용하는 경우 함수 매개 변수를 잘못된 커플 링이라고 부를 필요는 없습니다. 나는 체인의 문제 측면은 생각 추가 도입 결합 level1()하고 level2()있는 아무 소용이 없다 newParam그것을 통과를 제외합니다. 좋은 답변, 커플 링 +1
null

6
@null 실제로 사용 하지 않으면 발신자로부터받는 대신 값을 구성 할 수 있습니다.
Random832

3

이 답변이 귀하의 질문에 직접 대답하지는 않지만 개선 방법을 언급하지 않고 통과시키는 것을 거부합니다 (반 패턴으로 생각할 수 있으므로). 나는 당신과 다른 독자들이 "트램프 데이터"를 피하는 방법에 대한이 추가 논평으로부터 가치를 얻을 수 있기를 바랍니다 (Bob Dalgleish가 우리에게 도움이 되었기 때문에).

나는이 문제를 피하기 위해 더 많은 것을 할 것을 제안하는 답변에 동의합니다. 그러나 " 많은 인수를 전달하는 데 사용한 클래스를 전달하기 만하면 됩니다."로 넘어 가지 않고 이러한 인수 전달을 깊게 줄이는 또 다른 방법 은 프로세스의 일부 단계가 하위 레벨 대신 상위 레벨에서 발생하도록 리팩토링하는 것입니다. 하나. 예를 들어, 다음은 코드 이전 입니다.

public void PerformReporting(StuffRepository repo, string desiredName) {
   var stuffs = repo.GetStuff(DateTime.Now());
   FilterAndReportStuff(stuffs, desiredName);
}

public void FilterAndReportStuff(IEnumerable<Stuff> stuffs, string desiredName) {
   var filter = CreateStuffFilter(FilterTypes.Name, desiredName);
   ReportStuff(stuffs.Filter(filter));
}

public void ReportStuff(IEnumerable<Stuff> stuffs) {
   stuffs.Report();
}

에서해야 할 일이 많을수록이 상황은 더욱 악화됩니다 ReportStuff. 사용하려는 리포터 인스턴스를 전달해야 할 수도 있습니다. 그리고 전달해야 할 모든 종류의 의존성, 함수에 대한 중첩 함수.

내 제안은 모든 단계를 더 높은 수준으로 끌어 올리는 것입니다.이 단계에 대한 지식은 일련의 메소드 호출에 분산되는 대신 단일 메소드로 생활해야합니다. 물론 실제 코드에서는 더 복잡하지만 아이디어는 다음과 같습니다.

public void PerformReporting(StuffRepository repo, string desiredName) {
   var stuffs = repo.GetStuff(DateTime.Now());
   var filter = CreateStuffFilter(FilterTypes.Name, desiredName);
   var filteredStuffs = stuffs.Filter(filter)
   filteredStuffs.Report();
}

여기서 큰 차이점은 긴 체인을 통해 종속성을 전달할 필요가 없다는 것입니다. 한 수준이 아닌 몇 수준으로 평탄화하더라도 프로세스가 해당 수준에서 일련의 단계로 표시되도록 일부 "평 평화"를 달성하면 개선 된 것입니다.

이것은 여전히 ​​절차 적이며 아직 어떤 대상으로 바뀌지 않았지만, 무언가를 수업으로 바꾸어 달성 할 수있는 캡슐화 유형을 결정하는 좋은 단계입니다. 이전 시나리오 에서 깊게 연결된 메소드 호출 은 실제로 발생 하는 세부 사항을 숨기고 코드를 이해하기 어렵게 만들 수 있습니다. 이 작업을 과도하게 수행하여 고수준 코드를 사용하지 말아야 할 일을 알게하거나 너무 많은 일을 수행하여 단일 책임 원칙을 위반하는 방법을 만들 수 있지만 일반적으로 일을 평평하게하는 것이 약간 도움이된다는 것을 알았습니다 더 명확하게 코드를 개선하기 위해 점진적으로 변경합니다.

이 모든 작업을 수행하는 동안 테스트 가능성을 고려해야합니다. 체인 된 메서드 호출은 실제로 테스트 하려는 슬라이스에 대한 어셈블리에 진입 점과 출구 점이 없기 때문에 단위 테스트를 어렵게 만듭니다 . 이 평탄화를 사용하면 메소드가 더 이상 많은 종속성을 갖지 않으므로 테스트하기가 쉽고 모의자를 많이 요구하지 않습니다!

나는 최근에 17 개의 의존성 (모두 조롱해야 함)과 같은 클래스에 유닛 테스트를 추가하려고 시도했습니다. 나는 아직 모든 것을 얻지 못했지만 클래스를 세 가지 클래스로 나누었습니다. 각 클래스는 관련 된 명사 중 하나를 처리하고 최악의 경우 12 명, 약 8 명으로 종속성 목록을 얻었습니다. 최고.

테스트 가능성은 더 나은 코드를 작성 하도록 합니다. 단위 테스트를 작성해야 코드에 대해 다르게 생각할 수 있으며 단위 테스트를 작성하기 전에 버그가 몇 개인 지에 관계없이 시작부터 더 나은 코드를 작성할 수 있기 때문에 단위 테스트를 작성해야합니다.


2

문자 그대로 데메테르 법칙을 위반하지는 않지만 문제는 몇 가지면에서 비슷합니다. 질문의 요점은 리소스를 찾는 것이므로 Demeter of Law에 대해 읽고 그 조언 중 얼마나 많은 것이 귀하의 상황에 적용되는지 확인하십시오.


1
세부 사항에 약간 약하며 아마도 다운 보트를 설명합니다. 그러나 정신적으로이 해답은 바로 그 자리에 있습니다. OP는 데메테르 법칙을 읽어야합니다. 이것은 관련 용어입니다.
Konrad Rudolph

4
FWIW, 저는 데메테르 법칙 (일명 "최소 특권")이 전혀 관련이 없다고 생각합니다. OP의 경우는 트램 데이터가없는 경우 자신의 기능 이 작업을 수행 할 수없는 곳입니다 (다음 사람은 콜 스택을 필요로하기 때문에 다음 사람이 필요하기 때문에 등등). 최소 권한 / 계량기의 법칙은 매개 변수가 실제로 사용되지 않는 경우에만 관련이 있으며,이 경우 수정이 명백합니다. 사용되지 않은 매개 변수를 제거하십시오!
Quuxplusone

2
이 질문의 상황은 Demeter of Law와는 전혀 관련이 없습니다 ... 메소드 호출 체인과 관련하여 피상적 인 유사성이 있지만 그렇지 않으면 매우 다릅니다.
Eric King

@Quuxplusone 가능하지만이 경우 체인 호출이 해당 시나리오에서 실제로 의미가 없기 때문에 설명이 상당히 혼란스러워 집니다. 대신 중첩 되어야합니다 .
Konrad Rudolph

1
LoD 위반을 처리하기 위해 제안 된 일반적인 리팩토링은 부정기 데이터를 도입하는 것이기 때문에이 문제 LoD 위반과 매우 유사합니다. IMHO, 이것은 커플 링을 줄이는 좋은 출발점이지만 충분하지 않습니다.
Jørgen Fogh

1

효율성, 유지 관리 성 및 구현 용이성 측면에서 항상 모든 것을 전달하는 오버 헤드 (지속적으로 15 가지 정도의 변수가 있음)가 아닌 오버 헤드가 아닌 특정 변수를 전역 변수로 사용하는 것이 가장 좋습니다. 따라서 (C ++의 개인 정적 변수로) 더 나은 범위 지정을 지원하는 프로그래밍 언어를 찾는 것이 네임 스페이스의 잠재적 혼란을 완화하고 변조 된 것을 찾는 것이 합리적입니다. 물론 이것은 단지 일반적인 지식입니다.

그러나 OP가 명시한 접근 방식은 함수형 프로그래밍을 수행하는 경우 매우 유용 합니다.


0

발신자는 아래의 모든 수준에 대해 알지 못하고 신경 쓰지 않기 때문에 반 패턴이 전혀 없습니다.

누군가 더 높은 수준 (매개 변수)을 호출하고 더 높은 수준이 작업을 수행 할 것으로 기대합니다. params와 함께 상위 레벨이하는 것은 발신자 비즈니스가 아닙니다. higherLevel은 params를 level1 (params)에 전달하여 최상의 방법으로 문제를 처리합니다. 절대적으로 괜찮습니다.

콜 체인이 표시되지만 콜 체인이 없습니다. 최상의 작업을 수행하는 기능이 맨 위에 있습니다. 그리고 다른 기능이 있습니다. 각 기능은 언제든지 교체 할 수 있습니다.

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