2009-04-23 3 views
12

나는 하나 또는 두 개의 메소드를 서로 후에 여러 번 호출해야하는 클래스가있다. 메서드는 현재 void을 반환합니다. 내가 생각했는데, 그 방법이 중첩 될 수 있도록 this을 반환하는 것이 더 좋을까요? 또는 그 아주 고려 아주 아주 나쁜가? 또는 나쁜 경우 동일한 유형의 새 객체가 반환되면 더 좋을까요? 아니면 어떻게 생각하니?C# : 메서드 중첩을 위해 'this'를 반환 하시겠습니까?

var a = new Adder(); 
    a.Add(4); 
    a.Remove(1); 
    a.Add(7); 
    a.Remove(3); 

다른 두이 방법을 사용할 수 있습니다 :

var a = new Adder() 
     .Add(4) 
     .Remove(1) 
     .Add(7) 
     .Remove(3); 
을 첫 번째는이 방법을 사용할 수 있습니다

// Regular 
class Adder 
{ 
    public Adder() { Number = 0; } 

    public int Number { get; private set; } 

    public void Add(int i) { Number += i; } 
    public void Remove(int i) { Number -= i; } 
} 

// Returning this 
class Adder 
{ 
    public Adder() { Number = 0; } 

    public int Number { get; private set; } 

    public Adder Add(int i) { Number += i; return this; } 
    public Adder Remove(int i) { Number -= i; return this; } 
} 

// Returning new 
class Adder 
{ 
    public Adder() : this(0) { } 
    private Adder(int i) { Number = i; } 

    public int Number { get; private set; } 

    public Adder Add(int i) { return new Adder(Number + i); } 
    public Adder Remove(int i) { return new Adder(Number - i); } 
} 

: 예를 들어 나는 가산기 클래스의 세 가지 버전을 만들었습니다

유일한 차이점은 첫 번째 경우의 anew Adder() 인 반면 후자의 경우 마지막 방법의 결과입니다.

내가 처음 발견 한 것은 빠르게 반복적으로 반복해서 쓰는 것이 었습니다. 그래서 다른 버전 중 하나를 사용하고 싶습니다.

세 번째는 많은 String 메서드 및 IEnumerable 확장 메서드와 같은 다른 많은 메서드와 비슷합니다. 나는 그것이 당신이 var a = new Adder(); var b = a.Add(5);과 같은 일을 할 수 있다는 점에서 긍정적 인면을 가지고 있다고 생각합니다. 그런 다음 0과 하나는 5였습니다. 그러나 동시에, 새로운 객체를 생성하는 것이 조금 비싸지 않습니까? 그리고 첫 번째 물체는 언제 죽을까요? 첫 번째 메서드가 일종의 반환합니다 때? 또는?

어쨌든, 나는 this을 반환하고 그것을 사용할 것이라고 생각하지만, 다른 사람들이이 사건에 대해 어떻게 생각하는지 알고 싶어합니다. 가장 좋은 방법은 무엇입니까?

+1

당신은 당신이 말하는대로하지 않습니다. 당신은 Number에서 더하거나 뺄 것이고 그것을 새로운 Adder (...)에 별도의 정수로 전달할 것입니다. – Samuel

+0

오, 잘 잡으세요! 덕분에) – Svish

답변

17

'돌아 가기'스타일은 종종 fluent interface라고하며 일반적으로 사용됩니다.

+0

아하. 시원한. 그러면 나는 그 다음에 갈 것이라고 생각합니다. =) – Svish

+2

유창한 스타일은 매우 다른 대안과 비슷하게 씁니다. 이는 내부 상태를 수정하는 대신 개체를 불변으로 만들고 개체의 새 인스턴스를 반환하는 것입니다. 예제에있는 것과 같은 간단한 클래스를 사용하면 (예제가 매우 현실적이지는 않음) 단순히 대신 불변으로 만드는 장점과 단점을 고려할 수 있습니다. – mquander

+2

jQuery'$ ('eleld'). show()'는'this'를 반환하는데, 그렇지 않으면 eleId의 복사본을 만들 것이기 ​​때문입니다. Linq'myCollection.Where (x => x.IsValid)'는 새로운 객체를 반환하는데, 그렇지 않으면 myCollection이 변경 될 수 있기 때문입니다. – Keith

0

다음을 고려하십시오. 5 년 후이 코드로 돌아 가면이 코드가 의미가 있습니까? 그렇다면, 앞으로 나아갈 수 있다고 생각합니다.

이 특정 예에서는 연산자를 과부하로 설정하면 작업이 명확 해지고 동일한 작업이 수행됩니다.

+0

글쎄, 당연히 : p이 경우 최고의 int 종류를 직접 사용했을 것입니다. 하지만 중첩 된 메서드의 간단한 예제로 생각해 낼 수있었습니다. 내 초점은 그들이 실제로 한 일이 아니라 호출하는 방법이었습니다. p – Svish

+0

5 년 후에 돌아올 때를 제외하고. 메소드 체인은 연산자가 클래스 자체를 볼 필요가있는 동안 마우스를 한 번 움직여서 즉시 나타납니다. – Samuel

+0

아, 좋은 지적이다. – Svish

6

"유창한 구문"이 마음에 들었고 두 번째 구문을 사용했습니다. 어쨌든 유창한 구문에 불편을 느끼는 사람들을 위해 첫 번째로 사용할 수 있습니다.

다른 생각

는 가산기 같은 인터페이스를 쉽게 하나가 사용할 수 있도록합니다 :

public Adder Add(params int[] i) { /* ... */ } 
public Adder Remove(params int[] i) { /* ... */ } 

