C #에서 "정적"을 사용하지 않습니까?


109

코드 검토를 위해 다른 건축가에게 작성한 신청서를 제출했습니다. 그들 중 하나가 거의 즉시 저에게 답장을 보내면서 "정적"을 사용하지 마십시오. 정적 클래스와 메소드로 자동화 된 테스트를 작성할 수 없습니다. "정적"은 피해야합니다. "

수업을 확인한 후 1/4 정도의 수업이 "정적"으로 표시되어 있습니다. 클래스는 코드 전체에서 사용되는 단일 전역 클래스이므로 클래스의 인스턴스를 만들지 않을 때는 정적을 사용합니다.

그는 정적 코드와 함께 사용할 수없는 조롱, IOC / DI 기술과 관련된 것을 언급했습니다. 그는 테스트 할 수 없기 때문에 써드 파티 라이브러리가 정적 일 때는 불행한 일이라고 말했다.

이 다른 건축가가 맞습니까?

업데이트 : 여기에 예가 있습니다.

APIManager-이 클래스는 내가 호출하는 타사 API의 사전을 다음 허용 시간과 함께 유지합니다. 많은 제 3자가 서비스 약관에서 가지고있는 API 사용 제한을 시행합니다. Thread.Sleep (APIManager.GetWait ( "ProviderXYZ"));를 호출하여 타사 서비스를 호출하는 모든 곳에서 사용합니다. 전화하기 전에. 여기에있는 모든 것은 스레드 안전하고 C #의 TPL과 잘 작동합니다.


37
static괜찮습니다; static 필드매우 주의해서 다루어야합니다
Marc Gravell

2
왜 타사 라이브러리에 대한 테스트를 작성합니까? 타사 라이브러리의 제작자가 테스트를 수행했다고 가정하면 일반적으로 자신의 코드를 테스트하지 않습니까?
Nope

3
그 특별한 반대는 나에게 너무 일반적입니다. 대부분의 경우 정적 클래스를 변경하지 않고 인수를 조롱합니다. 조롱이 필요한 집계 클래스 인 정적 필드, 예.

8
분명히 당신의 동료는 심각한 전신성 OOPus 홍반과 같은 심각한 상태를 가지고 있습니다. 그들은 최대한 빨리 치료가 필요합니다!
SK-logic

6
답변을 제공하는 답변 섹션이 있습니다. 왜 사람들이 누군가에게 답변 / 제안을 주려고 시도하는 이유를 이해하지 못합니다 ... StackExchange의 목적을 무효화합니다.
peteski

답변:


121

정적 클래스의 상태 유지 여부에 따라 다릅니다. 개인적으로 정적 클래스에서 함께 발생하는 상태 비 저장 함수에는 문제가 없습니다.


111
옳은. 부작용이없는 정적 기능은 가장 테스트 가능한 기능입니다.
Robert Harvey

9
+1이지만 안전 조항이 있습니다. 거의 모든 추상 알고리즘은 상태 비 저장 (stateless)이지만, 비 상태 정적 클래스 또는 메소드로 구현해야한다는 의미는 아닙니다. 그런 식으로 구현하면 코드를 사용하는 모든 코드가 거의 연결되지 않습니다. 예를 들어 정렬 알고리즘을 정적 방법으로 구현하고 프로젝트를 통해이를 사용하는 경우 일시적으로 정렬을 다른 알고리즘 으로 대체 할 수 없습니다. 즉, 테스트 목적과 문제 영역을 좁히기 위해 정렬을 일시적으로 대체 할 수 없습니다 . 이것은 많은 경우에 큰 장애입니다. 그 외에도 합리적으로 사용하면 정적은 완전히 정상입니다.

11
간단히 말해서 : 가장 테스트 가능한 함수이지만, 다른 코드를 더 테스트 할 수있게 만드는 것은 아닙니다 . 이것이 건축가가 의미 한 바입니다.

4
@ tereško : C # 언어를 사용하려면 메서드를 호출하기 위해 클래스의 인스턴스를 만들지 않으려면 정적 메서드가 정적 클래스의 일부 여야합니다. 아마도 "클래스"가 아니라 "인스턴스"를 의미 할 것입니다.
Robert Harvey

8
@quetzalcoatl : 델리게이트 (즉, 다른 알고리즘)를 정적 메소드에 전달하는 것은 완벽하게 합법적입니다. Linq의 확장 메소드는 모두 정적 메소드입니다. 예 : msdn.microsoft.com/en-us/library/…
Robert Harvey

