모든 정적 메소드를 사용할 수 있습니까?


64

아래 두 UpdateSubject 메소드의 차이점은 무엇입니까? 엔터티에서 작업하려는 경우 정적 메서드를 사용하는 것이 좋습니다. 어떤 상황에서 비 정적 방법을 사용해야합니까?

public class Subject
{
    public int Id {get; set;}
    public string Name { get; set; }

    public static bool UpdateSubject(Subject subject)
    {
        //Do something and return result
        return true;
    }
    public bool UpdateSubject()
    {
        //Do something on 'this' and return result
        return true;
    }
}

나는이 성가신 질문에 대해 커뮤니티에서 많은 발차기를 겪을 것이라는 것을 알고 있지만 나는 그것을 묻는 것을 멈출 수 없었다.

상속을 다룰 때 이것이 비현실적입니까?

업데이트 :
지금 우리 직장에서 일어나고 있습니다. 우리는 5 명의 개발자와 함께 6 개월간의 asp.net 웹 애플리케이션을 개발하고 있습니다. 우리 건축가는 모든 API에 모든 정적 메소드를 사용하기로 결정했습니다. 정적 메소드에 대한 그의 추론은 가벼우 며 서버로드를 줄임으로써 웹 애플리케이션에 유리합니다.


9
Rich Hickey는 이와 같은 비 이념적 인 것을 권장합니다 (모든 정적 메소드). 기능적 스타일을 좋아한다면 기능적 언어를 사용할 수도 있습니다.;) 소프트웨어의 모든 것이 객체로 가장 잘 모델링되는 것은 아닙니다.
Job

13
@ JOB : 이것이 실제로 어떻게 작동하는지 알지 못합니다-절차 적입니다.
Aaronaught

2
@Job :이 클래스는 변경할 수 없습니다.
Aaronaught

3
@DonalFellows : 물론 C #과 F #은 모두 혼합 패러다임입니다. 그러나 사실은 정적 메소드! = 함수형 프로그래밍입니다. FP는 전달 / 체인 기능, 변경 불가능한 데이터 유형, 부작용 방지 등을 의미합니다. 이는 OP의 코드 스 니펫과 완전히 반대입니다.
Aaronaught

4
"그의 정적 방법에 대한 추론은 가벼우 며 서버로드를 낮춤으로써 웹 애플리케이션에 도움이됩니다." 그건 잘못이야 서버로드 유지?
mamcx

답변:


87

명시적인 "this"매개 변수를 사용하여 정적 메소드의 가장 명백한 문제점을 다루겠습니다.

  1. 가상 디스패치와 다형성이 손실됩니다. 파생 클래스에서 해당 메서드를 재정의 할 수 없습니다. 물론 파생 클래스에서 new( static) 메서드를 선언 할 수 있지만이 클래스에 액세스하는 모든 코드는 전체 클래스 계층 구조를 알고 명시 적 검사 및 캐스팅을 수행 해야 합니다.

  2. 인터페이스는 대부분의 언어에서 메서드를 선언 할 수 없기 때문에 # 1 확장명으로 클래스의 인스턴스를 interface로 바꿀 수 없습니다 static.

  3. 불필요한 세부 사항. : 어느 것이 더 읽을 Subject.Update(subject)아니면 그냥 subject.Update()?

  4. 인수 확인. 다시 언어에 의존하지만, 많은 사람들은 null 참조 버그가 안전하지 않은 런타임 조건 (버퍼 오버런의 종류)을 생성하지 않도록 this인수가 아님 null을 확인하기 위해 암시 적 검사를 컴파일합니다 . 인스턴스 메소드를 사용하지 않으면 모든 메소드의 시작 부분에이 검사를 명시 적으로 추가해야합니다.

  5. 혼란 스럽습니다. 정상적이고 합리적인 프로그래머가 static메소드를 발견하면 자연스럽게 유효한 인스턴스가 필요 하지 않다고 가정 합니다 (비교 또는 평등 메소드와 같이 여러 인스턴스가 필요하거나 null참조 에서 작동 할 것으로 예상되지 않는 한 ) . 이 방법으로 사용되는 정적 메소드를 보면 이중 또는 삼중 테이크 아웃을 수행 할 수 있으며 4 ~ 5 회 후에 스트레스를 받고 화를 내고 집 주소를 알면 신이 도와줍니다.

  6. 그것은 복제의 한 형태입니다. 인스턴스 메소드를 호출 할 때 실제로 발생하는 것은 컴파일러 또는 런타임이 유형의 메소드 테이블에서 메소드를 찾아서 this인수로 사용하여 호출하는 것 입니다. 기본적으로 컴파일러가 이미 수행 한 작업을 다시 구현하고 있습니다. DRY를 위반 하여 필요하지 않은 경우 다른 방법으로 동일한 매개 변수를 반복해서 반복합니다.

