c# - List<T>로부터 상속받지 않는 이유는 무엇입니까?



.net oop (18)

내 프로그램을 계획 할 때 종종 다음과 같은 사슬로 시작합니다.

축구 팀은 축구 선수 목록에 불과합니다. 그러므로 나는 다음과 같이 표현해야한다.

var football_team = new List<FootballPlayer>();

이 목록의 순서는 플레이어가 명부에 올라있는 순서를 나타냅니다.

그러나 나중에 팀은 기록되어야하는 단순한 선수 명단 외에 다른 속성을 가지고 있음을 깨닫습니다. 예를 들어, 이번 시즌의 총 득점, 현재 예산, 유니폼 색상, 팀 이름을 나타내는 string 등.

그래서 나는 생각한다.

좋아요, 풋볼 팀은 선수 목록과 비슷하지만, 이름 ( string )과 누적 합계 ( int )가 있습니다. .NET은 축구 팀 저장을위한 클래스를 제공하지 않으므로 내 자신의 클래스를 만들 것입니다. 가장 유사하고 관련성이 높은 기존 구조는 List<FootballPlayer> 이므로이를 상속받습니다.

class FootballTeam : List<FootballPlayer> 
{ 
    public string TeamName; 
    public int RunningTotal 
}

그러나 List<T> 에서 상속해서는 안된다는 지침 이 있음이 밝혀졌습니다. 나는 두 가지면에서이 가이드 라인에 철저히 혼란 스럽다.

왜 안돼?

외관상으로는 List 는 어떻게해서든지 성과를 위해 낙관된다 . 어떻게 그렇게? List 확장하면 어떤 성능 문제가 발생합니까? 무엇이 깰 것인가?

필자가 보았던 또 다른 이유는 List 가 Microsoft에 의해 제공되고,이를 제어 할 수 없기 때문에 "공개 API"를 공개 한 후에 나중에 변경할 수 없다는 것 입니다. 그러나 나는 이것을 이해하기 위해 애 쓰고있다. 공개 API 란 무엇이며 왜 신경 써야합니까? 현재 프로젝트에 공개 API가 없거나없는 경우이 가이드 라인을 무시해도됩니까? List 에서 상속을 받고 공개 API가 필요하다면 어떤 어려움이 있습니까?

그것은 왜 중요합니까? 목록은 목록입니다. 무엇이 바뀔 수 있습니까? 무엇을 바꾸고 싶습니까?

그리고 마지막으로 Microsoft가 List 에서 상속 받기를 원하지 않는다면 왜 클래스를 sealed 하지 않았습니까?

내가 뭘 더 사용해야하니?

분명히 사용자 지정 컬렉션의 경우 Microsoft는 List 대신 확장해야하는 Collection 클래스를 제공합니다. 그러나이 클래스는 매우 베어 AddRange 와 같은 많은 유용한 것들을 가지고 있지 않습니다. jvitor83의 대답 은 특정 메소드에 대한 성능 근거를 제공하지만, 느린 AddRange 아닌 것보다 나은 점은 무엇입니까?

Collection 에서 상속하는 것은 List 에서 상속하는 것보다 더 많은 작업이며 아무런 이점도 없습니다. 확실히 Microsoft는 아무런 이유없이 추가 작업을 수행하라고 말하지 않을 것입니다. 그래서 나는 어떻게 든 오해하고있는 것처럼 느껴질 수밖에 없으며 Collection 상속하는 것은 실제로 내 문제에 대한 올바른 해결책이 아닙니다.

IList 구현과 같은 제안을 보았습니다. 그냥 싫다. 이것은 아무것도 얻지 못하는 상용구 코드 수십 라인입니다.

마지막으로, 어떤 것은 List 를 wrapping하는 것을 제안한다.

class FootballTeam 
{ 
    public List<FootballPlayer> Players; 
}

이 문제에는 두 가지 문제가 있습니다.

  1. 그것은 내 코드를 불필요하게 장황하게 만든다. 이제 my_team.Players.Count 대신 my_team.Players.Count 를 호출해야합니다. 고맙게도 C #을 사용하면 색인 생성기를 정의하여 색인 생성을 투명하게 만들 수 있으며 내부 List 의 모든 메서드를 전달할 수 있습니다. 나는 그 모든 일을 위해 무엇을 얻습니까?

  2. 그것은 평범하지 않습니다. 축구 팀은 선수 명단을 갖고 있지 않습니다. 그것은 선수 목록입니다. 당신은 "John McFootballer가 SomeTeam의 선수들과 합류했다고"말하지 않습니다. 당신은 "존이 SomeTeam에 가입했다"고 말합니다. "문자열의 문자"에 문자를 추가하지 않으면 문자열에 문자를 추가합니다. 도서관의 서적에 책을 추가하지 않고 서적을 도서관에 추가합니다.

"내부에서 일어나는 일"이 "Y를 내부 목록에 추가"한다고 말할 수 있지만 이것은 세계에 대해 매우 반 직관적 인 사고 방식처럼 보입니다.

