When planning out my programs, I often start with a chain of thought like so:
A football team is just a list of football players. Therefore, I should
When you consider your team as a list of players, you are projecting the "idea" of a foot ball team down to one aspect: You reduce the "team" to the people you see on the field. This projection is only correct in a certain context. In a different context, this might be completely wrong. Imagine you want to become a sponsor of the team. So you have to talk to the managers of the team. In this context the team is projected to the list of its managers. And these two lists usually don't overlap very much. Other contexts are the current versus the former players, etc.
So the problem with considering a team as a list of its players is that its semantic depends on the context and that it cannot be extended when the context changes. Additionally it is hard to express, which context you are using.
When you using a class with only one member (e.g. IList activePlayers
), you can use the name of the member (and additionally its comment) to make the context clear. When there are additional contexts, you just add an additional member.
In some cases it might be overkill to create an extra class. Each class definition must be loaded through the classloader and will be cached by the virtual machine. This costs you runtime performance and memory. When you have a very specific context it might be OK to consider a football team as a list of players. But in this case, you should really just use a IList
, not a class derived from it.
When you have a very specific context, it is OK to consider a team as a list of players. For example inside a method it is completely OK to write:
IList<Player> footballTeam = ...
When using F#, it can even be OK to create a type abbreviation:
type FootballTeam = IList<Player>
But when the context is broader or even unclear, you should not do this. This is especially the case when you create a new class whose context in which it may be used in the future is not clear. A warning sign is when you start to add additional attributes to your class (name of the team, coach, etc.). This is a clear sign that the context where the class will be used is not fixed and will change in the future. In this case you cannot consider the team as a list of players, but you should model the list of the (currently active, not injured, etc.) players as an attribute of the team.
Does allowing people to say
myTeam.subList(3, 5);
make any sense at all? If not then it shouldn't be a List.
It depends on the behaviour of your "team" object. If it behaves just like a collection, it might be OK to represent it first with a plain List. Then you might start to notice that you keep duplicating code that iterates on the list; at this point you have the option of creating a FootballTeam object that wraps the list of players. The FootballTeam class becomes the home for all the code that iterates on the list of players.
It makes my code needlessly verbose. I must now call my_team.Players.Count instead of just my_team.Count. Thankfully, with C# I can define indexers to make indexing transparent, and forward all the methods of the internal List... But that's a lot of code! What do I get for all that work?
Encapsulation. Your clients need not know what goes on inside of FootballTeam. For all your clients know, it might be implemented by looking the list of players up in a database. They don't need to know, and this improves your design.
It just plain doesn't make any sense. A football team doesn't "have" a list of players. It is the list of players. You don't say "John McFootballer has joined SomeTeam's players". You say "John has joined SomeTeam". You don't add a letter to "a string's characters", you add a letter to a string. You don't add a book to a library's books, you add a book to a library.
Exactly :) you will say footballTeam.Add(john), not footballTeam.List.Add(john). The internal list will not be visible.
If your class users need all the methods and properties** List has, you should derive your class from it. If they don't need them, enclose the List and make wrappers for methods your class users actually need.
This is a strict rule, if you write a public API, or any other code that will be used by many people. You may ignore this rule if you have a tiny app and no more than 2 developers. This will save you some time.
For tiny apps, you may also consider choosing another, less strict language. Ruby, JavaScript - anything that allows you to write less code.
A football team is not a list of football players. A football team is composed of a list of football players!
This is logically wrong:
class FootballTeam : List<FootballPlayer>
{
public string TeamName;
public int RunningTotal
}
and this is correct:
class FootballTeam
{
public List<FootballPlayer> players
public string TeamName;
public int RunningTotal
}
While I don't have a complex comparison as most of these answers do, I would like to share my method for handling this situation. By extending IEnumerable<T>
, you can allow your Team
class to support Linq query extensions, without publicly exposing all the methods and properties of 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
{
...
}