그것은 어떤의 상상하기 어렵다 좋은 정적 메서드와 인스턴스 메서드를 대체하는 이유. 제발 하지마


5
마지막 줄만 +1하십시오. 더 많은 사람들이 "정적 방법"을 생각하기 전에 "인스턴스 방법"을 생각하길 바랍니다
Stuart Leyland-Cole

4
+1. 정적 메소드는 테스트를 작성하기 어렵게 만듭니다. 대부분의 경우 모의 할 수 없으므로 테스트의 일부가 정적 메소드에 의존하면 no를 대체 할 수 없습니다. 당신의 테스트에서 op. 닷넷 세계에서 대표적인 사례는 수업이 좋아하는 것 File, FileInfo그리고 DirectoryInfo(사실, 필요하고 테스트에서 조롱으로 다음 주입 할 수있는 인터페이스 뒤에 숨길 라이브러리가있다).
sm

나는 다형성 / 상속에 대한 당신의 요점이 무질서하다고 생각합니다. 코드 재사용을 위해 상속을 사용해서는 안되므로 관련이 없습니다. 상속 부분도 모호합니다. 대부분의 현대 언어에서 메소드 서명은 그 자체로 다형성입니다. 기능적인 접근 방식을 취하면 메소드를 전달하기 시작하고 인터페이스를 기반으로 상호 교환 가능한 간단한 메소드로 복잡한 메소드를 작성합니다. OOP와 마찬가지로 단순히 설계하여 설계하지 않아도 상쇄되지 않는 정적 메소드에는 근본적으로 결점이 없습니다.
sara

@sm 그것은 사실이 아닙니다. 정적이든 아니든 ANYTHING에 대한 의존도가 높으면 단위 테스트를 할 수 없으며 테스트 할 수없는 기간입니다. static은 이것을 유발하지 않습니다. 정적 메소드는 DB 연결을 나타내는 클래스와 동일한 방식으로 주입 될 수 있습니다. "정적"은 "글로벌 상태 및 외부 자원을 건 드리는 정적 및 강제 숨겨진 종속성"을 의미한다고 가정합니다. 그건 불공평하네요.
sara

@kai는 X와 Y가 무엇이든 X 대신 Y를 수행하는 정적 메소드를 주입하십시오. 나사로 조이기 위해 외부에 의존 할 필요는 없습니다. 아마도 긴 계산 X가 필요하지 않은 일부 측면을 테스트하는 데 관심이있을 수 있습니다. 그러면 래퍼를 만들지 않고 멋진 정적 메서드를 어떻게 주입합니까? 모든 언어에서 함수 전달을 지원하지는 않습니다. 정적 메소드에는 유스 케이스가 있으며 적절할 사용합니다 . 이것은 항상 그렇듯이 "당신이 반드시 당신을 꼭 의미 할 필요는 없기 때문에"의 경우입니다.
sm

13

정적 메소드 만 사용할 수있는 C에서 OOP를 수행하는 방법을 정확히 설명했으며 "this"는 구조체 포인터입니다. 그리고 네, 생성하는 동안 구조체의 한 요소로 저장되도록 함수 포인터를 지정하여 C에서 런타임 다형성을 수행 할 수 있습니다. 다른 언어로 제공되는 인스턴스 메소드는 구문 적으로 당연히 동일한 기능을하는 구문 설탕입니다. 파이썬과 같은 일부 언어는 중간에 "self"매개 변수가 명시 적으로 나열되어 있지만 호출을위한 특수 구문은 상속과 다형성을 처리합니다.


FWIW, C를 사용하면 다음 obj->type->mthd(obj, ...);과 같이 가상 디스패치를 ​​수행 할 수 있습니다. 이는 다른 언어로 가상 디스패치에서 발생하는 것과 비슷하지만 (뒤에서) 발생합니다.
Donal Fellows

@Karl 좀 더 깊이 파고 들기 위해 어떤 종류의 서면 참고서를 제공해 주시겠습니까?
Jorge Lavín

좋은 참조 구현으로 GObject 를 살펴볼 수 있습니다 .
Karl Bielefeldt