내 질문 (요약)

"논리적으로"(즉, "인간의 마음에") 몇 가지 종소리와 호루라기가있는 list things 뿐인 데이터 구조를 나타내는 올바른 C # 방식은 무엇입니까?

List<T> 로부터 상속받을 수 없는가? 언제 받아 들일 수 있습니까? 왜 안돼? List<T> 로부터 상속받을 것인지 아닌지를 결정할 때 프로그래머는 무엇을 고려해야 하는가?

https://src-bin.com


Answer #1

마지막으로, 어떤 것은 List를 wrapping하는 것을 제안한다.

그것이 올바른 방법입니다. "불필요하게 말씨"는 이것을 보는 나쁜 방법입니다. my_team.Players.Count 를 작성할 때 명시 적으로 의미가 있습니다. 당신은 선수들을 세고 싶습니다.

my_team.Count

..아무 의미 없다. 뭐라구?

팀은 단순한 선수 목록 이상으로 구성된 목록이 아닙니다. 팀이 플레이어를 소유하므로 플레이어가 멤버가되어야합니다 (멤버).

지나치게 자세한 내용 걱정된다면 항상 팀의 속성을 노출 할 수 있습니다.

public int PlayerCount {
    get {
        return Players.Count;
    }
}

..된다 :

my_team.PlayerCount

이것은 지금 의미가 있으며 Demeter의 법칙을 준수합니다.

복합 재사용 원칙을 고수하는 것도 고려해야합니다. List<T> 를 상속하면 팀 플레이어리스트이며 불필요한 메소드를 노출한다고 말할 수 있습니다. 이것은 틀린 말입니다 - 명시된 바와 같이 팀은 이름, 관리자, 임원, 강사, 의료진, 급여 한도 등을 가진 선수 목록 이상입니다. 팀 수업에 선수 목록이 포함되어 있으면 "팀 에는 선수 명단 있다"고 말하고 있지만 다른 것들도 가질 수있다.


Answer #2

데이터 구조를 나타내는 올바른 C # 방법은 무엇입니까 ...

기억하세요, "모든 모델이 잘못되었지만 일부 모델은 유용합니다." - 조지 EP 박스

"올바른 방법"은 없으며 유용한 방법입니다.

귀하 및 / 또는 귀하의 사용자에게 유용한 것을 선택하십시오. 그게 전부 야. 경제적으로 개발하고, 과도하게 기술하지 마십시오. 작성하는 코드가 적을수록 디버깅 할 코드가 적어집니다. (다음 판을 읽으십시오).

- 편집 됨

내 최선의 대답은 ... 달라질 수 있습니다. 목록을 상속하면이 클래스의 클라이언트가 노출되지 않아야하는 메서드에 노출됩니다. 주로 FootballTeam이 비즈니스 엔터티처럼 보이기 때문입니다.

- 제 2 판

나는 진실하게 내가 "over-engineer"코멘트에 관해 언급하고 있었던 것을 기억하고 있지 않다. KISS 사고 방식이 좋은 가이드 라고 생각하지만 목록에서 비즈니스 클래스를 이어 받으면 추상화 누출 로 인해 해결되는 것보다 많은 문제가 발생한다는 점을 강조하고 싶습니다 .

반면에 List에서 상속받을 수있는 경우는 제한적이라고 생각합니다. 내가 이전 판에서 쓴 것처럼, 그것은 달려있다. 각 사례에 대한 답변은 지식, 경험 및 개인 취향 모두에 크게 영향을받습니다.

대답에 대해 더 정확하게 생각하도록 도와 주신 @kai에게 감사드립니다.


Answer #3

인터페이스보다 클래스 선호

클래스는 클래스로부터 파생되는 것을 피하고 필요한 최소한의 인터페이스를 구현해야합니다.

상속으로 캡슐화가 끊어짐

클래스에서 파생 되면 캡슐화가 중단됩니다 .

  • 컬렉션 구현 방법에 대한 내부 세부 정보를 제공합니다.
  • 적절하지 않을 수도있는 인터페이스 (공용 함수 및 속성 집합)를 선언합니다.

무엇보다도 이것은 당신의 코드를 리팩토링하는 것을 더 어렵게 만듭니다.

클래스는 구현 세부 사항입니다.

클래스는 코드의 다른 부분에서 숨겨져 야하는 구현 세부 사항입니다. 즉 System.List, 현재와 미래에는 적절할 수도 있고 그렇지 않을 수도있는 추상 데이터 유형의 특정 구현입니다.

개념적으로 System.List데이터 유형을 "목록"이라고 부르는 것은 약간의 붉은 청어이다. A System.List<T>는 요소 추가, 삽입 W 제거에 대한 상각 된 O (1) 조작을 지원하고 요소 수를 검색하거나 색인으로 요소를 가져오고 설정하는 O (1) 조작을 지원하는 변경 가능한 순서 콜렉션입니다.

인터페이스가 작을수록 더 유연합니다.

