C #에서 유창하게 갈 때?


78

많은 측면에서 저는 Fluent 인터페이스의 아이디어를 정말 좋아하지만 C #의 모든 현대적인 기능 (초기화 기, 람다, 명명 된 매개 변수)을 사용하여 "그만한 가치가 있습니까?"라고 생각합니다. 사용하다?". 누구나 유창한 패턴을 사용할 때 자신의 경험이나 의사 결정 매트릭스를 받아 들일 수있는 연습이 아니라면 나에게 줄 수 있습니까?

결론:

지금까지 답변에서 얻은 좋은 경험 규칙 :

  • 유창한 인터페이스는 세터보다 더 많은 작업을 수행 할 때 크게 도움이됩니다. 호출은 컨텍스트 통과를 통해 더 많은 혜택을 볼 수 있기 때문입니다.
  • 유창한 인터페이스는 유일한 사용 수단이 아니라 API 위에있는 레이어로 생각해야합니다.
  • 람다, 이니셜 라이저 및 명명 된 매개 변수와 같은 현대적인 기능을 통해 손쉬운 인터페이스를 통해보다 친숙한 인터페이스를 만들 수 있습니다.

다음은 현대적인 기능이 덜 필요하다고 느끼는 의미의 예입니다. 예를 들어 직원을 만들 수있는 Fluent 인터페이스 (예 : 열악한 예)를 예로 들어 보겠습니다.

Employees.CreateNew().WithFirstName("Peter")
                     .WithLastName("Gibbons")
                     .WithManager()
                          .WithFirstName("Bill")
                          .WithLastName("Lumbergh")
                          .WithTitle("Manager")
                          .WithDepartment("Y2K");

다음과 같은 이니셜 라이저로 쉽게 작성할 수 있습니다.

Employees.Add(new Employee()
              {
                  FirstName = "Peter",
                  LastName = "Gibbons",
                  Manager = new Employee()
                            {
                                 FirstName = "Bill",
                                 LastName = "Lumbergh",
                                 Title = "Manager",
                                 Department = "Y2K"
                            }
              });

이 예제에서는 생성자에서 명명 된 매개 변수를 사용할 수도 있습니다.


1
좋은 질문이지만 더 많은 위키 질문 인 것 같습니다
Ivo

귀하의 질문은 "fluent-nhibernate"로 태그되었습니다. 그래서 유창한 인터페이스 를 만들지 , 유창한 nhibernate와 XML 구성 을 사용 할지 결정하려고 합니까?
Ilya Kogan

1
프로그래머로 이주하기로 결정 SE
Matt Ellen

@Ilya Kogan, 나는 그것이 유창한 인터페이스 패턴의 일반적인 태그 인 "fluent-interface"로 태그되었다고 생각한다. 이 질문은 nhibernate에 관한 것이 아니라 유창한 인터페이스를 만들지 여부 만 말했듯이. 감사.

1
이 게시물을 통해 C에서이 패턴을 사용하는 방법을 생각할 수있었습니다. 제 시도는 Code Review 자매 사이트 에서 찾을 수 있습니다 .
otto

답변:


28

(나는 한 유창 인터페이스 작성 다뤄 함께하는) 더 많은 노력이 필요하지만 당신이 바로 그것을 할 경우, 그 결과 사용자 코드의 의도가 더 분명하기 때문에 그것은 급료 지불을 가지고있다. 본질적으로 도메인 특정 언어의 형태입니다.

다시 말해, 코드가 작성된 것보다 더 많이 읽고 (그리고 어떤 코드가 아닌가?) 유창한 인터페이스를 만드는 것을 고려해야합니다.

유창한 인터페이스는 컨텍스트에 대한 것이 아니라 객체를 구성하는 단순한 방법 이상입니다. 위의 링크에서 볼 수 있듯이 유창한 API를 사용하여 다음을 달성했습니다.

  1. 컨텍스트 (일반적으로 동일한 작업으로 많은 작업을 순서대로 수행하는 경우 컨텍스트를 계속 선언하지 않고도 작업을 연결할 수 있습니다).
  2. 발견 가능성 ( objectA.인텔리스 센스 로 이동 하면 많은 힌트를 얻을 수 있습니다. 위의 경우 plm.Led.내장 LED 제어를위한 모든 옵션을 plm.Network.제공하며 네트워크 인터페이스로 수행 할 수있는 작업을 plm.Network.X10.제공합니다 . X10 장치에 대한 네트워크 작업 : 생성자 이니셜 라이저로이 작업을 수행 할 수는 없습니다 (관용적이지 않은 모든 다른 유형의 작업에 대해 객체를 생성해야하는 경우 제외).
  3. 리플렉션 (위의 예제에서는 사용되지 않음)-LINQ 표현식으로 전달하여 조작하는 기능은 특히 강력한 단위 도구로, 특히 단위 테스트 용으로 빌드 한 일부 도우미 API에서 매우 강력한 도구입니다. 속성 getter 식을 전달하고 유용한 식을 모두 작성하고 컴파일하고 실행하거나 속성 getter를 사용하여 컨텍스트를 설정할 수 있습니다.

내가 일반적으로하는 한 가지는 다음과 같습니다.

test.Property(t => t.SomeProperty)
    .InitializedTo(string.Empty)
    .CantBeNull() // tries to set to null and Asserts ArgumentNullException
    .YaddaYadda();

유창한 인터페이스없이 어떻게 그런 일을 할 수 있는지 보지 못합니다.

편집 2 : 다음과 같이 정말 가독성을 향상시킬 수 있습니다.

test.ListProperty(t => t.MyList)
    .ShouldHave(18).Items()
    .AndThenAfter(t => testAddingItemToList(t))
    .ShouldHave(19).Items();

답장을 보내 주셔서 감사하지만 Fluent를 사용해야하는 이유는 알고 있지만 위의 새 예제와 같이보다 구체적인 이유를 찾고 있습니다.
Andrew Hanlon

답장을 보내 주셔서 감사합니다. 나는 당신이 두 가지 좋은 경험 법칙을 설명했다고 생각한다. 2) 세터보다 많은 통화가있을 때 Fluent에 대해 생각하십시오.
Andrew Hanlon

