도우미 스타일의 "유틸리티 백"정적 클래스를 피하면서 "자유 ​​기능"을 깨끗하게 처리하는 C # 패턴


9

나는 최근에 내가 작업하는 큰 C # 코드베이스 주위에 떠있는 몇 가지 도우미 스타일의 "유틸리티 백"정적 클래스를 검토하고 있었다.

// Helpers.cs

public static class Helpers
{
    public static void DoSomething() {}
    public static void DoSomethingElse() {}
}

내가 검토 한 특정 방법은

  • 대부분 서로 관련이없고
  • 호출에서 명시적인 상태가 지속되지 않으면
  • 작고
  • 각각 관련없는 다양한 유형에 의해 소비됩니다.

편집 : 위의 문제는 의심되는 문제의 목록이 아닙니다. 내가 검토하고있는 특정 방법의 공통 특성 목록입니다. 답변이보다 관련성이 높은 솔루션을 제공하도록 돕는 것이 맥락입니다.

이 질문에 대해서는이 종류의 방법을 GLUM (일반 경량 유틸리티 방법)이라고합니다. "glum"의 부정적인 의미는 부분적으로 의도 된 것입니다. 이것이 바보 같은 말장난으로 생긴다면 미안합니다.

GLUM에 대한 내 자신의 기본 회의론을 제외하더라도, 나는 이것에 대해 다음과 같은 것을 좋아하지 않습니다.

  • 정적 클래스는 네임 스페이스로만 사용됩니다.
  • 정적 클래스 식별자는 기본적으로 의미가 없습니다.
  • 새로운 GLUM이 추가 될 때, (a)이 "가방"클래스는 정당한 이유없이 만지거나 (b) 새로운 "가방"클래스가 생성됩니다 (그 자체로는 일반적으로 문제가되지 않습니다. 나쁜 점은 새로운 것입니다) 정적 클래스는 종종 관련성 문제를 반복하지만 더 적은 방법으로).
  • 메타 이름은 여부, 표준이 아닌, 불가피하게 끔찍한, 일반적으로 내부적으로 일관성 Helpers, Utilities또는 무엇 이건.

이것을 리팩토링하기위한 합리적으로 좋고 간단한 패턴은 무엇입니까?

아마 강조해야 할 것입니다 : 내가 다루는 모든 방법은 서로 관련이 없습니다. 그것들을 더 세밀하지만 여전히 다중 멤버 정적 클래스 메소드 백으로 나누는 합리적인 방법은없는 것 같습니다.


1
GLUMs ™ 정보 : 모범 사례를 따르면 방법이 가벼워 야하므로 "경량"의 약어를 제거 할 수 있다고 생각합니다.;)
Fabio

@Fabio 동의합니다. 나는이 용어를이 질문의 글쓰기의 약어로 사용하는 (아마도 재미없고 혼란스럽지 않은) 농담으로 포함시켰다. 문제의 코드베이스가 오래 지속되고 레거시이며 약간 지저분하기 때문에 "가벼운"이 여기에 적절하다고 생각합니다. 만약 현재 예산이 더 많으면 더 적극적으로 접근하여 후자를 처리 할 것입니다.
William

답변:


17

서로 밀접하게 관련된 두 가지 문제가 있다고 생각합니다.

  • 정적 클래스는 논리적으로 관련없는 함수를 포함합니다
  • 정적 클래스의 이름은 포함 된 함수의 의미 / 컨텍스트에 대한 유용한 정보를 제공하지 않습니다.

이러한 문제는 논리적으로 관련된 메서드 만 하나의 정적 클래스로 결합하고 다른 정적 클래스로 이동하여 해결할 수 있습니다. 문제 영역을 기반으로 사용자와 팀은 메소드를 관련 정적 클래스로 분리하는 데 사용할 기준을 결정해야합니다.

우려와 가능한 해결책

방법은 대부분 서로 관련이 없습니다 - 문제. 하나의 정적 클래스에서 관련 메소드를 결합하고 관련이없는 메소드를 자체 정적 클래스로 이동하십시오.

명시 적 상태가 호출에 걸쳐 지속하지 않고 방법은 - 아니 문제. 상태 비 저장 방법은 버그가 아닌 기능입니다. 정적 상태는 어쨌든 테스트하기 어렵고 메소드 호출에서 상태를 유지해야하는 경우에만 사용해야하지만 상태 머신 및 yield키워드 와 같은 더 나은 방법이 있습니다 .

방법 작은 - 문제가되지 않습니다. -방법 작아야합니다.

방법은 각 관련이없는 종류의 다양한 소비하는 - 문제가되지 않습니다. 관련이없는 많은 장소에서 재사용 할 수있는 일반적인 방법을 만들었습니다.