데이터 구조를 설계 할 때 인터페이스가 간단할수록 코드가 유연 해집니다. LINQ가 얼마나 강력한 지에 대한 데모를 살펴보십시오.

인터페이스 선택 방법

"목록"을 생각할 때 자신에게 "야구 선수 컬렉션을 대표해야합니다"라고 말함으로써 시작해야합니다. 따라서 이것을 클래스로 모델링하기로 결정했다고 가정 해 봅시다. 먼저해야 할 일은이 클래스가 노출해야하는 최소한의 인터페이스 양을 결정하는 것입니다.

이 프로세스를 안내하는 데 도움이되는 몇 가지 질문은 다음과 같습니다.

  • 내가 계산해야하나요? 구현하지 않는 경우IEnumerable<T>
  • 이 컬렉션은 초기화 된 후에 변경 될 예정입니까? 고려하지 않으면 IReadonlyList<T>.
  • 색인으로 항목에 액세스 할 수 있습니까? 중히 여기다ICollection<T>
  • 컬렉션에 항목을 추가하는 순서가 중요합니까? 어쩌면 ISet<T>?
  • 당신이 정말로 이러한 일을 원한다면 앞으로 나아가서 구현하십시오 IList<T>.

이렇게하면 코드의 다른 부분을 야구 선수 컬렉션의 구현 세부 정보에 연결하지 않고 인터페이스를 존중하는 한 구현 방법을 자유롭게 변경할 수 있습니다.

이 접근 방식을 사용하면 코드를 읽고, 리팩터링하고, 재사용하기가 더 쉬워집니다.

상용어 방지에 대한 참고 사항

현대 IDE에서 인터페이스를 구현하는 것은 쉽습니다. 마우스 오른쪽 버튼을 클릭하고 "인터페이스 구현"을 선택하십시오. 필요한 경우 모든 구현을 멤버 클래스로 전달합니다.

즉, 많은 상용구를 작성하는 경우 잠재적으로 더 많은 기능을 노출해야하기 때문입니다. 클래스에서 상속하지 않아야하는 것과 같은 이유입니다.

응용 프로그램에 적합한 더 작은 인터페이스를 설계 할 수 있으며, 필요할 때 다른 인터페이스로 매핑 할 수있는 몇 가지 도우미 확장 기능을 설계 할 수도 있습니다. 이것은 LinqArray 라이브러리에IArray 대한 자신의 인터페이스 에서 취한 접근법 입니다.


Answer #4

디자인> 구현

노출하는 메소드와 속성은 디자인 결정입니다. 상속받은 기본 클래스는 구현 세부 사항입니다. 나는 그것이 이전 단계로 돌아갈만한 가치가 있다고 생각합니다.

객체는 데이터 및 동작의 모음입니다.

따라서 첫 번째 질문은 다음과 같아야합니다.

  • 이 객체는 내가 만드는 모델에서 어떤 데이터를 구성합니까?
  • 이 개체는 해당 모델에서 어떤 동작을 나타 냅니까?
  • 앞으로 어떻게 변화할까요?

상속은 "isa"(a) 관계를 의미하는 반면 컴포지션은 "has a"(hasa) 관계를 의미 함을 명심하십시오. 애플리케이션의 진화에 따라 상황이 달라질 수 있으므로 상황에 맞는 적절한 것을 선택하십시오.

특정 유형으로 생각하기 전에 인터페이스를 생각해보십시오. 일부 사람들은 두뇌를 "디자인 모드"에 쉽게 적용 할 수 있다고 생각합니다.

이것은 매일 모든 사람이이 수준에서 의식적으로 코딩하는 것이 아닙니다. 그러나 당신이 이런 종류의 화제를 검토하고 있다면, 당신은 설계 수역을 걷고 있습니다. 그것을 깨닫는 것은 해방 될 수 있습니다.

디자인 세부 사항 고려

MSDN 또는 Visual Studio의 List <T> 및 IList <T>를 살펴보십시오. 그들이 노출하는 메소드와 속성을 확인하십시오. 이 방법들은 모두 누군가가보기에 FootballTeam에하기를 원하는 것처럼 보입니까?

footballTeam.Reverse ()는 당신에게 의미가 있습니까? footballTeam.ConvertAll <TOutput> ()이 원하는 것처럼 보이십니까?

이것은 속임수가 아닙니다. 대답은 진실로 "예"일 것입니다. List <Player> 또는 IList <Player>를 구현하거나 상속받는 경우, 그 플레이어들과 만난다. 모델에 이상적이라면 그렇게하십시오.

예라고 결정하면 이해가됩니다. 개체를 플레이어 (동작)의 컬렉션 / 목록으로 처리 할 수있게하려면 ICollection 또는 IList를 구현하고 싶습니다. 개념적으로 :

class FootballTeam : ... ICollection<Player>
{
    ...
}

개체에 플레이어 (데이터)의 컬렉션 / 목록이 포함되도록하려는 경우 컬렉션 또는 목록을 속성 또는 멤버로 지정하려면 모두 그렇게하십시오. 개념적으로 :