2
@ach, 나는 "응답자보다 많은 통화"에 대한이 회신에 아무것도 보이지 않습니다. "코드가 쓰여진 것보다 훨씬 더 많이 읽혀진다"는 그의 말에 혼란 스러우십니까? 이는 속성 게터 / 세터가 아니라 코드를 읽는 사람과 코드를 쓰는 사람에 관한 것입니다. 우리는 일반적으로 코드를 수정하는 것보다 주어진 코드 줄을 훨씬 더 많이 읽기 때문에 사람 이 코드를 쉽게 읽을 수 있도록하는 것입니다.
Joe White

@Joe White, 아마도 'call'이라는 용어를 'action'으로 바꿔야 할 것입니다. 그러나 아이디어는 여전히 유효합니다. 감사.
Andrew Hanlon

테스트를위한 성찰은 사악합니다!
Adronius

24

Scott Hanselman은 Podcast Hanselminutes의 에피소드 260에서 Jonathan Carter와 함께 이에 대해 이야기 합니다. 유창한 인터페이스는 API의 UI와 비슷하다고 설명합니다. 유창한 인터페이스를 유일한 액세스 포인트로 제공하지 말고 "일반 API 인터페이스"위에 일종의 코드 UI로 제공해야합니다.

Jonathan Carter는 자신의 블로그에서 API 디자인 대해 약간 이야기합니다 .


정보 링크에 감사드립니다. API 상단의 UI는 그것을 보는 좋은 방법입니다.
Andrew Hanlon

14

유창한 인터페이스는 "올바른"추론을 할 때 코드 컨텍스트 내에서 제공하는 매우 강력한 기능입니다.

단순한 한 줄의 코드 체인을 일종의 의사 블랙 박스로 만드는 것이 목표라면 잘못된 트리를 짖는 것일 수 있습니다. 반면에 메소드 호출을 체인화하고 코드 가독성을 향상시키는 수단을 제공하여 API 인터페이스에 가치를 추가하는 데 사용하는 경우 많은 계획과 노력을 기울이면 그만한 가치가 있다고 생각합니다.

유창한 인터페이스를 만들 때 일반적인 "패턴"이되는 것처럼 보이는 것을 피하고 싶습니다. 여러분은 모든 유창한 메소드의 이름을 "with"로 지정합니다. 문맥에서 잠재적으로 좋은 API 인터페이스를 빼앗기 때문에 본질적인 가치 .

핵심은 유창한 구문을 도메인 특정 언어의 특정 구현으로 생각하는 것입니다. 제가 이야기하고있는 것에 대한 좋은 예로써, StoryQ를 살펴보십시오. StoryQ는 유창하고 DSL을 매우 가치 있고 유연한 방식으로 표현하는 수단으로 사용합니다.


답장을 보내 주셔서 감사합니다.
Andrew Hanlon

메소드의 'with'접두사를 신경 쓰지 않습니다. 체인을 위해 객체를 반환하지 않는 다른 메소드와 구별합니다. 예 : position.withX(5)position.getDistanceToOrigin()
LegendLength

5

초기 참고 사항 : 나는 질문에서 하나의 가정으로 문제를 겪고 있으며,이 게시물의 끝 부분에서 구체적인 결론을 도출합니다. 이것이 아마도 완전한 포괄 답변을 제공하지는 않기 때문에 이것을 CW로 표시하고 있습니다.

Employees.CreateNew().WithFirstName("Peter")…

다음과 같은 이니셜 라이저로 쉽게 작성할 수 있습니다.

Employees.Add(new Employee() { FirstName = "Peter",  });