정적 클래스는 전적으로 네임 스페이스로 사용하고 있습니다 - 문제가되지 않습니다. 정적 메소드가 사용되는 방식입니다. 이 스타일의 호출 메소드가 귀찮은 경우 확장 메소드 또는 using static선언을 사용하십시오.

정적 클래스 식별자는 기본적으로 의미가 - 문제 . 개발자가 관련없는 방법을 사용하기 때문에 의미가 없습니다. 관련이없는 메소드를 자체 정적 클래스에 넣고 구체적이고 이해하기 쉬운 이름을 지정하십시오.

새로운 무뚝뚝한가 추가됩니다 때, 중 (a)이 "가방"클래스 터치됩니다 (SRP 슬픔) - 문제가되지 않습니다 만 논리적으로 관련이 방법은 하나 개의 정적 클래스에 있어야한다는 생각을 따르는 경우. SRP클래스 나 메소드에 원칙을 적용해야하므로 여기서 위반하지 않습니다. 정적 클래스의 단일 책임은 하나의 일반 "아이디어"에 대해 다른 메소드를 포함하는 것을 의미합니다. 이 아이디어는 기능 또는 변환, 반복 (LINQ), 데이터 정규화 또는 유효성 검사일 수 있습니다.

또는 자주 (b)는 새로운 "가방"클래스 (봉지 이상)가 생성됩니다 - 없는 문제. 클래스 / 정적 클래스 / 함수-소프트웨어 설계에 사용하는 개발자 도구입니다. 팀에 수업 사용에 대한 제한이 있습니까? "더 많은 가방"의 최대 한도는 얼마입니까? 프로그래밍은 컨텍스트에 관한 것입니다. 특정 컨텍스트의 솔루션을 위해 논리적으로 계층 구조가있는 네임 스페이스 / 폴더에 논리적으로 명명 된 200 개의 정적 클래스가 이해되면 문제가되지 않습니다.

메타 이름은 도우미, 유틸리티, 또는 무엇이든, 불가피하게, 끔찍한 표준이 아닌, 보통 내부적으로 일관성이 - 문제. 예상되는 동작을 설명하는 더 나은 이름을 제공하십시오. 더 나은 이름 지정에 도움이되는 관련 클래스 만 한 클래스에 유지하십시오.


SRP 위반은 아니지만 공개 / 폐쇄 원칙 위반입니다. 그건 내가 일반적으로 OCP는 그것의 가치보다 더 문제가 될 발견했습니다 말했다

2
정적 클래스에는 @Paul, Open / Close 원칙을 적용 할 수 없습니다. 그것은 SRP 또는 OCP 원칙을 정적 클래스에 적용 할 수 없다는 점입니다. 정적 메소드에 적용 할 수 있습니다.
Fabio

좋아, 여기에 약간의 혼란이 있었다. 질문의 첫 번째 항목 목록은 문제가 의심되는 목록 이 아닙니다 . 내가 검토하고있는 특정 방법의 공통 특성 목록입니다. 답변이보다 적합한 솔루션을 제공하도록 돕는 것이 맥락입니다. 명확히하기 위해 편집 할 것입니다.
William

1
"정적 클래스로서 네임 스페이스"로, 이것이 일반적인 패턴이라는 것은 그것이 안티 패턴이 아니라는 것을 의미하지는 않습니다. 이 특정 응집력이 낮은 정적 클래스 내에서 메소드 (기본적으로 C / C ++에서 용어를 빌리는 "무료 함수")는이 경우 의미있는 엔티티입니다. 이 특정 상황 에서 단일 파일에 100 개의 메서드 A가있는 정적 클래스보다 100 개의 단일 메서드 정적 클래스 (100 개 파일) 가있는 하위 네임 스페이스가있는 것이 구조적으로 더 나은 것 같습니다 A.
William

1
나는 당신의 대답에 대해 상당히 큰 정리, 주로 미용을했습니다. 마지막 단락이 무엇인지 잘 모르겠습니다. Math정적 클래스 를 호출하는 코드를 테스트하는 방법에 대해 아무도 걱정하지 않습니다 .
Robert Harvey

5

C #에서 이와 같은 상황에서 정적 클래스가 네임 스페이스와 같이 사용된다는 규칙을 채택하십시오. 네임 스페이스는 좋은 이름을 얻습니다. using staticC # 6기능 은 삶을 조금 더 쉽게 만듭니다.