class FootballTeam ...
{
    public ICollection<Player> Players { get { ... } }
}

플레이어가 셀 수를 세거나 추가하거나 제거하는 대신 플레이어 집합을 열거 할 수 있기를 원할 것입니다. IEnumerable <Player>는 완벽하게 유효한 옵션입니다.

당신은이 인터페이스들 중 어느 것이 당신의 모델에서 전혀 유용하지 않다고 느낄지도 모른다. 이것은 덜 가능성이 있습니다 (IEnumerable <T>는 많은 경우에 유용합니다).하지만 여전히 가능합니다.

이 중 하나가 모든 경우에 분명하고 틀림없이 잘못되었다고 당신에게 이야기하려고 시도하는 사람 은 모두 잘못된 것입니다. 그것은 절대적으로 그리고 결정적으로 당신에게 시도하는 사람은 바로 모든 경우에는 잘못이다.

구현으로 이동

데이터 및 동작을 결정한 후에는 구현에 대한 결정을 내릴 수 있습니다. 여기에는 상속 또는 구성을 통해 의존하는 구체적인 클래스가 포함됩니다.

이것은 큰 걸음이 아닐 수도 있습니다. 사람들은 종종 디자인과 구현을 하나로 모았습니다. 머리에서 두 번째 또는 두 번째로 모두 실행하고 타이핑을 시작하는 것이 가능하기 때문입니다.

생각의 실험

인공적인 예 : 다른 사람들이 언급했듯이, 팀이 항상 "단지"플레이어의 집합은 아닙니다. 팀의 경기 점수 모음을 유지합니까? 모델 팀에서 팀과 클럽을 교체 할 수 있습니까? 그렇다면 팀이 선수를 모으고 있다면 아마도 직원 수 및 / 또는 점수 모음 일 것입니다. 그렇다면 당신은 결국 :

class FootballTeam : ... ICollection<Player>, 
                         ICollection<StaffMember>,
                         ICollection<Score>
{
    ....
}

디자인에도 불구하고 C #의이 시점에서 C # "only"가 단일 상속을 지원하므로 List <T>에서 상속하여 이러한 모든 기능을 구현할 수 없습니다. (이 말라 키를 C ++에서 사용해 본 사람이라면 좋은 생각이라고 생각할 것입니다.) 상속을 통해 하나의 컬렉션을 구현하고 컴포지션을 통해 하나의 컬렉션을 구현하는 것은 더러운 것처럼 느껴질 수 있습니다. <Player> .Count 및 IList <StaffMember> .Count 등 ILIst를 명시 적으로 구현하지 않으면 Count와 같은 속성이 사용자에게 혼란스러워집니다. 혼란스럽지 않고 고통 스럽습니다. 이것이 어디로 가고 있는지 알 수 있습니다. 이 길을 생각하다가이 방향으로 나아가는 것이 잘못되었다고 말할 수 있습니다. (그리고 이런 방식으로 구현한다면 동료도 마찬가지입니다!)

짧은 답변 (너무 늦음)

콜렉션 클래스로부터 상속받지 않는 것에 대한 가이드 라인은 C #과 관련이 없으며 많은 프로그래밍 언어에서 찾을 수 있습니다. 법이 아닌 지혜를 얻습니다. 한 가지 이유는 실제로 컴포지션은 이해력, 구현 가능성 및 유지 관리 측면에서 상속을 상대로 승리하는 것으로 간주된다는 것입니다. 특히 시간이 지남에 따라, 그리고 코드에서 객체의 정확한 데이터와 동작에 대해 깊이 생각하지 않는 한 유용하고 일관된 "isa"관계보다 유용하고 일관된 "hasa"관계를 찾는 것이 실제 / 도메인 객체와 더 일반적입니다 변경. 이것에 의해, 콜렉션 클래스로부터 상속하는 것을 항상 배제 할 것은 없습니다. 그러나 암시적일 수 있습니다.


Answer #5

여기에 좋은 해답이 있습니다. 나는 그 (것)들에 뒤에 오는 점을 추가 할 것입니다.

"논리적으로"(즉, "인간의 마음에") 몇 가지 종소리와 호루라기가있는 목록 일 뿐인 데이터 구조를 나타내는 올바른 C # 방식은 무엇입니까?

비어있는 것을 채우기 위해 축구의 존재에 익숙한 열 명의 컴퓨터 프로그래머가 아닌 사람들에게 물어보십시오.

A football team is a particular kind of _____

"종소리와 호루라기가있는 축구 선수 명단"이라고 말한 적이 있습니까? 아니면 모두 "스포츠 팀", "클럽"또는 "조직"이라고 말했습니까? 축구 팀이 특별한 종류의 선수 라는 귀하의 생각은 인간의 마음과 인간의 마음 속에 있습니다.