93

그는 그것에 대해 너무 일반적입니다. 그는 맞습니다. 테스트를 방해합니다. 그러나 static클래스와 메소드는 그 위치가 있으며 실제로 컨텍스트에 따라 다릅니다. 코드 예제가 없으면 실제로 말할 수 없습니다.

클래스는 코드 전체에서 사용되는 단일 전역 클래스이므로 클래스의 인스턴스를 만들지 않을 때는 정적을 사용합니다.

코드 냄새가 심할 수 있습니다. 수업을 위해 무엇을 사용하고 있습니까? 데이터를 보유하려면? 데이터베이스에 액세스하려면? 그리고 그는 맞습니다. 이러한 경우 정적 클래스는 사실상 암시적인 싱글 톤이므로 종속성 삽입을 살펴 봐야합니다.

상태를 변경하지 않고 제공 한 매개 변수로 작동하는 확장 메서드 또는 도우미에 사용하는 경우 일반적으로 좋습니다.


3
+1은 진술이 일반적이라고 말하고 확장 방법을 언급 한 것에 대해 +1이며, 테스트 할 수 있고 의존성을 여전히 아는 한 조롱 할 수 있습니다.
Nope

4
암시적인 싱글 톤처럼 단일 클래스 인스턴스로서 정적 정적, 그것은 지저분해질 것입니다 ....

"데이터베이스에 액세스하려면?" 왜 안돼? 테이블 어댑터가 정적이 아닌 경우 정적 클래스 @ 강력한 형식의 데이터 세트를 작성하면 아무런 문제가 없습니다 .... 참조 또는 예제로 요점을 증명하지 않는 한?
수학

27

수업을 확인한 후 1/4 정도의 수업이 "정적"으로 표시되어 있습니다. 클래스는 코드 전체에서 사용되는 단일 전역 클래스이므로 클래스의 인스턴스를 만들지 않을 때는 정적을 사용합니다.

가장 좋은 방법은 코드를 테스트하고 단위 테스트하는 것입니다. 반복 가능하고 독립적이며 단순하며 한 번에 한 가지 방법 만 테스트하는 테스트를 설계하십시오. 다른 무작위 순서로 테스트를 실행하십시오. 안정적인 "녹색"빌드를 얻을 수 있습니까?

그렇다면 코드를 방어 할 수있는 올바른 방법입니다. 그러나 어려움이 있다면 인스턴스 기반 방법을 사용해야 할 수도 있습니다.


7
이것은 나에게도 충격을 준 요점이다. 정적 및 상태 비 저장 '헬퍼'스타일 클래스는 훌륭하지만 모든 클래스의 25 %를 정적 클래스로 보유한 경우에는 해당 노력이 해당 사례로 제한되지 않을 수 있습니다.
Matthew Scharley

2
@MatthewScharley-아마 4 개의 클래스를 받았으며 그 중 하나는 정적입니다 :)
Alexus

1
싱글 톤과 같은 stibc를 사용하는 경우 나중에 더 많은 인스턴스를 작성하는 것이 어려울 수 있습니다. 예를 들어 문서를 처리하는 응용 프로그램을 만드는 것처럼 나중에 여러 문서를 관리하려고 할 때 문제가 발생합니다.
Michel Keijzers

11

이미 게시 된 답변에는 많은 좋은 점이 있지만 누락 된 것으로 보입니다.

정적 필드는 가비지 수집 되지 않습니다 .

많은 메모리 제약 조건이있는 앱이있는 경우 이것은 매우 중요하며 사람들이 캐시를 구현하려고 할 때이 패턴이 매우 일반적 일 수 있습니다.

정적 함수는 거의 나쁘지 않지만 다른 모든 사람들은 이미 충분히 자세히 다루었습니다.


10

IoC / DI에서 얻을 수있는 장점 중 하나는 클래스 간의 상호 작용이 인터페이스간에 협상된다는 것입니다. 인터페이스를 자동 또는 반자동으로 조롱 할 수 있기 때문에 단위 테스트가 쉬워 지므로 각 부품의 입력 및 출력을 테스트 할 수 있습니다. 또한, 모든 것 사이에 인터페이스를 배치하면 테스터 빌리티를 넘어서 가장 모듈화 된 코드를 가질 수 있습니다. 의존성을 망칠 걱정없이 하나의 구현을 쉽게 대체 할 수 있습니다.