10

정적 메소드의 문제점은 서브 클래스가 필요한 순간입니다.

정적 메서드는 하위 클래스에서 재정의 될 수 없으므로 새 클래스는 메서드의 새로운 구현을 제공 할 수 없으므로 유용성이 떨어집니다.


2
이것이 반드시 문제 는 아닙니다 . 일부 언어는 의도적으로 동일한 작업을 수행하기위한 다른 메커니즘 (C ++ 및 C #의 비가 상 메소드)을 제공합니다. C #은이 개념을 더욱 확장하기 위해 "밀봉 된"메소드도 제공합니다! 분명히, 당신은 단지 그것을 위해 이것을하지 않을 것이지만 그것을 알고 좋은 기술입니다 ...
Shog9

@Steve는 두 방법을 직접 호출 할 수 있으므로 대체하지 않습니다.

Java 스태틱 메소드에서 @Steve는 대체 할 수 없습니다.

@ Thorbjørn-첫째, 질문은 Java 또는 다른 특정 OOP 언어로 태그되지 않습니다. 더 중요한 것은 정적 멤버 함수를 재정의 할 수 있다고 말하지 않았습니다. 나는 비유를 사용하여 요점을 찾으려고 노력했다. 내가 분명히 실패하자 주석이 삭제되었습니다.
Steve314

2
어떤 의미에서, 이들은 여전히 ​​"새로운 메소드 구현"입니다. 표준 OOP 의미가 아닙니다. 나는 "당신의 문구 중 하나 완벽하지 NUR-NUR - 없음 NUR-NUR"를 말하는 것 같은이 조금 느낌 ;-)
Steve314

7

메소드를 선언하면 메소드 를 실행하기 위해 static클래스에서 객체를 인스턴스화하지 않아도됩니다 ( new키워드 사용). 그러나 정적이 아닌 한 멤버 변수는 참조 할 수 없습니다 .이 경우 해당 멤버 변수 는 클래스 의 특정 인스턴스화 된 객체가 아니라 클래스에 속합니다 .

달리 말하면 static키워드는 선언이 클래스 정의의 일부가되므로 클래스의 모든 인스턴스 (클래스에서 인스턴스화 된 객체)에서 단일 참조 점이 됩니다.

따라서 클래스의 여러 실행 사본을 원하고 모든 실행 사본간에 상태 (구성원 변수)를 공유하지 않으려는 경우 (즉, 각 오브젝트는 고유 한 상태를 유지함) 해당 멤버 변수를 정적으로 선언 할 수 없습니다.

일반적으로, static하나 이상의 매개 변수를 허용하고 부작용없이 (예 : 클래스의 상태 변수 변경) 유틸리티를 작성하는 경우에만 클래스와 메소드 를 사용해야합니다.


1
나는 OP가 static의미 하는 바를 이해하고 , 왜 모든 것을 정적으로 선언하지 않는지 묻습니다.
Ed S.

1
그는 되는 인스턴스를 전달 Subject하는 대신 암시 적으로 만 명시 적 매개 변수로 - this매개 변수입니다.
Steve314

글쎄요 ..하지만 당신의 생각이 보이지 않습니다.
Ed S.

@Ed-암시 적 this매개 변수를 통해 할 수있는 명시 적 매개 변수를 통해 거의 모든 작업을 수행 할 수 있습니다 . 기대에는 차이가 있지만 상속 외부에서는 할 있는 일에 실질적인 차이가 없습니다 . 예를 들어, 정적이 아닌 멤버 명시 적 매개 변수를 통해 액세스 할 있습니다.
Steve314

나는 C #에서와 같이 동일한 클래스에서 선언 된 정적 메소드를 통해서도 C #에서 인수의 개인 멤버를 얻을 수 없다는 가정하에 운영 중이었습니다. 왜 그렇게 생각했는지는 모르겠지만 틀 렸습니다.
Ed S.

3

사람들이 '정적 메소드가 나쁜 디자인을 보여준다'고 주장하는 이유는 반드시 메소드 때문이 아니라 실제로는 암묵적이거나 명시적인 정적 데이터입니다. 암시 적으로는 반환 값 또는 매개 변수에 포함되지 않은 프로그램의 모든 상태를 의미합니다. 정적 함수에서 상태를 수정하는 것은 절차 적 프로그래밍에 대한 후퇴입니다. 그러나 함수가 상태를 수정하지 않거나 상태 수정을 구성 요소 객체 함수에 위임하는 경우 실제로 절차 적 기능보다 기능적 패러다임입니다.