List<T>메커니즘 입니다. 축구 팀은 비즈니스 개체 입니다. 즉, 프로그램의 비즈니스 도메인 에있는 개념을 나타내는 개체입니다. 그 (것)들을 섞지 말라! 축구 팀 은 일종의 팀입니다. 그것은 명단을 가지고 있고 , 명단 선수 명단 입니다 . 명단은 특정 종류의 선수 목록 이 아닙니다. 명부 선수 명단 입니다 . List<Player>Roster 라는 속성을 만듭니다. 그리고 풋볼 팀에 대해 아는 모든 사람들이 선수 명단에서 선수를 지우지 못한다고 생각하지 않는다면, ReadOnlyList<Player> 를 읽으십시오.

List<T> 로부터 상속받을 수 없는가?

누구에게 용납 될 수 없습니까? 나를? 아니.

언제 받아 들일 수 있습니까?

List<T> 메커니즘확장 하는 메커니즘을 만들 때.

List<T> 로부터 상속받을 것인지 아닌지를 결정할 때 프로그래머는 무엇을 고려해야 하는가?

메커니즘 이나 비즈니스 객체를 구축하고 있습니까?

그러나 그것은 많은 코드입니다! 나는 그 모든 일을 위해 무엇을 얻습니까?

List<T> 50 개 이상의 관련 멤버에 대한 전달 방법을 작성하는 데 걸렸을 것이라는 질문을 입력하는 데 더 많은 시간을 할애했습니다. 당신은 명확하게 두려움을 두려워하지 않으며, 여기서는 아주 적은 양의 코드에 대해 이야기하고 있습니다. 이것은 몇 분 작업입니다.

최신 정보

좀 더 생각해 봤는데 축구 팀을 선수 명단으로 모델링하지 않는 또 다른 이유가 있습니다. 실제로 축구 팀을 선수 명단으로 모델링하는 것은 나쁜 생각 일 수 있습니다. 선수 명단을 가지고있는 팀의 문제는 당신이 가진 순간이 순간적으로 팀의 스냅 샷 이라는 것입니다. 귀하의 비즈니스 사례가이 수업에 무엇인지 모르겠지만 풋볼 팀을 대표하는 수업이 있었다면 "Seahawks 선수들이 2003 년과 2013 년 사이에 부상으로 인해 게임을 놓친 사람이 몇 명이나 되었습니까?"와 같은 질문을하고 싶습니다. 또는 "이전에 다른 팀에서 뛰었던 덴버 선수가 전년 대비 가장 큰 야드를 뛰게 한 선수는 누구였습니까?" 또는 " Piggers는 올해 내내 갔습니까? "

즉, 풋볼 팀은 선수를 채용, 부상 당하거나 은퇴 한 시점 등 의 역사적 사실을 모아 놓은 것으로 잘 모델링 된 것으로 보입니다. 분명히 현재 선수 명단은 전장에 있어야 할 중요한 사실입니다. 센터이지만, 더 많은 역사적인 관점을 요구하는이 객체로하고 싶은 다른 흥미로운 것들이있을 수 있습니다.


Answer #6

와우, 귀하의 게시물에는 수많은 질문과 포인트가 있습니다. 당신이 Microsoft로부터 얻는 대부분의 추론은 정확한 시점입니다. List<T> 대한 모든 것부터 시작하겠습니다.

  • List<T> 고도로 최적화되어 있습니다. 주요 용도는 객체의 private 멤버로 사용되는 것입니다.
  • class MyList<T, TX> : List<CustomObject<T, Something<TX>> { ... } 같이 더 친숙한 이름을 가진 클래스를 만들 때가 있기 때문에 Microsoft에서 봉인하지 않았습니다. 이제 var list = new MyList<int, string>(); 을 수행하는 것만 큼 쉽습니다 var list = new MyList<int, string>(); .
  • CA1002 : 일반 목록을 공개하지 마십시오 . 기본적으로이 앱을 독점 개발자로 사용하려는 경우에도 좋은 코딩 방법으로 개발하는 것이 좋습니다. 소비자가 색인 된 목록을 필요로하는 경우 IList<T> 로 목록을 노출 할 수 있습니다. 이것은 나중에 클래스 내에서 구현을 변경해 보겠습니다.
  • Microsoft는 Collection<T> 매우 일반적인 모델로 만들었습니다. 왜냐하면이 모델은 일반적인 개념이므로 ... 이름에서 알 수 있습니다. 그것은 단지 컬렉션입니다. IList<T> 를 구현하지만 List<T> 를 구현 하지 않는 SortedCollection<T> , ObservableCollection<T> , ReadOnlyCollection<T> 등의보다 정확한 버전이 있습니다.
  • Collection<T> 는 회원 (즉, 추가, 제거 등)이 가상이기 때문에 재정의 할 수있게합니다. List<T> 는 그렇지 않습니다.
  • 귀하의 질문의 마지막 부분에 자리하고 있습니다. 축구 팀은 단순한 선수 명단 그 이상입니다. 따라서 선수 목록이 포함 된 수업이어야합니다. 컴포지션과 상속을 생각하십시오. 축구 팀 선수 명단 (명부)을 가지고 있으며 , 선수 명단이 아닙니다.