Adder adder = new Adder() 
    .Add(1, 2, 3) 
    .Remove(3, 4); 

난 항상 짧고 읽기 쉬운 인터페이스를 만들기 위해 노력하지만처럼 복잡 많은 사람들이 코드를 작성 좋아 가능한.

+0

params 사용법에 대한 멋진 아이디어. 나 자신을 사용할 수도 있습니다 .... – Svish

0
  1. 구체적인 경우 산술 연산자에 과부하가 가장 좋은 해결책 일 수 있습니다.

  2. 반환하기 this (Fluent 인터페이스)은 표현을 만드는 일반적인 방법입니다. 단위 테스트 및 조롱 프레임 워크에서는이를 많이 사용합니다. Fluent Hibernate는 또 다른 예입니다.

  3. 새 인스턴스를 반환하는 것이 좋습니다. 클래스를 변경하지 못하게 할 수 있습니다 - 일반적으로 좋은 일이고 멀티 스레딩의 경우에는 매우 편리합니다. 그러나 불변성이 당신에게 유용하지 않다면 객체 생성 오버 헤드를 생각해보십시오.

+0

멋지게 요약 =) – Svish

0

당신이 그것을 Adder라고 부르는 경우, 나는 이것을 반환 할 것입니다. 그러나 Adder 클래스가 대답을 포함하는 것은 이상한 일입니다.

MyNumber와 같은 것으로 만들고 Add() - 메서드를 만드는 것이 좋습니다.

가 이상적으로 (IMHO), 즉 인스턴스 내부에 저장되어있는 번호를 변경하지 않을,하지만 당신은 반환하는 새 값으로 새 인스턴스를 만들 :

class MyNumber 
{ 
    ... 

    MyNumber Add(int i) 
    { 
     return new MyNumber(this.Value + i); 
    } 
} 
+0

물론. 그리고 Remove라는 메소드를 포함하는 낯선 사람도 실제로는 Subtract라고해야합니다. 그러나 그 점은 여기에 있지 않습니다 : p – Svish

0

내가 생각하는 간단한 인터페이스 즉, "유창한"인터페이스는 특히 매우 구현하기 쉽기 때문에 매우 유용합니다. 유창한 인터페이스의 가치는 그것이 이해의 길로가는 불필요한 잔털을 제거한다는 것입니다. 이러한 인터페이스를 개발하는 데는 많은 시간이 걸릴 수 있습니다. 특히 인터페이스가 시작될 때 더욱 그렇습니다. 인터페이스의 사용법이 "읽는"방법에 대해 걱정해야합니다. 내 마음 속에서, 그러한 인터페이스를위한 가장 강력한 사용은 그것이 저장하는 문자의 양이 아니라 프로그래머의 의도를 전달하는 방법입니다.

귀하의 특정 질문에 답하기 위해 "나는이 스타일"을 좋아합니다. 유창한 인터페이스의 전형적인 사용법은 일련의 옵션을 정의하는 것입니다. 즉, 클래스의 인스턴스를 만든 다음 인스턴스의 유창한 메서드를 사용하여 개체의 원하는 동작을 정의합니다. 예/아니오 옵션 (예 : 로깅)이있는 경우 "setLogging (bool state)"메서드가 아니라 "WithLogging"및 "WithoutLogging"메서드를 사용합니다. 이것은 다소 많은 작업이지만 최종 결과의 명확성은 매우 유용합니다.

2

체인은 가지고있는 좋은 물건이며 일부 프레임 워크에서는 핵심입니다 (예 : Linq 확장과 jQuery는 모두이를 크게 사용합니다). 새 객체를 생성 여부

또는 return this 당신이 당신의 초기 목적은 행동을 기대하는 방법에 따라 달라집니다 : jQuery를에 체인으로 연결
var a = new Adder(); 

var b = a.Add(4) 
     .Remove(1) 
     .Add(7) 
     .Remove(3); 

//now - should a==b ? 

는 원래의 목적을 변경 한 것 - 그것은이를 반환했습니다. 예상되는 동작입니다. 그렇지 않으면 기본적으로 UI 요소가 복제됩니다.

Linq에서 연결하면 원래 컬렉션이 변경되지 않습니다. 그것도 예상되는 동작입니다. 각 연결된 함수는 필터 또는 변형이며 원래 컬렉션은 종종 불변입니다.

어떤 패턴이 어떤 작업에 더 적합합니까?

+0

Linq와 jQuery 뒤에있는 사고에 대한 좋은 관찰 = "a a == b"에 관해서는,이 바보 같은 경우에 그들은 평등 한 것으로 간주되어야합니다. 같은 번호. 그러나 방법 2 또는 3을 사용했는지에 따라 참조가 실제로 같을 필요는 없습니다. – Svish

+0

작업을 마친 후 매번'new'를 사용하면 a.Number == 1, b.Number == 7. 나중에'this'를 사용하면 a.Number == 7이됩니다. – Keith

0

두 번째 및 세 번째 해결 방법의 주요 차이점은이 대신 새 인스턴스를 반환하면 특정 상태의 개체를 "catch"하고 계속할 수 있다는 것입니다.

var a = new Adder() .Add (4);

var b = a.Remove (1);

var c = a.Add (7) .제거 (3);

이 경우 b와 c는 모두 a로 캡처 된 상태를 시작점으로합니다. Growing Object-Oriented Software, Guided by Tests by Steve Freeman; Nat Pryce에 테스트 도메인 객체를 작성하기위한 패턴을 읽는 동안이 관용구를 발견했습니다.

인스턴스 수명과 관련하여 다음과 같이하십시오. 제거 또는 추가 호출이 반환되자 마자 가비지 수집이 가능할 것으로 예상됩니다.