상태를 변경하지 않으면 정적 메서드와 클래스에 많은 이점이 있습니다. 분석법이 순수하고 부작용이 없으며 오류가 완전히 재현되도록하는 것이 훨씬 쉬워집니다. 또한 공유 라이브러리를 사용하는 여러 응용 프로그램에서 서로 다른 방법을 사용하면 동일한 입력으로 동일한 결과를 얻을 수 있으므로 오류 추적 및 단위 테스트가 훨씬 쉬워집니다.

궁극적으로 'StateManager'와 같이 일반적으로 보이는 대형 객체와 정적 또는 전역 데이터 사이에는 실제로 차이가 없습니다. 정적 메소드를 올바르게 사용하면 나중에 개정 자에게 상태를 수정하지 않으려는 의도가 있음을 알 수 있습니다.


1
귀하의 진술 "... 절차 적 프로그래밍에 대한 후퇴입니다 ..."는 당신이 그것을 너무 나쁘다고 생각한다고 생각합니다.
NoChance

making ... unit testing much easier.아닙니다. 정적 메소드와 코드를 분리 할 수 ​​없기 때문에 정적 메소드를 호출하는 코드는 단위 테스트가 불가능합니다 . 정적 메소드에 대한 호출은 해당 클래스에 대한 하드 코딩 된 종속성이됩니다.
StuperUser

2

언어와 수행하려는 작업에 따라 큰 차이가 없지만 static버전이 조금 더 혼란 스럽다는 대답이 있습니다 .

예제 구문에서 Java 또는 C #이 제안한 것처럼 Thorbjørn Ravn Andersen이 재정의 (늦은 바인딩) 문제를 지적하는 것이 옳다고 생각합니다. 정적 메서드에는 후기 바인딩을 기반으로하는 "특별한"매개 변수가 없으므로 늦은 바인딩을 가질 수 없습니다.

실제로 정적 메서드는 모듈과 같은 이름을 가진 모듈의 함수일 뿐이며 실제로는 OOP 메서드가 아닙니다.


2

기본적으로이 작업을 수행 할 때 객체의 요점을 모두 물리칩니다. 절차 코드에서 객체 지향 코드로 거의 전환 한 이유가 있습니다.

클래스 유형을 매개 변수로 사용하는 정적 메서드를 절대 선언 하지 않습니다 . 그것은 이득없이 코드를 더 복잡하게 만듭니다.


3
당신이 통과한다면 당신은 그것을 할 수있는 objectA와 static방법, 새로운 반환 object. 함수형 프로그래머는 항상이 작업을 수행합니다.
Robert Harvey

질문 : Math정적 클래스 "게인이없는 복잡한" 을 고려합니까 , 아니면 모든 숫자 유형에 절대 값, 부정, 추가, 제곱, 임의의 근 등을 정의하는 가상 메소드가있는 경우 선호합니까? 나는 그렇게 생각하지 않습니다. 불변 값을 적용해야하는 숨겨진 가변 상태를 저장해야 할 때 객체가 깔끔합니다. 유형에서 작동하는 모든 결정 가능한 메소드를 유형 자체로 통합하면 복잡하고 유지 보수 할 수없는 코드로 연결됩니다. Robert에 동의합니다. 함수형 프로그래머의 작동 방식을보고 싶을 수도 있습니다.
sara

@kai 내가 말했듯이, 거의. Math 클래스는 합리적인 예외이지만 숫자 유형에 첨부하는 것은 나쁜 생각이 아닙니다.
Loren Pechtel 2016 년

2

여기에 많은 훌륭한 답변이 있으며, 답변으로 생각해 낼 수있는 것보다 훨씬 더 통찰력과 지식이 있지만, 옷을 입지 않은 것이 조금 있다고 느낍니다.

"저희 설계자는 모든 API에 모든 정적 메소드를 사용하기로 결정했습니다. 정적 메소드에 대한 그의 추론은 가벼우 며 서버로드를 줄임으로써 웹 애플리케이션에 도움이됩니다 ."

(굵은 강조는 내 것입니다)

질문 의이 부분에 대한 2C는 다음과 같습니다.

이론적으로는, 정적 메서드를 호출하면 실제로 해당 메서드를 호출하는 오버 헤드 만 있습니다. 비 정적 (인스턴스) 메소드를 호출하면 객체를 먼저 인스턴스화하고 어느 시점에서 인스턴스를 파괴하는 추가 오버 헤드가 발생합니다 (사용 된 플랫폼에 따라 자동 가비지 콜렉션의 일부 또는 수동을 통해).