이 코드를 작성했다면 클래스는 아마도 다음과 같이 보일 것입니다.

public class FootballTeam
{
    // Football team rosters are generally 53 total players.
    private readonly List<T> _roster = new List<T>(53);

    public IList<T> Roster
    {
        get { return _roster; }
    }

    // Yes. I used LINQ here. This is so I don't have to worry about
    // _roster.Length vs _roster.Count vs anything else.
    public int PlayerCount
    {
        get { return _roster.Count(); }
    }

    // Any additional members you want to expose/wrap.
}

Answer #7

이것은 compositioninheritance 의 고전적인 예입니다.

이 특별한 경우 :

팀이 행동이 추가 된 선수 목록입니까?

또는

팀은 플레이어 목록을 포함하는 자체 개체입니다.

List를 확장하면 여러 가지 방법으로 자신을 제한 할 수 있습니다.

  1. 액세스를 제한 할 수 없습니다 (예 : 명단을 변경하는 사람들 중지). List 메서드를 모두 가져올 필요가 있는지 여부를 결정할 수 있습니다.

  2. 다른 것들의리스트를 원한다면 어떻게 될까요? 예를 들어, 팀에는 코치, 매니저, 팬, 장비 등이 있습니다. 그 중 일부는 자체적으로 목록이 될 수도 있습니다.

  3. 상속 옵션을 제한합니다. 예를 들어 일반 Team 객체를 만든 다음 BaseballTeam, FootballTeam 등을 상속하는 것이 좋습니다. List로부터 상속 받기 위해서는 팀으로부터 상속을 받아야하지만, 그러면 모든 다양한 팀 유형이 같은 로스터를 구현해야합니다.

작곡 - 당신이 당신의 객체 내부에서 원하는 행동을주는 객체를 포함합니다.

상속 - 개체가 원하는 동작을 가진 개체의 인스턴스가됩니다.

두 가지 모두 용도가 있지만 구성이 바람직한 경우는 분명합니다.


Answer #8

FootballTeam 에 메인 팀과 함께 예비 팀이 있다면 어떨까요?

class FootballTeam
{
    List<FootballPlayer> Players { get; set; }
    List<FootballPlayer> ReservePlayers { get; set; }
}

어떻게 모델링 할 것입니까?

class FootballTeam : List<FootballPlayer> 
{ 
    public string TeamName; 
    public int RunningTotal 
}

그 관계는 명확하게 a 가 아니라 a 이다.

또는 RetiredPlayers ?

class FootballTeam
{
    List<FootballPlayer> Players { get; set; }
    List<FootballPlayer> ReservePlayers { get; set; }
    List<FootballPlayer> RetiredPlayers { get; set; }
}

일반적으로 컬렉션에서 상속 받기를 원하면 SomethingCollection 클래스의 이름을 지정하십시오.

SomethingCollection 의미 론적으로 의미가 있습니까? 귀하의 유형 Something 모음 인 경우에만 이것을하십시오.

FootballTeam 의 경우에는 제대로 작동하지 않습니다. Team 은 하나의 Collection 이상입니다. Team 이 코치, 트레이너 등을 가질 수있는 다른 답변이 있습니다.

FootballCollection 은 축구 컬렉션이나 축구 용품 모음처럼 들릴 수 있습니다. TeamCollection , 팀들의 모임.

FootballPlayerCollection 은 실제로 그것을 원한다면 List<FootballPlayer> 에서 상속 한 클래스의 유효한 이름이 될 선수 컬렉션처럼 들립니다.

Really List<FootballPlayer> 는 다루기에 아주 좋은 유형입니다. 어쩌면 IList<FootballPlayer> 메서드에서 반환하는 경우 일 수 있습니다.

요약하자면

자신에게 물어

  1. XY 입니까? 또는 XY 입니까?

  2. 내 수업 이름이 무엇인지 의미합니까?


Answer #9

그들이 List<T>"최적화 된" 이라고 말하면 좀 더 비싼 가상 메소드와 같은 기능을 가지고 있지 않다는 것을 의미한다고 생각합니다. 그래서 문제는 한 번에 노출 있다는 것입니다 List<T>귀하의 공개 API 당신에게, 비즈니스 규칙을 적용 할 이상 그 기능을 사용자 정의 할 수 느슨한 수. 그러나이 상속 된 클래스를 프로젝트 내에서 내부적으로 사용하는 경우 (API로 수천 명의 고객 / 파트너 / 다른 팀에 잠재적으로 노출되는 것과 반대), 시간을 절약하면 원하는 기능이 될 수 있습니다. 복제. 상속의 장점은 List<T>가까운 미래에 사용자 정의 할 수없는 바보 래퍼 코드를 많이 제거한다는 것입니다. 또한 클래스가 명시 적으로 동일한 의미를 갖도록하려는 경우List<T> 귀하의 API의 삶에 대한 그것은 또한 괜찮을 수도 있습니다.

