For starters, most teams I know don't have cloning technology, and thus can't have the same member of a team multiple times, but List<T> absolutely allows this. There are no restrictions on uniqueness in a List<T>. So then you may proceed to override Add() to first check if the team member is already in the team:
void Add(Player aPlayer)
{
if (Contains(aPlayer))
return;
base.Add(aPlayer);
}
Wonderful! But now you're a List<T> by name only, because you don't actually behave like a List. You don't follow the most basic postcondition of the API. Every single method that adds an item to a List<T> expects the List<T> to be 1 item larger afterward. So basically, you've created a List<T> that has undefined behavior with every existing function that ever calls Add(), because it doesn't work the way it was originally sold as working, so what is the point of going around calling yourself a List? You are a crash waiting to happen. You are not a List, you are a list manager.Now is when someone will probably say, no no no. Subclassing isn't the problem. The problem is you didn't subclass the right thing. You should have subclassed Set<T>. Because teams don't have a natural ordering and contain unique players. But now you're just going to run into new problems. Like the fact that you have to check if a player is first in a sane state before adding them:
void Add(Player aPlayer)
{
if (aPlayer.team != null)
return; // need to be a free agent!
base.Add(aPlayer);
}
We can argue whether this should throw, or whether it should do it unconditionally, but the point is that the virtue of needing logic means its not this thing, its something managing this thing. Someone already wrote Set<T> and List<T> code that was almost certainly smarter than you. You wouldn't go poking around their original source to make your program a tiny bit easier to write at risk of ruining other things, so subclassing it is just a way of cheating and only poking around their source only for specific instances, but still causing all the same potential problems for those instances.Remember, you should ask yourself "Can I guarantee every function that expects a T to behave exactly the same way when I pass a P instead (where P is a subclass of T)?". Almost always the answer is NO, since that's why you subclassed it to begin with! So you shouldn't do it. If the answer is YES, then you've probably only added an ivar or new methods in which case its equivalent to composition. The only thing subclassing gives you over composition is the ability to modify the behavior of existing methods, which breaks contracts with every other part of the code.