이것에 대한 악마의 옹호자의 놀이 : 우리는 더 나아가서 다음과 같은 것을 말할 수 있습니다 :

  • (인스턴스) 메소드를 호출 할 때마다 인스턴스가 생성되면 실제로 나빠질 수 있습니다 (정적 상태로두고 호출하는 것과는 대조적으로)

  • 생성자의 복잡성, 유형 계층 구조, 다른 인스턴스 멤버 및 기타 알려지지 않은 요소에 따라 비 정적 메서드 호출의 추가 오버 헤드가 다양하고 실제로 커질 수 있습니다.

사실, IMHO, 위의 요점 (그리고 "정적이 빠르기 때문에 정적을 사용하자"의 일반적인 접근 방식)은 짚맨 논쟁 / 평가입니다.

  • 코드가 좋고이 인수에 더 구체적 인 경우 인스턴스가 필요할 때만 작성되고 (최적의 방식으로 파괴 된 경우) 적절한 경우 인스턴스 메소드를 사용하여 추가 오버 헤드를 얻지 못합니다 ( 필요한 객체 만 생성하십시오. 해당 메소드가 정적 인 것으로 선언 된 경우에도 생성되었을 것입니다.

  • 이 방법으로 정적 메소드 선언을 남용하면 인스턴스 작성 방법과 관련하여 코드와 관련된 일부 문제를 숨길 수 있습니다 (메소드가 정적이기 때문에 잘못된 인스턴스 코드는 나중에 알려지지 않았으므로 결코 좋지 않습니다).


2

이것은 당신이 묻는 커뮤니티에 따라 다른 답변을 갖는 경향이있는 매우 흥미로운 질문입니다. 다른 대답은 C # 또는 Java 바이어스 인 것 같습니다. 일반적으로 순수한 객체 지향적이고 객체 방향이 무엇인지에 대한 관용적 인 견해를 갖는 프로그래밍 언어입니다.

C ++과 같은 다른 커뮤니티에서는 객체 지향이보다 자유로이 해석됩니다. 일부 전문가들은이 주제를 연구하고 실제로 자유 기능이 캡슐화를 개선 한다고 결론을 내 렸습니다 .

클래스에서 캡슐화의 양을 측정하는 합리적인 방법은 클래스의 구현이 변경되면 깨질 수있는 함수의 수를 계산하는 것입니다. 이 경우 n 개의 멤버 함수가있는 클래스보다 n 개의 멤버 함수가있는 클래스가 더 캡슐화되어 있음이 분명해집니다. 그리고 그 관찰은 비회원 비 친구 기능을 회원 기능보다 선호 한다는 주장을 정당화 하는 것입니다

스콧 마이어스

C ++ 용어에 익숙하지 않은 사람들을 위해 :

  • 멤버 함수 = 방법
  • 비 멤버 함수 = 정적 메서드
  • 비 친구 = 공개 API 만 사용
  • 비회원 비 친구 기능 = 공용 API 만 사용하는 정적 메소드

이제, 당신에게 질문에 대답하기 위해 :

모든 정적 메소드를 사용할 수 있습니까?

아니요, 항상 정적 메서드를 사용해서는 안됩니다.

그러나 클래스 의 공개 API 만 사용해야 할 때마다 사용해야합니다 .


-3

자바에서 ...

정적 메서드는 덮어 쓰지 않고 대신 숨겨져 클래스를 확장하는 경우 큰 차이 (문제?)가됩니다.

반면에 나는 자바 프로그래머가 이유를 이해하지 않고 모든 HAS가 객체라고 생각하는 경향이 있음을 알았습니다.

클래스 고유의 코드에는 정적을 사용해야합니다. 정적은 상속에서 잘 작동하지 않습니다.

정적 메소드 사용의 좋은 예는 부작용이없는 독립 함수입니다.

키워드 동기화 참조 ...


2
이 정적 메서드는 어떻게 숨기고 클래스를 확장 할 때이 차이점과 문제가 어디에서 발생합니까? 동기화는 이것과 어떻게 맞습니까? 정적 및 상속 문제는 어디에서 발생합니까? 핵심 Java 라이브러리에 좋은 정적 메소드와 나쁜 정적 메소드를 제공하고 각각 왜 그런지 설명 할 수 있습니까?
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.