클래스의 메소드는 언제 자신을 수정 한 후 동일한 인스턴스를 리턴해야합니까?


9

나는 세 가지 방법을 가지고있는 클래스가 A(), B()하고 C(). 이러한 메소드는 자체 인스턴스를 수정합니다.

방법은 인스턴스는 별도의 사본 (다만 때 인스턴스를 반환해야하지만 Clone()) 나는 반환하는 자유로운 선택 가지고 void또는 같은 인스턴스 ( return this;) 메소드에서 동일한 인스턴스를 수정하고 다른 값을 반환하지 않습니다.

동일한 수정 된 인스턴스를 반환하기로 결정할 때와 같은 깔끔한 메소드 체인을 수행 할 수 있습니다 obj.A().B().C();.

이것이 유일한 이유일까요?

자체 인스턴스를 수정하고 반환해도 괜찮습니까? 아니면 이전과 같이 사본 만 반환하고 원본 개체를 그대로 두어야합니까? 동일한 수정 된 인스턴스를 반환 할 때 사용자는 반환 된 값이 복사본이라고 가정 할 수 있습니다. 그렇지 않으면 반환되지 않습니다. 괜찮다면, 방법에 대해 그러한 것들을 명확히하는 가장 좋은 방법은 무엇입니까?

답변:


7

체인 사용시기

함수 체인은 자동 완성 기능이있는 IDE가 일반적인 언어에서 주로 사용됩니다. 예를 들어, 거의 모든 C # 개발자는 Visual Studio를 사용합니다. 따라서 C #으로 개발하는 경우 Visual Studio가 체인 구축을 지원하므로 메서드에 체인을 추가하면 해당 클래스의 사용자가 시간을 절약 할 수 있습니다.

다른 한편으로, 본질적으로 매우 역동적이고 IDE에서 자동 완성 기능을 지원하지 않는 PHP와 같은 언어는 체인을 지원하는 클래스가 적습니다. 연결은 올바른 phpDoc을 사용하여 연결 가능한 메소드를 노출 할 때만 적절합니다.

체인이란 무엇입니까?

Foo다음과 같은 클래스가 주어지면 두 가지 방법 모두 체인 가능합니다.

function what() { return this; }
function when() { return new Foo(this); }

하나는 현재 인스턴스에 대한 참조이고 새 인스턴스를 작성한다고해서 체인 가능한 메소드라는 것을 변경하지는 않습니다.

연결 가능한 메서드가 현재 개체 만 참조해야한다는 금 규칙은 없습니다. 실제로, 체인 가능한 메소드는 서로 다른 두 클래스에 걸쳐있을 수 있습니다. 예를 들어;

class B { function When() { return true; } };
class A { function What() { return new B(); } };

var a = new A();
var x = a.What().When();

this위의 예에서는 언급 이 없습니다 . 이 코드 a.What().When()는 체인의 예입니다. 흥미로운 점은 클래스 유형 B이 변수에 할당되지 않는다는 것입니다.

메소드는 리턴 값이 표현식의 다음 컴포넌트로 사용될 때 연결됩니다.

몇 가지 예가 더 있습니다.

 // return value never assigned.
 myFile.Open("something.txt").Write("stuff").Close();

// two chains used in expression
int x = a.X().Y() * b.X().Y();

// a chain that creates new strings
string name = str.Substring(1,10).Trim().ToUpperCase();

사용시기 thisnew(this)

대부분의 언어에서 문자열은 변경할 수 없습니다. 따라서 체인 메서드 호출은 항상 새로운 문자열을 생성합니다. StringBuilder와 같은 객체를 수정할 수있는 위치

일관성은 모범 사례입니다.

객체의 상태를 수정하고 this를 반환하는 메서드가있는 경우 새 인스턴스를 반환하는 메서드를 혼용하지 마십시오. 대신 Clone()명시 적으로 수행 할 특정 메소드를 작성하십시오 .

 var x  = a.Foo().Boo().Clone().Foo();

그것은 내부에서 무슨 일이 일어나고 있는지에 대해 훨씬 명확 a합니다.

외부와 속임수

나는 이것을 체인 아웃과 관련된 많은 일반적인 문제를 해결하기 때문에 스텝 아웃 및 백 트릭 이라고 부릅니다 . 기본적으로 원래 클래스에서 새 임시 클래스로 나간 다음 원래 클래스로 돌아갑니다.

