인스턴스 또는 정적 도우미 메소드 중 어떤 것이 더 나은 방법입니까?


48

이 질문은 주관적이지만 대부분의 프로그래머가 어떻게 접근하는지 궁금합니다. 아래 샘플은 의사 C #이지만 Java, C ++ 및 기타 OOP 언어에도 적용됩니다.

어쨌든 내 클래스에서 도우미 메서드를 작성할 때 도우미 메서드를 정적으로 선언하고 도우미 메서드에 필요한 경우 필드를 전달하는 경향이 있습니다. 예를 들어 아래 코드가 주어지면 Method Call # 2 사용하는 것이 좋습니다 .

class Foo
{
  Bar _bar;

  public void DoSomethingWithBar()
  {
    // Method Call #1.
    DoSomethingWithBarImpl();

    // Method Call #2.
    DoSomethingWithBarImpl(_bar);
  }

  private void DoSomethingWithBarImpl()
  {
    _bar.DoSomething();
  }

  private static void DoSomethingWithBarImpl(Bar bar)
  {
    bar.DoSomething();
  }
}

이 작업을 수행하는 이유는 도우미 메서드가 구현을 읽지 않아도 다른 개체에 가능한 부작용이 있음을 분명히 보여주기 때문입니다. 이 방법을 사용하여 디버깅하는 데 도움이되는 메서드를 신속하게 파악할 수 있습니다.

자신의 코드에서 어떤 것을 선호 하고 그렇게하는 이유는 무엇입니까?


3
나는이 질문에서 C ++를 제거 할 것이다.
Matthieu M.

1
@MatthieuM .: 무료 방법인지 여부는 중요하지 않습니다. 내 질문의 목적은 도우미 메서드를 인스턴스 메서드로 선언 할 때 어떤 이점이 있는지 묻는 것입니다. 따라서 C ++에서 인스턴스 메소드 대 무료 메소드 / 함수를 선택할 수 있습니다. ADL에 대한 좋은 지적이 있습니다. 그래서 당신은 무료 방법을 사용하고 싶다고 말하는가? 감사!
Ilian

2
C ++의 경우 무료 메소드가 권장됩니다. 캡슐화를 높이고 (간접적으로 공용 인터페이스에 액세스 할 수 있으므로) ADL (특히 템플릿)로 인해 여전히 훌륭하게 재생됩니다. 그러나 이것이 Java / C #에 적용 가능한지 모르겠습니다 .C ++는 Java / C # 과 크게 다르므로 답변이 다를 것으로 기대합니다 (따라서 3 개의 언어를 함께 요구하는 것은 의미가 없다고 생각합니다).
Matthieu M.


1
누군가를 악화시키지 않으면 서 정적 메소드를 사용하지 않는 좋은 이유를 들어 본 적이 없습니다. 메소드가 수행하는 작업을보다 명확하게하기 때문에 가능한 모든 곳에서 사용합니다.
Sridhar Sarnobat

답변:


23

이것은 실제로 달려 있습니다. 도우미가 작동하는 값이 기본 인 경우 Péter가 지적한 것처럼 정적 메서드를 사용하는 것이 좋습니다.

복잡한 경우 SOLID , 특히 S , ID가 적용 됩니다.

예:

class CookieJar {
      function takeCookies(count:Int):Array<Cookie> { ... }
      function countCookies():Int { ... }
      function ressuplyCookies(cookies:Array<Cookie>
      ... // lot of stuff we don't care about now
}

class CookieFan {
      function getHunger():Float;
      function eatCookies(cookies:Array<Cookie>):Smile { ... }
}

class OurHouse {
      var jake:CookieFan;
      var jane:CookieFan;
      var cookies:CookieJar;
      function makeEveryBodyAsHappyAsPossible():Void {
           //perform a lot of operations on jake, jane and the cookies
      }
      public function cookieTime():Void {
           makeEveryBodyAsHappyAsPossible();
      }
}

이것은 당신의 문제에 관한 것입니다. 필요한 매개 변수를 사용 하는 정적 메서드를 만들 수 있습니다makeEveryBodyAsHappyAsPossible . 다른 옵션은 다음과 같습니다.

interface CookieDistributor {
    function distributeCookies(to:Array<CookieFan>):Array<Smile>;
}
class HappynessMaximizingDistributor implements CookieDistributor {
    var jar:CookieJar;
    function distributeCookies(to:Array<CookieFan>):Array<Smile> {
         //put the logic of makeEveryBodyAsHappyAsPossible here
    }
}
//and make a change here
class OurHouse {
      var jake:CookieFan;
      var jane:CookieFan;
      var cookies:CookieDistributor;