나는 FxCop 규칙이 그렇게 말하고 있거나 누군가의 블로그가 "나쁜"관행이라고 말하기 때문에 많은 사람들이 여분의 작업을 수행하는 것을 종종 봅니다. 많은 경우에, 이것은 패턴 palooza 별난을 디자인하기 위해 코드를 바꿉니다. 많은 지침과 마찬가지로 예외 가질 있는 가이드 라인으로 취급하십시오 .


Answer #10

나는 당신의 일반화에 동의하지 않는다고 생각합니다. 팀은 단순한 선수 집단이 아닙니다. 팀에는 이름, 상징, 관리 / 관리자 스태프 수집, 코치 승무원 수집, 그리고 플레이어 수집과 같은 많은 정보가 있습니다. 따라서 FootballTeam 클래스에는 3 개의 컬렉션이 있어야하며 컬렉션 자체가 아닙니다. 현실 세계를 적절히 모델링하는 것이지요.

Specialized StringCollection처럼 내부 저장소에 객체를 추가하거나 제거하기 전에 유효성 검사 및 검사와 같은 다른 기능을 제공하는 PlayerCollection 클래스를 고려해 볼 수 있습니다.

아마도 PlayerCollection의 개념이 선호하는 접근 방식에 적합할까요?

public class PlayerCollection : Collection<Player> 
{ 
}

그리고 FootballTeam은 다음과 같이 보일 수 있습니다 :

public class FootballTeam 
{ 
    public string Name { get; set; }
    public string Location { get; set; }

    public ManagementCollection Management { get; protected set; } = new ManagementCollection();

    public CoachingCollection CoachingCrew { get; protected set; } = new CoachingCollection();

    public PlayerCollection Players { get; protected set; } = new PlayerCollection();
}

Answer #11

내 더러운 비밀 : 나는 사람들이 말하는 것을 신경 쓰지 않고 그것을한다. .NET Framework는 "XxxxCollection"(맨 위에는 UIElementCollection)으로 확산됩니다.

그래서 내가 말하는 것을 멈추게한다.

team.Players.ByName("Nicolas")

내가 그것을 더 잘 발견 할 때

team.ByName("Nicolas")

또한, 내 PlayerCollection은 "Club"과 같은 다른 클래스에서 코드 중복없이 사용할 수 있습니다.

club.Players.ByName("Nicolas")

어제의 우수 사례는 내일의 사례가 아닐 수도 있습니다. 대부분의 모범 사례 뒤에는 이유가 없으며, 대부분은 커뮤니티 간의 광범위한 합의입니다. 지역 사회에 물어 보는 대신 자신에게 물어볼 때 당신을 비난 할 것인지, 더 읽기 쉽고 유지할 수있는 것은 무엇입니까?

team.Players.ByName("Nicolas") 

또는

team.ByName("Nicolas")

정말.의심의 여지가 있니? 이제는 실제 사용 사례에서 List <T>를 사용하지 못하도록하는 다른 기술적 제약 조건을 가지고 놀 필요가 있습니다. 그러나 존재해서는 안되는 제약 조건을 추가하지 마십시오. 마이크로 소프트가 그 이유를 문서화하지 않았다면, 그것은 분명히 아무데도 오는 "모범 사례"일 것입니다.


Answer #12

대부분의 답변으로는 복잡한 비교가 없지만 이러한 상황을 처리하기위한 방법을 공유하고 싶습니다. 확장을 IEnumerable<T>통해 Team클래스가 모든 메소드와 속성을 공개하지 않고도 Linq 쿼리 확장을 지원할 수 있습니다 List<T>.

class Team : IEnumerable<Player>
{
    private readonly List<Player> playerList;

    public Team()
    {
        playerList = new List<Player>();
    }

    public Enumerator GetEnumerator()
    {
        return playerList.GetEnumerator();
    }

    ...
}

class Player
{
    ...
}

Answer #13

이것은 나에게 "있는 것"대 "있는 것"을 생각 나게합니다. 때로는 수퍼 클래스에서 직접 상속하는 것이 더 쉽고 더 중요합니다. 다른 경우 독립형 클래스를 만들고 멤버 변수에서 상속 한 클래스를 포함시키는 것이 더 중요합니다. 클래스의 기능에 계속 액세스 할 수는 있지만 인터페이스 나 클래스에서 상속 할 때 생길 수있는 다른 제약 사항에 바인딩되지는 않습니다.

어느 쪽을합니까? 많은 것들과 마찬가지로 ... 그것은 상황에 달려 있습니다. 내가 사용할 가이드는 다른 클래스에서 상속 받기 위해서는 진정으로 "is a"관계 여야한다는 것입니다. 따라서 BMW라는 클래스를 작성하면 BMW가 실제로 차이기 때문에 Car에서 상속받을 수 있습니다. Horse 클래스는 말에서 실제로 포유 동물이며 실제 포유 동물의 기능은 Horse와 관련이 있어야하기 때문에 Mammal 클래스에서 상속받을 수 있습니다. 하지만 팀이 목록이라고 말할 수 있습니까? 내가 말할 수있는 것에서 볼 때 팀과 같은 것은 실제로 "목록"입니다. 그래서이 경우 멤버 변수로 List가 있습니다.