임시 클래스는 원래 클래스에 특수 기능을 제공하기 위해 존재하지만 특수한 조건에서만 존재합니다.

체인이 상태 를 변경해야 할 때가 종종 있지만, 클래스 A가 가능한 모든 상태를 나타내는 것은 아닙니다 . 따라서 체인 중에에 대한 참조가 포함 된 새 클래스가 도입되었습니다 A. 이를 통해 프로그래머는 상태 로 들어가서로 돌아갈 수 A있습니다.

다음은 내 예 B입니다. 특수 상태를로 지정하십시오 .

class A {
    function Foo() { return this; }
    function Boo() { return this; }
    function Change() return new B(this); }
}

class B {
    var _a;
    function (A) { _a = A; }
    function What() { return this; }
    function When() { return this; }
    function End() { return _a; }
}

var a = new A();
a.Foo().Change().What().When().End().Boo();

이것은 매우 간단한 예입니다. 더 많은 통제력을 원한다면 다른 방법 B을 가진 새로운 슈퍼 유형으로 돌아갈 수 A있습니다.


4
Intellisense와 같은 자동 완성 지원에만 의존하여 메소드 체인을 권장하는 이유를 정말로 이해하지 못합니다. 메소드 체인 또는 유창한 인터페이스는 모든 OOP 언어에 대한 API 디자인 패턴입니다. 자동 완성 기능은 오타 및 유형 오류를 방지하는 것입니다.
amon

@amon 당신은 내가 말한 것을 잘못 읽었습니다. 나는 지능이 언어에 공통적 일 때 더 인기가 있다고 말했다. 나는 그것이 기능에 달려 있다고 말한 적이 없다.
Reactgular

3
이 답변은 체인의 가능성을 이해하는 데 도움이되지만 체인을 사용할 때의 결정에 여전히 부족합니다. 물론 일관성이 최고 입니다. 그러나 함수에서 객체를 수정하는 경우를 참조하고 return this있습니다. 내 라이브러리 사용자가이 상황을 처리하고 이해하도록 어떻게 도울 수 있습니까? (필요하지 않은 경우에도 체인 방식을 허용하면 실제로 괜찮을까요? 아니면 한 방향으로 만 변경해야합니까? 전혀 변경이 필요하지 않습니까?)
Martin Braun

@modiX 답변이 정확하면 답변으로 수락하십시오. 당신이 찾고있는 다른 물건은 체인 사용 방법에 대한 의견이며, 그에 대한 옳고 그른 대답은 없습니다. 그것은 정말로 당신에게 달려 있습니다. 작은 세부 사항에 대해 너무 걱정하고 있습니까?
Reactgular

3

이러한 메소드는 자체 인스턴스를 수정합니다.

