2009-11-13 5 views

답변

29

의견에서 "포함 된 아이디어가"상속을 완전히 대체하기에 충분했는지 궁금해했습니다. 나는 그 질문에 대한 대답이 "예"라고 말할 것입니다. 몇 년 전 나는 Snit이라는 Tcl OO 시스템을 사용하여 상속을 제외하고 구성 및 위임을 사용하여 매우 간단하게 작업했습니다.Snit은 Go의 접근 방식과 크게 다르지만, 한 가지 측면에서 공통적 인 철학적 근거가 있습니다. 이것은 클래스의 계층 구조가 아닌 기능과 책임을 결합하는 메커니즘입니다.

다른 사람들도 언급했듯이 실제로 언어 설계자가 지원하고자하는 프로그래밍 실습의 종류에 관한 것입니다. 그러한 모든 선택은 그들 자신의 장단점이있다. 나는 "모범 사례"가 반드시 여기에 적용되는 문구라고 생각하지 않습니다. 아마도 Go의 상속 계층을 개발할 것으로 보입니다.

(Tcl에 익숙한 독자를 위해, 나는 Snit이 [incr Tcl]보다 언어의 "느낌"과 약간 더 가까운 일치라고 느꼈다. Tcl은 적어도 생각의 방식에있어 위임에 관한 것이다.)

36

Gang of 4의 중요한 원칙은 "상속하는 구성을 선호한다"입니다; 을 작성하면을 따르게됩니다 ;-). 상속

+4

상속은 지나치게 많이 사용됩니다. 구성,하지만 내가 정말로 알고 싶은 질문은 임베딩이 상속을 완전히 대체 할 수 있는지 여부입니다. 실제로이 코드를 쓰거나 쓰지 않고 대답하기 어려운 질문 일 것입니다. – Casebash

+0

글쎄, 템플릿 방법과 같은 디자인 상 중요한 힌트가있는 키 패턴을 직접 (직접적으로) 얻지는 못했지만, 그것은 살인자처럼 보이지 않을 것입니다. - 최악의 경우, 약간의 편의성 (약간 더 명확한 코딩이 필요함)의 손실을 수반하는 것으로 보입니다. –

+0

@Casebash : 사람들은 우리가 말할 수있는 JS 프로토 타입을 사용할 수있었습니다. –

12

유일한 용도는 다음과 같습니다

  • 다형성

    • 이동의 인터페이스의 "정적 오리 입력"시스템은이 문제를 다른 클래스

      에서
  • 차입 구현을 해결

    • 이 삽입을위한 것입니다

정확히 1 대 1지도를하지 않는 이동의 접근 방식은, 자바 상속과 다형성의 고전적인 예 (based on this) 고려 :

//roughly in Java (omitting lots of irrelevant details) 
//WARNING: don't use at all, not even as a test 

abstract class BankAccount 
{ 
    int balance; //in cents 
    void Deposit(int money) 
    { 
     balance += money; 
    } 

    void withdraw(int money) 
    { 
     if(money > maxAllowedWithdrawl()) 
      throw new NotEnoughMoneyException(); 
     balance -= money; 
    } 

    abstract int maxAllowedWithdrawl(); 
} 

class Account extends BankAccount 
{ 
    int maxAllowedWithdrawl() 
    { 
     return balance; 
    } 
} 

class OverdraftAccount extends BankAccount 
{ 
    int overdraft; //amount of negative money allowed 

    int maxAllowedWithdrawl() 
    { 
     return balance + overdraft; 
    } 
} 

상속 및 다형성이 결합되어 있으며 기본 구조를 변경하지 않고이를 Go로 변환 할 수 없습니다.

내가 이동에 깊이 탐구하지 않은,하지만 나는 그것을 이런 식으로 뭔가 보일 것이다 가정 : 메모 사항에 따라

//roughly Go? .... no? 
//for illustrative purposes only; not likely to compile 
// 
//WARNING: This is totally wrong; it's programming Java in Go 

type Account interface { 
    AddToBalance(int) 
    MaxWithdraw() int 
} 

func Deposit(account Account, amount int) { 
    account.AddToBalance(amount) 
} 

func Withdraw(account Account, amount int) error { 
    if account.MaxWithdraw() < amount { 
     return errors.New("Overdraft!") 
    } 
    account.AddToBalance(-amount) 
    return nil 
} 

type BankAccount { 
    balance int 
} 

func (account *BankAccount) AddToBalance(amount int) { 
    account.balance += amount; 
} 

type RegularAccount { 
    *BankAccount 
} 

func (account *RegularAccount) MaxWithdraw() int { 
    return account.balance //assuming it's allowed 
} 

type OverdraftAccount { 
    *BankAccount 
    overdraft int 
} 

func (account *OverdraftAccount) MaxWithdraw() int { 
    return account.balance + account.overdraft 
} 

이 완전히 이동 자바를하고있는 한 이후 코드에 잘못된 방법입니다 . Go에 이런 것을 쓰려면 아마도 이것보다 훨씬 많이 구성되어있을 것입니다.

+0

당신은 이것을 컴파일하지는 않을 것이지만 이것을 읽는 다른 사람들을 돕기 위해 몇 가지 포인트를 언급했다 : 타입은 Go에서 타입 리터럴을 필요로한다. 'type RegularAccount {} '대신'type RegularAccount struct {}'를 사용하십시오. func 프로토 타입을 타입 정의에 넣을 수 없습니다. 'func (this * receiverType) funcName (parms) returnType' 값을 반환하는 func에 대해 반환 유형을 제공해야합니다. 'func (account * RegularAccount) maxWithdraw() int {}' 마지막으로 Go에서 "func"라인을 여는 중괄호로 끝내야합니다. – burfl

+0