스 멜리 한 클래스 이름 Helpers은 가능하면 메소드를 더 나은 이름의 정적 클래스로 분리해야한다는 신호를 보내지 만, 강조된 가정 중 하나는 다루는 메소드가 "페어와 관련이 없다"는 것입니다. 기존 메소드 당 하나의 새로운 정적 클래스. 아마도 괜찮다면

  • 새로운 정적 클래스 에는 좋은 이름 이 있습니다.
  • 실제로 프로젝트 유지 관리에 도움이된다고 판단합니다.

고안된 예로서, Helpers.LoadImage다음과 같이 될 수 있습니다 FileSystemInteractions.LoadImage.

여전히 정적 클래스 메서드 백으로 끝날 수 있습니다. 이것이 일어날 수있는 몇 가지 방법 :

  • 아마도 원래의 메소드 중 일부만이 새로운 클래스의 이름을 가질 수 있습니다.
  • 아마도 새로운 클래스는 나중에 다른 관련 메소드를 포함하도록 진화했을 것입니다.
  • 어쩌면 원래 클래스 이름은 냄새가 나지 않았으며 실제로는 괜찮 았습니다.

이 정적 클래스 메소드 백은 실제 C # 코드베이스에서 그리 드물지 않다는 것을 기억하는 것이 중요합니다. 몇 가지 작은 것들을 갖는 것은 병리학 적이 아닙니다 .


각각의 "자유로운 기능"과 같은 메소드를 자체 파일에 실제로 장점이있는 경우 (실제로 프로젝트의 유지 관리에 도움이된다고 정직하게 판단하면 괜찮고 가치가 있음) 정적 클래스 대신 정적 클래스를 만드는 것이 좋습니다 네임 스페이스와 유사한 정적 클래스 이름을 사용한 다음이를 통해 소비하는 부분 클래스 using static. 예를 들면 다음과 같습니다.

이 패턴을 이용한 더미 프로젝트의 구조

Program.cs

namespace ConsoleApp1
{
    using static Foo;
    using static Flob;

    class Program
    {
        static void Main(string[] args)
        {
            Bar();
            Baz();
            Wibble();
            Wobble();
        }
    }
}

푸 /Bar.cs

namespace ConsoleApp1
{
    static partial class Foo
    {
        public static void Bar() { }
    }
}

푸 /Baz.cs

namespace ConsoleApp1
{
    static partial class Foo
    {
        public static void Baz() { }
    }
}

Flob / Wibble.cs

namespace ConsoleApp1
{
    static partial class Flob
    {
        public static void Wibble() { }
    }
}

Flob / Wobble.cs

namespace ConsoleApp1
{
    static partial class Flob
    {
        public static void Wobble() { }
    }
}

-6

일반적으로 "일반적인 유틸리티 방법"이 필요하지 않습니다. 비즈니스 로직에서. 다시 한 번 살펴보고 적절한 장소에 보관하십시오.

수학 라이브러리 또는 무언가를 작성하는 경우 일반적으로 확장 방법을 싫어하지만 제안 할 것입니다.

확장 메소드를 사용하여 표준 데이터 유형에 함수를 추가 할 수 있습니다.

예를 들어 Helpers.MySort 대신 MySort () 일반 함수가 있거나 새 ListOfMyThings.MySort를 추가하면 IEnumerable에 추가 할 수 있습니다


요점은 또 다른 "딱딱한 모습"을 취하지 않는 것이다. 요점은 유틸리티의 "백"을 쉽게 정리할 수있는 재사용 가능한 패턴을 갖는 것 입니다. 유틸리티가 냄새라는 것을 알고 있습니다. 질문은 이것을 나타냅니다. 현명이 독립적 인 (하지만 너무 밀접하게 함께 배치) 도메인 개체를 분리하는 목표는 지금 과 프로젝트 예산에 쉽게 가능한 이동합니다.
William

1
당신이 경우 하지 가 "일반 유틸리티 메소드를,"당신은 아마 당신의 논리만큼의 나머지 부분에서 논리의 조각을 분리 아닙니다. 예를 들어, 내가 아는 한 .NET에는 범용 URL 경로 결합 기능이 없으므로 종종 하나를 작성하게됩니다. 이러한 종류의 균열은 표준 라이브러리의 일부로 더 나을 것이지만 모든 표준 라이브러리에는 무언가 가 없습니다 . 대안은, 다른 코드 내에서 직접 반복 논리를 떠나 그것을 만드는 훨씬 덜 읽을 수.
jpmc26


1
기능이 아닌 @Ewan, 그것은 범용 대의원 서명입니다 ...
TheCatWhisperer

1
@Ewan 그것은 TheCatWhisperer의 요점이었습니다. 유틸리티 클래스는 클래스에 메소드를 넣는 것에 대한 혼란입니다. 당신이 동의해서 다행입니다.
jpmc26
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.