반환한다는 방법을 가지고, 언어에 따라 void/ unit수정 자신의 인스턴스 또는 매개 변수가 비 관용적이다. 더 많은 언어를 사용하는 언어 (C #, C ++)에서도 더 기능적인 스타일의 프로그래밍 (불변의 객체, 순수한 함수)으로 전환하면서 유행을 벗어났습니다. 그러나 지금 당장 좋은 이유가 있다고 가정 해 봅시다.

이것이 유일한 이유일까요?

특정 동작 (think x++)의 경우 변수를 수정하더라도 작업이 결과를 반환 할 것으로 예상됩니다. 그러나 그것은 당신 스스로 그것을하는 유일한 이유입니다.

자체 인스턴스를 수정하고 반환해도 괜찮습니까? 아니면 이전과 같이 사본 만 반환하고 원본 개체를 그대로 두어야합니까? 동일한 수정 된 인스턴스를 반환 할 때 사용자는 반환 된 값이 복사본임을 인정할 수 있습니다. 그렇지 않으면 반환되지 않습니다. 괜찮다면, 방법에 대해 그러한 것들을 명확히하는 가장 좋은 방법은 무엇입니까?

때에 따라 다르지.

복사 / 신규 및 리턴이 공통 인 언어 (C # LINQ 및 문자열)에서 동일한 참조를 리턴하면 혼동 될 수 있습니다. 수정 및 반환이 일반적인 언어 (일부 C ++ 라이브러리)에서는 복사가 혼란 스러울 수 있습니다.

void속성과 같은 언어 구문 을 반환 하거나 사용하여 서명을 모호하지 않게 만드는 것이 가장 분명한 방법입니다. 그 후, SetFoo기존 인스턴스를 수정하고 있음을 나타 내기 위해 이름을 명확하게 만드십시오. 그러나 핵심은 작업중인 언어 / 라이브러리의 관용구를 유지하는 것입니다.


당신의 대답을위한 Thx. 나는 주로 .NET 프레임 워크로 작업하고 있으며 귀하의 답변에 따르면 비 관념적 ​​인 것들로 가득합니다. LINQ 메서드와 같은 작업은 작업 등을 추가 한 후 원본의 새 복사본을 반환하지만 컬렉션 유형 Clear()이나 Add()컬렉션 유형과 같은 항목은 동일한 인스턴스를 수정하고 반환 void합니다. 두 경우 모두 당신은 혼란 스러울 것이라고 말합니다 ...
Martin Braun

@modix-LINQ는 작업을 추가하는 동안 복사본을 반환 하지 않습니다 .
Telastyn

왜 내가 할 수 obj = myCollection.Where(x => x.MyProperty > 0).OrderBy(x => x.MyProperty);있습니까? Where()새로운 컬렉션 객체를 반환합니다. OrderBy()내용이 정렬되어 있기 때문에 다른 컬렉션 객체를 반환합니다.
Martin Braun

1
@modix - 그들은 구현하는 새로운 개체를 반환 IEnumerable하지만, 명확하게하기 위해, 그들은 복사하지 않습니다, 그리고 그들은 (제외 컬렉션없는 ToList, ToArray등).
Telastyn

이것은 옳습니다. 그것들은 사본이 아니며, 더 나아가 새로운 객체입니다. 또한 컬렉션 I을 말하면에서 상속하는 클래스가 아닌 계산할 수있는 객체 (모든 종류의 배열에 둘 이상의 객체를 보유하는 객체)를 참조하고 있습니다 ICollection. 내 잘못이야.
Martin Braun

0

(저는 프로그래밍 언어로 C ++을 가정했습니다)

나에게 이것은 대부분 가독성 측면이다. A, B, C가 수정자인 경우, 특히 임시 객체 인 경우 일부 함수에 매개 변수로 전달 된 경우 (예 :

do_something(
      CPerson('name').age(24).height(160).weight(70)
      );

에 비해

{
   CPerson person('name');
   person.set_age(24);
   person.set_height(160);
   person.set_weight(70);
   do_something(person);
}

수정 된 인스턴스에 대한 참조를 반환해도 괜찮은 경우 예라고 말하고 예를 들어 스트림 연산자 '>>'및 '<<'( http://www.cprogramming.com/tutorial/ operator_overloading.html )


1
당신은 틀렸다고 가정했는데 C #입니다. 그러나 나는 내 질문이 더 일반적이기를 원합니다.
Martin Braun

물론 Java 일 수도 있었지만 연산자를 사용하여 내 예제를 컨텍스트에 배치하는 것은 사소한 요점이었습니다. 본질은 독자의 기대와 배경에 영향을받는 가독성으로 요약되며, 다른 답변들도 지적했듯이 특정 언어 / 프레임 워크의 일반적인 관행에 영향을받습니다.
AdrianI

0

리턴 값을 수정하는 대신 복사 리턴으로 메소드 체인을 수행 할 수도 있습니다.

좋은 C # 예제는 string.Replace (a, b)입니다.이 문자열은 호출 된 문자열을 변경하지 않지만 대신 새 문자열을 반환하여 즐겁게 체인 할 수 있습니다.


예, 알아요 그러나 어쨌든 자신의 인스턴스를 편집하고 싶지 않을 때이 사례를 사용해야하기 때문에이 사례를 언급하고 싶지 않았습니다. 그러나 지적 해 주셔서 감사합니다.
Martin Braun

다른 답변에 따라 의미의 일관성이 중요합니다. 복사 반환과 일치 할 수 있고 수정 반환과 일치 할 수 없으므로 (불변의 객체로는 수행 할 수 없기 때문에) 귀하의 질문에 대한 답변은 복사 반환을 사용하지 않는다고 말합니다.
Peter Wone
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.