Answer #14

이는 "팀"객체 의 동작 에 따라 다릅니다 . 그것이 마치 콜렉션과 같이 동작한다면, 먼저 평범한 List로 표현하는 것이 좋습니다. 그런 다음 목록에서 반복되는 코드를 복제하는 것을 계속 알 수 있습니다. 이 시점에서 플레이어 목록을 래핑하는 FootballTeam 객체를 생성 할 수 있습니다. FootballTeam 클래스는 플레이어 목록에서 반복되는 모든 코드의 홈이됩니다.

그것은 내 코드를 불필요하게 장황하게 만든다. 이제 my_team.Count 대신 my_team.Players.Count를 호출해야합니다. 고맙게도 C #을 사용하면 색인 생성기를 정의하여 색인 생성을 투명하게 만들 수 있으며 내부 목록의 모든 메서드를 전달할 수 있습니다. 나는 그 모든 일을 위해 무엇을 얻습니까?

캡슐화.귀하의 고객은 FootballTeam 내부에서 무슨 일이 일어나는지 알 필요가 없습니다. 모든 고객이 알고있는 바와 같이, 데이터베이스에있는 플레이어의 목록을보고 구현할 수 있습니다. 그들은 알 필요가 없으며, 이는 디자인을 향상시킵니다.

그것은 평범하지 않습니다. 축구 팀은 선수 명단을 갖고 있지 않습니다. 그것은 선수 목록입니다. 당신은 "John McFootballer가 SomeTeam의 선수들과 합류했다고"말하지 않습니다. 당신은 "존이 SomeTeam에 가입했다"고 말합니다. "문자열의 문자"에 문자를 추가하지 않으면 문자열에 문자를 추가합니다. 도서관의 서적에 책을 추가하지 않고 서적을 도서관에 추가합니다.

정확히 :) 당신은 footballTeam을 말할 것입니다. (죤), 축구 팀이 아닙니다.리스트. 추가 (존). 내부 목록은 표시되지 않습니다.


Answer #15

질문을 다시 써 보겠습니다. 그래서 다른 관점에서 주제를 볼 수 있습니다.

축구 팀을 대표해야 할 때 기본적으로 이름이라는 것을 이해합니다. Like : "The Eagles"

string team = new string();

그런 다음 나중에 팀에 선수가 있다는 것을 알게되었습니다.

왜 나는 문자열 유형을 확장하여 플레이어 목록도 보유 할 수 없습니까?

문제에 대한 귀하의 입장은 임의적입니다. 팀이 무엇을 생각하는 시도 그렇지 않은 것 (특성) 입니다 .

그런 다음 다른 클래스와 속성을 공유하는지 확인할 수 있습니다. 상속에 대해서 생각해보십시오.


Answer #16

축구 팀 축구 선수 목록 이 아닙니다 . 축구 팀 축구 선수 목록으로 구성되어 있습니다 !

이것은 논리적으로 잘못되었습니다.

class FootballTeam : List<FootballPlayer> 
{ 
    public string TeamName; 
    public int RunningTotal 
}

이 말이 맞습니다.

class FootballTeam 
{ 
    public List<FootballPlayer> players
    public string TeamName; 
    public int RunningTotal 
}

Answer #17

클래스 사용자가 ** List에있는 모든 메서드와 속성을 필요로하는 경우 해당 클래스에서 클래스를 파생시켜야합니다. 필요하지 않은 경우 List 클래스를 묶고 클래스 사용자가 실제로 필요로하는 메서드에 대한 래퍼를 만듭니다.

공용 API 또는 많은 사람들이 사용할 다른 코드를 작성하는 경우 이는 엄격한 규칙 입니다. 작은 응용 프로그램과 2 명 이상의 개발자가없는 경우이 규칙을 무시할 수 있습니다. 이렇게하면 시간을 절약 할 수 있습니다.

작은 응용 프로그램의 경우 덜 엄격한 다른 언어를 선택할 수도 있습니다. Ruby, JavaScript - 적은 코드를 작성할 수있는 모든 것.


Answer #18
class FootballTeam : List<FootballPlayer> 
{ 
    public string TeamName; 
    public int RunningTotal;
}

이전 코드는 거리에서 축구를하는 사람들이 많다는 것을 의미합니다. 같은 것 :

어쨌든,이 코드는 (내 답변에서)

public class FootballTeam
{
    // Football team rosters are generally 53 total players.
    private readonly List<T> _roster = new List<T>(53);

    public IList<T> Roster
    {
        get { return _roster; }
    }

    public int PlayerCount
    {
    get { return _roster.Count(); }
    }

    // Any additional members you want to expose/wrap.
}

의미 : 이것은 관리, 선수, 관리자 등이있는 축구 팀입니다. 다음과 같은 것 :

이것은 당신의 논리가 그림에 어떻게 표시되는지입니다 ...





inheritance