내 눈 에이 두 버전은 다른 것을 의미하고 수행해야합니다.

  • 유창하지 않은 버전과 달리 유창한 버전은 새 버전 이 컬렉션 Employee에도 적용 된다는 사실을 숨 깁니다. 단지 새 객체가 d 임을 나타냅니다 .AddEmployeesCreate

  • 의 의미는 ….WithX(…)모호 특히이 F 번호에서 오는 사람들을위한 with객체 표현식 키워드 : 그들은 해석 할 수 있습니다 obj.WithX(x)A와 새로운 에서 객체가 파생되고 obj그와 동일한 obj의를 제외하고 X그 값 속성 x. 반면에 두 번째 버전에서는 파생 객체가 생성되지 않고 모든 객체가 원본 객체에 대해 지정되어 있음이 분명합니다.

….WithManager().With
  • 이것은 ….With…또 다른 의미가있다 : 속성 초기화의 "포커스"를 다른 객체로 전환. 유창한 API가 서로 다른 두 가지 의미를 가지고 있다는 사실 With은 무슨 일이 일어나고 있는지 정확하게 해석하기 어렵게 만드는 것입니다. 아마도 코드에서 의도 한 의미를 보여주기 위해 예제에서 들여 쓰기를 사용했을 것입니다. 다음과 같이 더 명확합니다.

    (employee).WithManager(Managers.CreateNew().WithFirstName("Bill").…)
    //                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    //                     value of the `Manager` property appears inside the parentheses,
    //                     like with `WithName`, where the `Name` is also inside the parentheses 

결론 :new T { X = x } 유창한 API ( Ts.CreateNew().WithX(x))를 사용 하여 간단하고 충분한 언어 기능을 "숨기는" 것이 가능하지만,

  1. 유창한 코드를 읽는 독자들은 여전히 ​​정확히 무엇을 이해하는지주의를 기울여야합니다. 즉, 유창한 API는 의미가 투명하고 모호하지 않아야합니다. 이러한 API를 설계하는 것은 예상보다 많은 작업 일 수 있으며 (사용 및 승인이 용이 한 테스트가 필요할 수 있음)

  2. 이 예제에서는 유창한 API가 기본 API (언어 기능)에 비해 "사용자 편의성"을 거의 제공하지 않습니다. 유창한 API는 기본 API / 언어 기능을 "더 사용하기 쉽게"만들어야한다고 말할 수 있습니다. 즉, 프로그래머가 상당한 노력을 절약해야합니다. 그것이 똑같은 것을 쓰는 또 다른 방법이라면 프로그래머의 삶을 더 쉽게 만들지는 않지만 디자이너의 작업을 더 어렵게하기 때문에 가치가 없을 것입니다 (위의 결론 # 1 참조).

  3. 위의 두 지점은 유창한 API가 기존 API 또는 언어 기능에 대한 계층이라고 자동 가정합니다. 유창한 API는 유일한 방법이 아니라 무언가를 수행하는 추가 방법이 될 수 있습니다. 즉, 유창한 API를 "선택"선택으로 제공하는 것이 좋습니다.


1
시간을내어 질문에 추가해 주셔서 감사합니다. 내가 선택한 모범이 잘못 생각되었다는 것을 인정할 것이다. 당시 필자는 개발중인 쿼리 API에 유체 인터페이스를 사용하려고했습니다. 나는 너무 단순화했다. 결함을 지적하고 좋은 결론을내어 주셔서 감사합니다.
Andrew Hanlon

2

나는 유창한 스타일을 좋아하며 의도를 매우 명확하게 표현합니다. 이후의 객체 이니셜 라이저 예제를 사용하면 해당 구문을 사용하려면 공용 속성 설정자가 있어야합니다. 유창한 스타일은 아닙니다. 예를 들어, 자바-set / get 스타일의 메소드를 거의 사용했기 때문에 공개 setter를 많이 얻지 못합니다.

두 번째 요점을 알려줍니다. 많은 속성 설정자와 함께 유창한 스타일을 사용하는지 잘 모르겠습니다. 아마도 두 번째 버전을 사용했을 것입니다. 함께 묶을 동사가 많거나 설정보다는 최소한 많은 일을합니다.


답장을 보내 주셔서 감사합니다. 훌륭한 경험 법을 표현했다고 생각합니다. 많은 세터를 통한 많은 통화에서 유창함이 더 좋습니다.
Andrew Hanlon

1

유창한 인터페이스 라는 용어에는 익숙하지 않았지만 LINQ를 포함하여 사용한 몇 가지 API를 상기시킵니다 .

개인적으로 C #의 최신 기능이 어떻게 이러한 접근 방식의 유용성을 방해하는지 알 수 없습니다. 차라리 그들이 손을 잡고 간다고 말하고 싶습니다. 예를 들어, 확장 메소드 를 사용하여 이러한 인터페이스를 달성하는 것이 훨씬 쉽습니다 .

아마도 당신이 언급 한 현대적인 기능 중 하나를 사용하여 유창한 인터페이스를 어떻게 대체 할 수 있는지에 대한 구체적인 예를 통해 답을 분명히하십시오.


1
답장을 보내 주셔서 감사합니다. 내 질문을 명확히하는 데 도움이되는 기본 예제를 추가했습니다.
Andrew Hanlon
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.