나는 운동으로 이것을 쓰려고 시도했다. 아주 초기에 Go에서 나를 위해 노력했다. 나는 더 많은 경험이있는 사람이 소리내어 고쳐주고 그것을 완성 할 수 있다면 거의 효과가있다. https://gist.github.com/mindplay-dk/807179beda57e676b8fb –

3

나는 지금 막에 대해 배우고 있지만, 당신이 의견을 묻기 때문에, 나는 지금까지 내가 아는 것에 근거하여 제안 할 것이다. Embedding은 기존 언어에서 이미 수행되고있는 우수 사례에 대한 명시 적 언어 지원 인 Go의 다른 많은 기능과 유사합니다. 예를 들어 알렉스 마르 텔리 (Alex Martelli)가 지적했듯이, 4 명의 갱은 "상속에 대한 구성을 선호한다"고 말합니다. Go는 상속을 제거 할뿐만 아니라 C++/Java/C#보다 컴포지션을 더 쉽고 강력하게 만듭니다.

"Go는 내가 언어 X로 할 수없는 새로운 것을 제공하지 않습니다."그리고 "왜 다른 언어가 필요한가요?"와 같은 코멘트에 의아해했습니다. 어떤 의미에서 Go는 이전에 수행 할 수 없었던 새로운 것을 제공하지는 않지만 다른 의미에서 새로운 것은 Go가 제공하는 최상의 기술의 사용을 촉진하고 장려한다는 것입니다. 이미 실제로 다른 언어를 사용하고 있습니다.

+3

몇 가지면에서 Go의 새로운 점은 제거 된 것입니다. 이것이 새로운 언어의 주요 이유입니다. 함수를 추가하는 것만이라면 C+++ 일 수도 있었지만 상속, 포인터 산술, 수동 메모리 할당과 같은 기능을 제거하려면 새로운 언어가 필요합니다. –

3

사람들은 Go에 퍼가기에 대한 정보에 대한 링크를 요청했습니다.

여기에는 삽입이 논의되고 구체적인 예가 제공되는 "유효 이동"문서가 있습니다. 이미 이동 인터페이스 및 유형의 좋은 이해가있을 때

http://golang.org/doc/effective_go.html#embedding

이 예제는 더 의미가 있습니다,하지만 당신은 방법의 설정과 경우 이름과 같은 인터페이스를 생각하여 그것을 가짜 당신은 생각할 수 구조체와 비슷한 구조체.

구조체에 대한 자세한 내용은

, 당신은 명시 적으로 포함 된 유형으로 구조체의 이름이없는 회원을 언급 이동 언어 사양을 볼 수 있습니다

http://golang.org/ref/spec#Struct_types

지금까지 난 단지 편리한 방법으로 사용했습니다 필드 이름이 소스 코드에 값을 추가하지 않을 때 내부 구조체의 필드 이름을 사용할 필요없이 하나의 구조체를 다른 구조체에 두는 것. 아래의 프로그래밍 연습에서는 제안서와 응답 채널이있는 유형 안에 제안 유형을 묶습니다.

https://github.com/ecashin/go-getting/blob/master/bpaxos.go#L30

7

는 임베딩 자동 위임을 제공합니다. 이 자체로는 상속을 대체하기에 충분하지 않습니다. 포함은 다형성의 형태를 제공하지 않기 때문입니다. Go 인터페이스는 다형성을 제공하지만 사용하는 인터페이스와 약간 다릅니다 (일부 사람들은 오리 타이핑 또는 구조 입력과 유사 함).

변경 사항이 광범위하므로 수행하기가 어려워서 상속 계층 구조를 신중하게 설계해야합니다. Go는 이러한 대안을 피하면서 강력한 대안을 제공합니다. 나는 그것을 좋아하지 http://nathany.com/good

3

을 :

여기에 조금 더 이동으로 OOP에 탐구 기사입니다.

사용하는 언어가 생각 패턴에 영향을줍니다. (C 프로그래머에게 "단어 수"를 구현하도록 요청하면 링크 된 목록을 사용하고 성능을 위해 이진 트리로 전환합니다.) 그러나 모든 Java/Ruby/Python 프로그래머는 Dictionary/Hash를 사용합니다. 두뇌는 다른 데이터 구조를 사용할 수 없다고 생각합니다.)

상속을 사용하면 추상적 인 것으로 시작하여 구체적인 것으로 서브 클래 싱해야합니다. 실제 유용한 코드는 N 레벨의 깊이에 묻힐 것입니다. 이렇게하면 부모 클래스에서 드래그하지 않고 코드를 다시 사용할 수 없으므로 객체의 "부분"을 사용하기가 어렵습니다.

Go에서는 인터페이스를 사용하여 클래스를 '모델링'할 수 있습니다. 그러나 당신은 이렇게 코딩 할 수 없습니다.

대신에 퍼가기를 사용할 수 있습니다. 코드는 각각 자체 데이터가있는 작고 분리 된 모듈로 나눌 수 있습니다. 이것은 재사용을 쉽게 만듭니다. 이 모듈성은 "큰"객체와 관련이 거의 없습니다. (즉 In Go에서는 Duck 클래스에 대해 알지 못하는 "quack()"메서드를 작성할 수 있지만 일반적인 OOP 언어에서는 "내 Duck.quack() 구현은 Duck의 다른 방법 ")

Go에서는 프로그래머가 모듈성에 대해 계속 생각하게 만듭니다. 이로 인해 커플 링이 낮은 프로그램이 생깁니다. 커플 링이 낮 으면 유지 보수가 훨씬 쉬워집니다. ("오, 저기, Duck.quack()은 정말 길고 복잡하지만, 적어도 오리의 나머지 부분에 의존하지 않는다는 것을 알고 있습니다.")