C #에는 메타 클래스가 없으므로 클래스는 정적 기능을 사용하여 인터페이스를 구현할 수 없으므로 클래스의 정적 기능은 순수한 IoC / DI 개체 모델을 구현하려는 노력을 방해합니다. 즉, 테스트하기 위해 모의 객체를 만들 수 없으며 실제 종속성을 만들어야합니다.

회사 / 프로젝트가 IoC 수행에 막대한 투자를하는 경우 이는 당연한 문제이며, 여러분이 겪는 고통은 모두의 이익입니다. 그러나 건축 적 관점에서 개인적으로 어떤 모델도 무덤에 따라야한다고 생각하지 않습니다. 정적 메서드 (예 : 싱글 톤 디자인 전략)를 사용하여 구현하는 것이 합리적입니다. 나는 OO의 이점을 잃기 시작하는 경향이 있다고 생각하기 때문에 정적 클래스에 대한 경향이 적지 만 라이브러리 클래스가 엔진보다 자연스러운 표현 일 때가 있습니다.


Commenter는 C #에서 정적 클래스에 있어야하는 확장 메소드를 상기시킵니다. 그것은 합법적이며 적어도 언어의 폭을 활용하려는 경우 C #에서 순수한 IoC가 잘 작동하지 않는 예입니다.


1
클래스가 올바른 이유로 정적이되고 확장 메소드와 같이 올바르게 구현 된 경우 외부 종속성이없고 자체 포함되어 있으므로 조롱 할 필요가 없으므로 테스트가 가능합니다. 순수한 IoC / DI 프로젝트를 유지하기위한 것이 아니라 비즈니스 요구 사항을 최대한 채우려면 디자인 요구 사항으로 인터페이스가 필요하지 않습니까?
Nope

1
귀하와 저는 귀하가 올바른 직업을위한 올바른 도구를 선택해야하며 철학에 의해 수갑을 치지 말아야한다는 일반적인 관점에 동의합니다. 그러나 순수한 IoC / DI 프로젝트에 제대로 확립 된 문화가 있다면 전통적으로 하향식 솔루션을 IoC / DI로 변환하는 것만 큼 피해를 입을 수 있습니다. 엔지니어링은 전환 비용을 고려해야합니다. 그건 그렇고, 확장 방법이 당신을 거기에 도달한다고 생각하지 않습니다. 정적 클래스를 사용하는 IoCish 동작에 대한 더 나은 솔루션은 컴파일 타임에 프리 프로세서 지시문 C 스타일을 사용하여 여러 클래스를 선택하는 것입니다
jwrush

1
어. 난 바보 야. HOLDING 확장 메소드에 대한 정적 클래스를 의미했습니다. 물론, 메소드를 수행해야합니다. 물론 그 목적을 위해 정적 클래스를 원합니다. 그것은 내 마음을 미끄러 뜨렸다. 그리고 이것은 C #이 IoC를위한 것이 아니라는 것을 보여줍니다. :)
jwrush

네, 그렇습니다. 기존 프로젝트에 기존 문화가 존재하는 경우 일이 수행되는 방식을 바꾸는 데 도움이되는 것보다 더 큰 피해를 줄 수 있습니다.
Nope

5

정적 클래스는 때때로 잘못 사용되며 다음과 같아야합니다.

  • 싱글 톤 (정적이 아닌 클래스에는 정적 멤버와 공용 정적 인스턴스 변수가있어 한 번만 검색 / 만들기)
  • 상태가 중요한 다른 클래스의 메소드. 해당 클래스의 데이터를 사용하지 않는 멤버가있는 경우 해당 클래스의 일부가 아니어야합니다.

소위 '유틸리티'함수일 수도 있고 유틸리티 클래스의 일부일 수도 있습니다. 유틸리티 클래스는 정적 메서드 만 포함하고 컨텍스트없이 도우미 함수로 사용되는 클래스입니다.


@topomorto 클래스는 여러 객체 / 인스턴스를 생성하는 생성자에 의해 인스턴스화되지 않아야하지만 항상 동일한 인스턴스를 반환하는 특수 메소드 (일반적으로 instance ()) <에 의해 인스턴스화되어서는 안되므로 instance ()를 처음 호출 한 후에 인스턴스화됩니다.
Michel Keijzers

@topomorto 방금 확인했는데 당신은 완전히 옳습니다. 일부 멤버 만 정적입니다 (인스턴스 변수 및 인스턴스 함수). 이에 따라 답변을 변경하겠습니다. 언급 해 주셔서 감사합니다.
Michel Keijzers

4