      public function cookieTime():Void {
           cookies.distributeCookies([jake, jane]);
      }
}

이제 OurHouse쿠키 배포 규칙의 복잡성에 대해 알 필요가 없습니다. 이제 규칙을 구현하는 객체 만 있어야합니다. 구현은 객체로 추상화되어 있으며, 그 책임은 전적으로 규칙을 적용하는 것입니다. 이 개체는 별도로 테스트 할 수 있습니다. OurHouse의 단순한 모의를 사용하여 테스트 할 수 있습니다 CookieDistributor. 쿠키 배포 규칙을 쉽게 변경할 수 있습니다.

그러나 과도하게 사용하지 않도록주의하십시오. 예를 들어 30 개의 클래스로 구성된 복잡한 시스템의 구현은 CookieDistributor각 클래스가 작은 작업을 수행하는 것만으로도 의미가 없습니다. SRP에 대한 나의 해석은 각 클래스가 하나의 책임을 가질 수 있다고 지시 할뿐만 아니라 단일 클래스가 단일 책임을 수행해야한다는 것입니다.

프리미티브 또는 프리미티브처럼 사용하는 객체 (예 : 공간, 행렬 또는 무언가의 점을 나타내는 객체)의 경우 정적 도우미 클래스는 의미가 있습니다. 선택의 여지가 있고 실제로 이해가된다면 실제로 데이터를 나타내는 클래스에 메소드를 추가하는 것을 고려할 수 있습니다. 예를 들어 메소드 Point를 갖는 것이 합리적입니다 add. 다시, 그것을 과장하지 마십시오.

따라서 문제에 따라 여러 가지 방법이 있습니다.


18

유틸리티 클래스의 메소드를 선언하는 것은 잘 알려진 관용구 static이므로 이러한 클래스를 인스턴스화 할 필요는 없습니다. 이 관용구를 따르면 코드를 이해하기 쉽게 만드는 것이 좋습니다.

이 방법에는 심각한 제한이 있습니다. 이러한 메소드 / 클래스는 쉽게 조롱 할 수는 없지만 (AFAIK는 적어도 C #에서는 이것을 달성 할 수있는 조롱 프레임 워크가 있지만, 평범하지 않으며 적어도 일부는 상업용). 따라서 도우미 메서드에 외부 종속성 (예 : DB)이있어 호출자가 단위 테스트를 어렵게하는 경우 정적이 아닌 것으로 선언하는 것이 좋습니다 . 이것은 의존성 주입을 허용하므로 메소드의 호출자가 단위 테스트를 더 쉽게 만듭니다.

최신 정보

설명 : 위의 유틸리티 클래스는 낮은 수준의 도우미 메서드 만 포함되며 일반적으로 상태는 없습니다. 상태 비 유틸리티 클래스 내의 헬퍼 메소드는 다른 문제입니다. OP를 잘못 해석 한 것에 대해 사과드립니다.

이 경우 코드 냄새가납니다. 주로 클래스 B의 인스턴스에서 작동하는 클래스 A의 메소드는 실제로 클래스 B에서 더 나은 위치를 차지할 수 있습니다. 그러나 원래 위치를 유지하기로 결정한 경우 옵션 1을 선호합니다. 더 간단하고 읽기 쉽습니다.


정적 도우미 메서드에 종속성을 전달할 수 없습니까?
Ilian

1
@JackOfAllTrades, 메소드의 유일한 목적이 외부 자원을 다루는 것이라면 그 의존성을 주입하는 것은 거의 없습니다. 그렇지 않으면 해결책이 될 수 있습니다 (후자의 경우 방법이 단일 책임 원칙을 위반할 수 있으므로 리팩토링의 후 보임).
Péter Török

1
정적은 쉽게 '조롱'됩니다-Microsoft Moles 또는 Fakes (VS2010 또는 VS2012 +에 따라 다름)를 사용하면 정적 메소드를 테스트에서 사용자 고유의 구현으로 바꿀 수 있습니다. 오래된 조롱 프레임 워크를 사용하지 않습니다. (또한 전통적인 조롱도하고 구체적인 수업도 조롱 할 수 있습니다). 확장 관리자를 통해 MS에서 무료입니다.
gbjbaanb

4

자신의 코드에서 어떤 것을 선호하십니까

this->DoSomethingWithBarImpl();물론 도우미 메서드가 인스턴스의 데이터 / 구현에 액세스 할 필요가없는 한, # 1 ( )을 선호합니다 static t_image OpenDefaultImage().

그렇게하는 이유는 무엇입니까?

공용 인터페이스와 개인 구현 및 멤버는 별도입니다. # 2를 선택하면 프로그램을 읽기가 훨씬 어려워집니다. # 1을 사용하면 항상 멤버와 인스턴스를 사용할 수 있습니다. 많은 OO 기반 구현과 비교할 때 많은 캡슐화를 사용하는 경향이 있으며 클래스 당 구성원은 거의 없습니다. 부작용이있는 한-종속성이있는 상태 유효성 검사가 종종 있습니다 (예 : 전달 해야하는 인수).

# 1은 읽기, 유지 관리 및 성능 특성이 훨씬 간단합니다. 물론 구현을 공유 할 수있는 경우가 있습니다.

static void ValidateImage(const t_image& image);
void validateImages() const {
    ValidateImage(this->innerImage());
    ValidateImage(this->outerImage());
}

# 2가 코드베이스에서 좋은 일반적인 솔루션 (예 : 기본값)이라면 디자인 구조에 대해 걱정할 것입니다. 클래스가 너무 많이 수행하고 있습니까? 캡슐화가 충분하지 않거나 재사용이 충분하지 않습니까? 추상화 수준이 충분히 높습니까?

마지막으로, 돌연변이가 지원되는 OO 언어 (예 : const방법)를 사용하는 경우 # 2가 중복 된 것으로 보입니다 .

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