[c#] API 디자인에서“너무 많은 매개 변수”문제를 피하는 방법은 무엇입니까?

이 API 기능이 있습니다.

public ResultEnum DoSomeAction(string a, string b, DateTime c, OtherEnum d, 
     string e, string f, out Guid code)

나는 그것을 좋아하지 않는다. 매개 변수 순서가 불필요하게 중요해지기 때문입니다. 새 필드를 추가하기가 더 어려워집니다. 전달되는 내용을 확인하기가 더 어렵습니다. 하위 함수의 모든 매개 변수를 전달하는 또 다른 오버 헤드를 생성하기 때문에 메소드를 더 작은 부분으로 리팩토링하기가 더 어렵습니다. 코드를 읽기가 더 어렵습니다.

가장 명확한 아이디어를 생각해 냈습니다. 데이터를 캡슐화하는 객체를 가지고 각 매개 변수를 하나씩 전달하는 대신 전달하십시오. 다음은 내가 생각해 낸 것입니다.

public class DoSomeActionParameters
{
    public string A;
    public string B;
    public DateTime C;
    public OtherEnum D;
    public string E;
    public string F;        
}

그것은 내 API 선언을 다음과 같이 줄였습니다.

public ResultEnum DoSomeAction(DoSomeActionParameters parameters, out Guid code)

좋은. 무고 해 보이지만 실제로는 큰 변화가있었습니다. 이전에 수행 한 작업은 실제로 익명의 불변 개체를 스택에 전달하는 것이 었습니다. 이제 우리는 매우 변경 가능한 새로운 클래스를 만들었습니다. 우리는 호출자 의 상태를 조작하는 기능을 만들었습니다 . 짜증나 이제 객체를 변경할 수 없게하려면 어떻게해야합니까?

public class DoSomeActionParameters
{
    public string A { get; private set; }
    public string B { get; private set; }
    public DateTime C { get; private set; }
    public OtherEnum D { get; private set; }
    public string E { get; private set; }
    public string F { get; private set; }        

    public DoSomeActionParameters(string a, string b, DateTime c, OtherEnum d, 
     string e, string f)
    {
        this.A = a;
        this.B = b;
        // ... tears erased the text here
    }
}

보시다시피 실제로 원래 문제인 너무 많은 매개 변수를 다시 생성했습니다. 그것이가는 길이 아니라는 것은 명백합니다. 내가 무엇을 할까? 이러한 불변성을 달성하기위한 마지막 옵션은 다음과 같이 “읽기 전용”구조체를 사용하는 것입니다.

public struct DoSomeActionParameters
{
    public readonly string A;
    public readonly string B;
    public readonly DateTime C;
    public readonly OtherEnum D;
    public readonly string E;
    public readonly string F;        
}

이를 통해 매개 변수가 너무 많은 생성자를 피하고 불변성을 달성 할 수 있습니다. 실제로 모든 문제를 해결합니다 (매개 변수 순서 등). 아직:

그때 혼란스러워서이 질문을하기로 결정했을 때 : C #에서 변이성을 도입하지 않고 “너무 많은 매개 변수”문제를 피하는 가장 간단한 방법은 무엇입니까? 그 목적으로 읽기 전용 구조체를 사용할 수 있지만 API 디자인이 좋지 않습니까?

설명 :

  • 단일 책임 원칙을 위반하지 않는다고 가정하십시오. 원래의 경우 함수는 주어진 매개 변수를 단일 DB 레코드에 씁니다.
  • 주어진 기능에 대한 특정 솔루션을 찾고 있지 않습니다. 이러한 문제에 대한 일반적인 접근 방식을 찾고 있습니다. 나는 가변성이나 끔찍한 디자인을 도입하지 않고 “너무 많은 매개 변수”문제를 해결하는 데 특히 관심이 있습니다.

최신 정보

여기에 제공된 답변은 서로 다른 장점 / 단점이 있습니다. 따라서 이것을 커뮤니티 위키로 변환하고 싶습니다. 코드 샘플과 찬반 양론으로 각 대답은 앞으로 비슷한 문제에 대한 좋은 가이드를 만들 것이라고 생각합니다. 나는 지금 그것을하는 방법을 찾으려고 노력하고있다.



답변

프레임 워크에 포함 된 한 스타일은 일반적으로 관련 매개 변수를 관련 클래스로 그룹화하는 것과 같습니다 (그러나 다시 가변성에 문제가 있음).

var request = new HttpWebRequest(a, b);
var service = new RestService(request, c, d, e);
var client = new RestClient(service, f, g);
var resource = client.RequestRestResource(); // O params after 3 objects


답변

빌더 및 도메인 별 언어 스타일 API–Fluent Interface의 조합을 사용하십시오. API는 조금 더 장황하지만 지능이 뛰어나 타이핑이 빠르고 이해하기 쉽습니다.

public class Param
{
        public string A { get; private set; }
        public string B { get; private set; }
        public string C { get; private set; }


  public class Builder
  {
        private string a;
        private string b;
        private string c;

        public Builder WithA(string value)
        {
              a = value;
              return this;
        }

        public Builder WithB(string value)
        {
              b = value;
              return this;
        }

        public Builder WithC(string value)
        {
              c = value;
              return this;
        }

        public Param Build()
        {
              return new Param { A = a, B = b, C = c };
        }
  }


  DoSomeAction(new Param.Builder()
        .WithA("a")
        .WithB("b")
        .WithC("c")
        .Build());


답변

당신이 가지고있는 것은 문제의 클래스 가 너무 많은 의존성을 가지고 있기 때문에 단일 책임 원칙을 위반하고 있음을 분명히 나타냅니다 . 이러한 종속성을 Facade Dependency의 클러스터로 리팩토링하는 방법을 찾으십시오 .


답변

매개 변수 데이터 구조를 a에서 a class로 변경하기 만하면 struct됩니다.

public struct DoSomeActionParameters 
{
   public string A;
   public string B;
   public DateTime C;
   public OtherEnum D;
   public string E;
   public string F;
}

public ResultEnum DoSomeAction(DoSomeActionParameters parameters, out Guid code) 

메소드는 이제 자체 구조 사본을 얻습니다. 인수 변수에 대한 변경 사항은 메소드에 의해 관찰 될 수 없으며, 변수에 대한 메소드 변경 사항은 호출자가 관찰 할 수 없습니다. 불변성없이 격리가 달성됩니다.

장점 :

  • 가장 쉬운 구현
  • 기초 역학에서의 행동의 최소 변화

단점 :

  • 불변성은 분명하지 않으며 개발자의주의가 필요합니다.
  • 불변성을 유지하기 위해 불필요한 복사
  • 점유 공간

답변

데이터 클래스 내에 빌더 클래스를 작성하는 것은 어떻습니까. 데이터 클래스에는 모든 세터가 개인용으로 있으며 빌더 만 설정할 수 있습니다.

public class DoSomeActionParameters
    {
        public string A { get; private set; }
        public string B  { get; private set; }
        public DateTime C { get; private set; }
        public OtherEnum D  { get; private set; }
        public string E  { get; private set; }
        public string F  { get; private set; }

        public class Builder
        {
            DoSomeActionParameters obj = new DoSomeActionParameters();

            public string A
            {
                set { obj.A = value; }
            }
            public string B
            {
                set { obj.B = value; }
            }
            public DateTime C
            {
                set { obj.C = value; }
            }
            public OtherEnum D
            {
                set { obj.D = value; }
            }
            public string E
            {
                set { obj.E = value; }
            }
            public string F
            {
                set { obj.F = value; }
            }

            public DoSomeActionParameters Build()
            {
                return obj;
            }
        }
    }

    public class Example
    {

        private void DoSth()
        {
            var data = new DoSomeActionParameters.Builder()
            {
                A = "",
                B = "",
                C = DateTime.Now,
                D = testc,
                E = "",
                F = ""
            }.Build();
        }
    }


답변

나는 C # 프로그래머는 아니지만 C #이 명명 된 인수를 지원한다고 생각합니다. (F #은 C #은 주로 이런 종류의 기능에 적합합니다)
http://msdn.microsoft.com/en-us/library/dd264739 .aspx # Y342

따라서 원래 코드를 호출하면 다음과 같습니다.

public ResultEnum DoSomeAction(
 e:"bar",
 a: "foo",
 c: today(),
 b:"sad",
 d: Red,
 f:"penguins")

이것은 더 이상 공간을 필요로하지 않으며 객체가 생성되고 모든 혜택을 누릴 수 있습니다. 왜냐하면 당신은 비 계측 시스템에서 일어나는 일을 전혀 변경하지 않았다는 사실입니다. 인수의 이름을 나타 내기 위해 아무것도 코딩 할 필요조차 없습니다.

편집 : 여기에 내가 찾은 예술이 있습니다.
http://www.globalnerdy.com/2009/03/12/default-and-named-parameters-in-c-40-sith-lord-in-training/
C # 4.0에서 명명 된 인수를 지원한다고 언급해야합니다. 3.0은 그렇지 않습니다.


답변

왜 불변성을 강제하는 인터페이스를 만드는가 (즉, 게터 만)?

본질적으로 첫 번째 솔루션이지만 인터페이스를 사용하여 매개 변수에 액세스하도록 함수를 강제합니다.

public interface IDoSomeActionParameters
{
    string A { get; }
    string B { get; }
    DateTime C { get; }
    OtherEnum D { get; }
    string E { get; }
    string F { get; }
}

public class DoSomeActionParameters: IDoSomeActionParameters
{
    public string A { get; set; }
    public string B { get; set; }
    public DateTime C { get; set; }
    public OtherEnum D { get; set; }
    public string E { get; set; }
    public string F { get; set; }
}

함수 선언은 다음과 같습니다.

public ResultEnum DoSomeAction(IDoSomeActionParameters parameters, out Guid code)

장점 :

  • struct솔루션 과 같은 스택 공간 문제가 없습니다.
  • 언어 시맨틱을 사용한 자연스러운 솔루션
  • 불변성은 명백하다
  • 융통성 (소비자가 원하면 다른 클래스를 사용할 수 있음)

단점 :

  • 일부 반복적 인 작업 (두 개의 다른 엔티티에서 동일한 선언)
  • 개발자는 DoSomeActionParameters그것이 매핑 될 수있는 클래스라고 추측해야 합니다.IDoSomeActionParameters