정적 메소드는 사용하기에 적합하며 프로그래밍에서 올바른 위치에 있습니다. 건축가가 정적 메소드를 완전히 지원하지 않는 테스트 프레임 워크가있는 것 같습니다. 코드가 더 큰 프로젝트의 일부인 경우 건축가 지침을 충족하는 것이 중요합니다. 그러나이 프로젝트가 적절한 경우 정적 메소드 사용을 방해하지 않도록하십시오.


1

클래스의 모든 인스턴스에 공통적 인 것들에 정적 속성을 사용하고 있습니다. 그리고 정적 메소드를 사용하여 클래스 객체 그룹을 얻었습니다. 나는 결코 전문가가 아니지만 이것은 지금까지 나를 위해 일 해왔다.

PHP 예 :

class vendorData {
  private static $data_table = 'vendor_data'; // static property
  private $id; // instance property
  private $name; // instance property

  public function __construct($vendor_id) {
    if(!self::validId($vendor_id) ) { // static method
      return false; // id doesn't exist
    }
    $this->id = $vendor_id; // id has been validated
    $this->getProperties(); // object method
  }

  private static function validId($vendor_id) {
    // send db query to find if the id exists
    // return true/false;
  }

  private function getProperties() { // object method
    $sql = "SELECT * FROM `{self::$data_table}` // using static property
        WHERE `id` = {$this->id}"; // using object instance property
    // get resultset
    foreach($result as $property => $value) {
      $this->$property = $value; // object instance properties all set
    }
  }

  // and here
  public static function getBy($property,$value) { // static method to return object array
    $sql = "SELECT `id` FROM `{self::$data_table}` // using static property
      WHERE `$property` = '$value'";
    // send query, get $ids array
    $obj_array = array();
    foreach($ids as $id) {
      // create array of current class objects using special static keyword
      // meaning of static here is different than in property/method declarations
      $obj_array[$id] = new static($id);
    }
    return $obj_array;
  }
}

1

나는 그들이 가지고있는 원칙은 괜찮다고 말하지만 (정적을 사용하지 마십시오) 진술은 잘못되었을 수 있습니다. 코드 범위는 얼마입니까? 숫자가 높고 앱의 단위 테스트에 익숙하다면 괜찮습니다. 그렇지 않다면 코드를 검토하는 것이 좋습니다. 정적 클래스가 그 이유인지 아닌지를 알 수 있습니다. 많은 것들에 달려 있습니다.


-3

정적 메서드가있는 클래스는 OOP의 원칙을 남용합니다. 이미 OOP가 아니며 클래스 지향 프로그래밍 인 COP입니다.

그들은 명확한 "성격"(나는 여기에서 내가 좋아하는 인간 은유 를 사용한다) 또는 정체성을 거의 갖지 않는다 . 그래서 그들은 자신이 누구인지 모릅니다. 이 점만으로도 OOP에서이 개념을 제거하기에 충분하지만 더 많은 개념이 있습니다.

다음은 첫 번째 요점의 결과입니다. 그러한 클래스는 자신이 누구인지 알지 못하기 때문에 크거나 큰 경향이 있습니다.

세 번째는 코드를 절대적으로 유지 관리 할 수 ​​없게 만듭니다. 정적 메서드를 사용하면 숨겨진 종속성이 생깁니다 . 그들은 모든 곳에서 팝업되며 수업에서 무슨 일이 일어나고 있는지 전혀 알지 못합니다. 생성자 서명을 보는 것만으로는 확실하지 않습니다.

숨겨진 의존성이 제대로 제어되지 않기 때문에 느리지 만 불가피하게 코드가 점점 더 응집력이 떨어지고 있습니다. 그들은 보이지 않는 것 같습니다. 그리고 다른 것을 추가하는 것은 매우 쉽습니다! 메소드의 서명을 수정할 필요는 없습니다. 정적 메소드를 호출하기 만하면됩니다. 그리고 추악한 코드가 따르는 것 ...

좋아, 아마 이미 짐작했을 것입니다. 긴밀한 결합은 종종 응집력이 낮습니다. 어떤 클래스를 사용하는 클라이언트가 많으면 커플 링이 더 엄격 해집니다. 그리고 이미 작성된 클래스 나 메소드를 사용하면 약간의 수정 만하면됩니다. 메소드 플래그 매개 변수는 하나뿐입니다.

정적 메소드 대신 무엇을 사용해야합니까? 글쎄, 내 제안은 OOP입니다. 시도해 보지 않겠습니까?

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