
Why not inherit from List? - rodrigocoelho
http://stackoverflow.com/questions/21692193/why-not-inherit-from-listt
======
alkonaut
The various SO answers combined sum it up.

\- Single inheritance means it's unnatural to inherit from the player list
which is just one aspect of a team.

\- It's a leak of implementation detail. Tomorrow you may want the team to
contain a sorted collection, or a set, or a sorted set.

\- It gives an unnatural syntax to team.count vs team.players.count. Again,
composition wins in clarity

\- You can still expose any part of a collection interface in your Team class,
e.g. team.Add(player). The details of what this does should be hidden. Adding
an interface like IEnumerable<Player> to the team gives a nice syntax like
"foreach(Player p in team)" without breaking encapsulation.

\- A list is a simple data structure, a team is not. Extending something to
completely change what it is usually means you have an unnatural inheritance.
A special list (sorted, cirular...) can extend list in a natural way, a
business object should probably "have a" rather than "be a" list.

~~~
ollysb
>> Adding an interface like IEnumerable<Player> to the team gives a nice
syntax like "foreach(Player p in team)"

It would be interesting to allow _private_ inheritance, i.e. a class inherits
methods from a superclass but they're all private and you need to add
interfaces to expose them. That way you could inherit an implementation
without leaking it into the public interface.

~~~
nitrogen
C++ does allow private inheritance IIRC, but at that point, why not just use a
nember variable?

~~~
ollysb
Good point, I suppose it would just be a poor man's delegation really.

------
bilalq
So the question basically drills down to why one should use composition
instead of inheritance. Most OOP languages only allow inheriting from a single
class, and that alone is reason enough to prefer composition in most cases.
It's one of the reasons I'm particularly fond of traits/modules.

~~~
ericHosick
Also a best known practice:

[http://en.wikipedia.org/wiki/Composition_over_inheritance](http://en.wikipedia.org/wiki/Composition_over_inheritance)

~~~
auvrw
although the two aren't mutually exclusive

[http://en.wikipedia.org/wiki/Decorator_pattern](http://en.wikipedia.org/wiki/Decorator_pattern)

------
frou_dh
The word "interface" isn't in an answer until the 7th one down. The corrupted-
OOP straitjacket even suppresses useful terminology!

------
danielhunt
I think his core assumption is wrong in this case, regardless of his feelings
towards using List or not:

You count the players on a team, not the team itself: team.count() means
nothing Team.players.count() is correct

It would, in fact, be incorrect to even implement a List interface on his Team
object

~~~
habosa
I don't think that's correct. He thinks that a team IS a group of players.
Just like a list IS a group of items. team.players.count is like
list.items.count

But that's a small point, in general I agree that he should not be inheriting
from List here.

------
javajosh
A sports team is a state-machine that evolves in time; a list, or indeed even
a value object, doesn't itself express this.

That said, the pithy, smug, common and unenlightening answer is that
inheritance breaks encapsulation. But really, that's a terrible reason not to
extend a collection class if you really want to. Just remain aware of the
trade-offs and do it.

~~~
judk
Java Lists evolve in time.

------
ucarion
I think this could be boiled down as: List<T> is a data structure. Anything
that inherits from List<T> should be a data structure too. So LinkedList<T>
could reasonably extend List<T>, but Team should not.

~~~
V-2
Yeah. That's what Eric Lippert called a mechanism vs. a business object. I
like this differentiation

------
chebum
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.

~~~
fzltrp
Strictly speaking you're probably correct. But the problem is that OP is
giving as an example a team, without strictly defining the expected behaviour
of that class, so everyone starts to imagine his own set of behaviour
ovverides, invariants, and so on, and in many cases they are right (the
salient point of subclassing is method overriding).

The problem really starts when an invariant supported by a superclass is
broken by it subclass. For instance, the idea that a player is unique in a
team is obvious, so as said elsewhere in this thread, the Add method, whose
invariant is to increment by one the size of the list (because duplicates are
allowed, and pretty much no predicate other than equality is applicable to a
generic type), would be broken by that Team subclass, and thus any function
relying on that implicit behaviour (afaik, c# doesn't implement contracts)
would break.

That Liksov substituton principle is tough indeed.

------
tolmasky
Do you want to be able to pass in Team for every function that currently asks
for List<T>? Because that is what you are stating through the act of
subclassing. At some point some part of your program may have a List<T>, but
_actually_ have a Team.

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.

~~~
alcari
[n.b: I'm used to duck typed languages, where you can get away with far more
shenanigans.]

> "Can I guarantee every function that expects T to behave exactly the same
> way when I pass P (where P is a subclass of T)". Almost always the answer is
> NO, that's why you subclassed it to begin with! So you shouldn't do it.

The entire point of subclassing is to be able to answer that question "no". By
your logic, we should never subclass anything, because it will behave
differently!

It seems to me you're not really trying to argue against subclassing here, but
rather against making specific container types subclasses of generic
containers types with extra logic (that breaks the container's documented
guarantees) added, instead of containing the logic and the container as class
attributes.

I'm reminded of the basic inheritance examples that show up all over, where
Dog inherits from Animal: this makes sense because a Dog is a type of Animal.
A Team is neither a type of List<T> nor a type of Set<T>, so it should not
inherit from them. A team can, however, contain a roster represented by a List
or Set alongside its other data.

~~~
tolmasky
_> The entire point of subclassing is to be able to answer that question "no".
By your logic, we should never subclass anything, because it will behave
differently!_

100% Correct, that is indeed my argument: the very purpose of subclassing is
contradictory. Subclassing exists solely to mask the mutation of existing
behavior, that is the _only_ feature it adds to composition.

Composition is perfectly capable of ADDING and SUBTRACTING behavior, but to
modify behavior it must do so _transparently_. You can't for example change
the behavior of Add, you must call another method that internally calls Add on
a child object and makes it explicit to the user code that the behavior is
thus different.

There is no point in having a type system if the types become meaningless
through subclass manipulation. I make a class T that has method M with
postcondition P. People write functions that accept T with these expectations.
When you create a subclass of T that modifies the behavior of M, the existing
functions can no longer be trusted to behave correctly -- this is just a fact.

It's not at all just about "container classes". We see this broken behavior
all the time in UI classes like UITableView extending UIView. UIView
theoretically has a publicly mutable subviews array, but it _stops_ being so
by certain subclasses. So all of a sudden you have a UIView subclass that
can't be passed in as an argument to functions with UIView parameters. This
may be obvious to people familiar with the framework, but to novices, it now
becomes meaningless to see the parameter UIView. Maybe it will do the right
thing, maybe it won't, depends on the subclass. Now you have to go read the
documentation (hopefully its documented).

The reason inheritance is taught with ridiculous examples like Dog and Animal
(or even worse, Triangle and Shape) is precisely because it is so hard to find
legitimate examples. You always end up just overriding things like Speak()
(which does nothing in Animal and prints "woof" in Dog). "Good" (re: concrete)
examples are usually really convoluted and hard to understand (or end up being
basically just interfaces like NSResponder which is 99% empty methods).

Duck typing environments are saner in my opinion because they are really just
interfaces. You are not making the strong statement "I am an X" (and thus
creating the expectation of static behavior), but instead "I have loose
behaviors A,B, and C" (in the same way as interfaces).

~~~
sirmarksalot
I came in expecting to fully agree with you here, but I think I see a flaw in
your logic, unless I'm misunderstanding you.

> Duck typing environments are saner in my opinion because they are really
> just interfaces. You are not making the strong statement "I am an X" (and
> thus creating the expectation of static behavior), but instead "I have loose
> behaviors A,B, and C" (in the same way as interfaces).

I've taken this to mean that you should not subclass unless you can guarantee
that the external behavior will be the same, i.e. you will always get the same
output for the same input, although its performance characteristics may be
different. This contrasts with implementing an interface, where you can return
different results, as long as the results are valid. Just trying to paraphrase
at this point -- tell me if I'm misrepresenting you.

Where I'm confused is trying to figure out why a violation of the former
property would be an issue. If your code block instantiates a subclass, then
it knows it isn't dealing with the base class, and it expects the behavior of
the subclass -- no problem there. However, when you write a function that asks
for the base class as a parameter, that strikes me as a law of demeter
violation. The function should not care what the passed object does, as long
as it does it legally.

At the risk of straw-manning, I assume your response would be that you should
be asking for the interface in that case, not the class. And that is my point.
You should never need a particular concrete data type, although you might ask
for one to avoid the boilerplate of creating a thousand one-off interfaces.

So this gets back to what people are actually trying to do by subclassing
List<T> or Set<T>. They want a good default implementation of IEnumerable<T>,
IList<T> or ISet<T>, but with some of the behaviors switched out. They'll
still fulfill the base interface requirements, and that's all any other
function should ask for, so really, what's the problem?

~~~
tolmasky
I knew that sentence would require a much longer and deeper discussion (and I
was hoping not to leave OOP to explain it). But in a nutshell, to me
interfaces are meant precisely to represent "unknown behavior" and classes for
"known behavior".

If we take a second to temporarily forget classes altogether and look at
functional programming, you may have something like map(). map() takes a list
(well known construct) and an iterator function (unknown outside behavior).
The API itself makes it clear what can be changed and what can't. The scope of
what is dynamic about the function and what isn't is immediately obvious from
the types themselves, completely transparent.

To me, interfaces serve this same role in non-functional programming: they
represent outside and dynamic behavior. Classes on the other hand represent
known behavior. The problem with "good default implementations" is that they
mix these two (opposite) concepts together, which regularly leads to
completely "consistent" but absurd outcomes, that are usually attempted to be
fixed with language-bandaids. Let me provide two examples.

The first is NSArray and NSMutableArray in Cocoa/Cocoa Touch (apologies for
the use of these, it is my strongest background, and ironically enough, one of
the closest "correct" subclassing examples in my opinion). NSMutableArray is a
subclass of NSArray. This makes no sense. Granted, it makes complete sense
from an "implementation" view, but when it comes to user code it makes the
classes meaningless: if I make a method that takes an NSArray, the whole point
is I'm saying its immutable. But I can pass in a mutable array and none of my
expectations will be valid: the array could change right under me. And yet the
compiler will be perfectly happy because it is a correct statement,
NSMutableArrays _are_ NSArrays. This should theoretically be an edge case:
using a subclass to provide the OPPOSITE behavior of the original superclass,
rendering the type-system absurd, and yet it is a widely used construct. I am
thus forced to defensively copy the immutable array because it may very well
be a mutable -- something completely "consistent" in this world. This to me
shows this fundamental confusion of interface vs. classes. To get back to what
you were saying, the problem with subclassing is precisely that it is
incredibly difficult to state what is done "legally".

If you have a pure interface, with no existing "default" behavior, its just
like a lambda: anything goes. If you have a class, its static. If you have
both (subclassing), it gets incredibly tricky and hard to predict the
interaction. The entire API of the class becomes surface area for mutation.

Now let me give you the second example: UIView. UIView has a -subviews method.
Why can't I override -subviews to return a static list of views if I never
want it to change (thus rendering addSubview: and removeSubview null)? The
documentation does NOT list this method as non-overridable (And many people in
fact override it to do perfectly legitimate things btw). And if I do override
it, I still abide by all the postconditions of the method as defined by the
API. And in fact, OTHER parts of the framework totally allow (And sometimes
encourage!) this type of "override instead of setting"-style programming. As a
novice using the framework, its a completely logical expectation that this
should work. So why does UIKit completely break down?

Because the real postconditions are incredibly more complicated than just
"Return your subviews". You have part of the code written as if nothing will
ever change (internally UIView relies on the _subviews ivar), but another that
acts like an interface advertising that it might change (possible UIView
subclasses). And half the time, the changes don't upset any delicate balance.
But other times, it does. Things like a final keyword wouldn't fix the above
problem because again, sometimes its legitimate to override. You can
absolutely start writing your code in such a way where things "should" work no
matter what method a subclass overrides, but that is very hard to get right
(and very hard to test), whereas you get this for free by just forcing
everyone to use the external interface! (This is what is meant by breaking
encapsulation: you work so hard to provide a sane API, that then anyone can
break by changing in otherwise completely legitimate ways). The real problem
is that you are saying two contradictory things. What you actually want is to
do ONE of the following:

1\. Force everyone to use addSubview: and removeSubview: since its a concrete
class in a hypothetical non-subclassable Objective-C world. No absurd results
because, again, everyone is using the API you worked so hard on and properly
tested (i.e. there is no way to futz around with -subviews) OR

2\. Have -subviews be a method of some sort of Drawable interface, such that
every other piece of code is now known to work with the expectation that it
calls that instead of some internal ivar or something, allowing all the
shenanigans you want.

Hopefully that makes my views a little clearer, but I'm super tired since its
2AM here so perhaps some of this was more verbose or not as clear as it could
be.

------
ap22213
When I encounter OO beginners, I often have them check out the SOLID
principles:

[http://en.m.wikipedia.org/wiki/Solid_(object-
oriented_design...](http://en.m.wikipedia.org/wiki/Solid_\(object-
oriented_design\))

I think this person is violating at least 4 of them.

~~~
raverbashing
I feel OO is so much prone to be misused it's not even funny, exactly because
of this.

Or creating a "Human" class inheriting from Body, Arm, Leg, Head

Inheritance also makes TDD a pain in the behind (my opinion) it's not even
funny.

~~~
ap22213
OO is a very hard art. It took me a good 10 years to be effective at it. But,
I learned in the 90s, when there was a lot less knowledge available. These
days, it should be a lot easier - so many good resources.

Anyway, I would love to try other ways of doing it. I see functional
programming often sold as the better alternative. However, I've never seen a
large code base written entirely in functional language.

Anyone have an examples of large functional programs code bases that are good
to learn from?

~~~
dllthomas
GHC is written in Haskell. I don't know the line count, but I understand it to
be a large project.

~~~
lomnakkus
It was around 140K LoC as of 2011. [1]

[1] [http://www.aosabook.org/en/ghc.html](http://www.aosabook.org/en/ghc.html)

EDIT: I see another poster arrived at a conclusion of 200KLoC from the same
source. I'm guessing the discrepancy is about the ~40KLoC which comprise the
runtime system.

------
boomlinde
I think it's fundamentally wrong to model a team as a list of players. It
isn't a list of players, and having a data structure representing the players
as a property of the team seems more meaningful in terms of communicating the
idea of team members. Even then, the most useful data structure to represent
the member players would probably be a set, in my opinion. Then, the whole
history of the team could be expressed in terms of simple operations on that
set.

------
EdwardDiego
> Microsoft did not seal it because sometimes you might want to create a class
> that has a friendlier name

I expect multiple-language support may have been a stumbling block here but
compromising your API (instead of sealing the class, document it as "you
shouldn't subclass this") for something that could be done with type aliasing
seems like a really bad trade-off. Is the C# _using_ keyword a johnny come
lately, or would it not support this functionality?

~~~
seanmcdirmid
Yep:

using MyList = List<MyThing>;

The only limitation is that you can't alias generic types, just fully
instantiated ones.

------
goblin89
I'd rather link to the first answer
([http://stackoverflow.com/a/21694054/247441](http://stackoverflow.com/a/21694054/247441)).
The “update” section looks like a nice example of how to step back and think
about data modeling of this sort.

> a football team seems to me to be well modeled as a collection of historical
> facts such as when a player was recruited, injured, retired, etc

~~~
yayitswei
Reminds me of the tagline for Datomic, a "database of time-based facts".

------
gbog
Would be interesting to hear what would be the functional stand on this.

In a way a team may not need the players. Maybe teams are just identifiers, as
are players, and we have a list of event relating players to teams. Then a
function can get these events and compute the current list of players in a
given team.

~~~
nbouscal
One way of doing it would be to use a record type, one part of which would be
the list of players. This is analogous to the composition strategy in OO. You
could then manipulate values of that record type using lenses.

The important question is what you actually want to do with this data. The
thing that has always seemed crazy to me about OO is how you can have page-
long discussions like this one without even talking about what the data is
being used for. The notion instead is to "model the real world", which sends
you off on a wild goose chase that is almost always unnecessary.

------
rmrfrmrf
IMO the easiest way to poke a hole in the argument is to say: ok, now what
happens when you want to add the coach of the team? and the owner? Adding them
to the Players list would be problematic considering they're not really
players. If you tried to rig it anyway, your Count method would return
incorrectly, and you'd have to override it to exclude coaches. While we're at
it, where are you storing the team's name? A variable name alone isn't going
to help you display that info to the end-user.

Granted, none of this matters if the OP's problem domain stays where they have
it now. OOP, however, shines when your domain is subject to change and
developers are willing to sacrifice terseness for flexibility.

------
oneeyedpigeon
In part, this is a confusion caused by semantics and inherent difficulties
with any kind of abstraction. The notion of a 'team' in real life is a very
flexible one, and as soon as we try to represent it using a concrete data
structure, we're bound to run into problems. The questioner shouldn't be
blamed for thinking a team and a list of players are one and the same - there
are many real life examples in which the two are used interchangeably. If
anything, this highlights the challenge of OOP: real life objects and their
relationships are just really difficult to model in a deterministic way.

------
d0
Is it any harder than this?

    
    
        class Team {
            List<Player> players = new List<Player>();
            public void AddPlayer(Player player) {
                // rule checks here
                players.add(player);
            }
            public IEnumerable<Player> Players {
                get { return players; }
            }
        }
    

Covers SOLID, DDD, the lot.

One should never inherit the list. Encapsulate perhaps but the list is there
as a fundamental structure and to inherit from it would break the
encapsulation of the domain model you are constructing and increase efferent
coupling.

~~~
robotresearcher
One useful improvement: the roster of players should be a set, not a list.
Duplicated player bugs become impossible.

~~~
d0
Excellent point. HashSet<Player> then.

------
nvarsj
Or use a better language ;-). E.g. in Scala you can use the LinearSeq trait,
Ruby the Enumerable mixin to get all the benefits without the drawbacks of
Java style subtyping.

~~~
laureny
> Or use a better language ;-). E.g. in Scala

You might want to watch Paul Phillips' recent talk:

[http://www.youtube.com/watch?v=4jh94gowim0](http://www.youtube.com/watch?v=4jh94gowim0)

You will probably start changing your mind about whether Scala is a "better"
language. Especially on the collections side.

> to get all the benefits without the drawbacks of Java style subtyping.

Actually, the Scala collections have the exact same problems caused by
subtyping that Java does (and quite a few more actually, as Paul explains).

------
crystaln
TLDR: Class hierarchies should only be used for truly hierarchical objects,
which is rare. Composition is almost always the correct structure. When in
doubt, use composition. You can almost never go wrong this way.

Another way to look at it, by subclassing List, any method that takes a list
can also take a Team. That makes no sense. A method that takes a List should
take a list of team members.

------
harunurhan
I don't think every player is a single property of team. However list of
players is. It is more logic to create an object for team which has list of
players, name, etc. Although your example doesn't explain your problem, there
may be some cases that we need to inherit from List.

------
cratermoon
Is a football team a _kind of_ List? If not, then it's wrong to inherit from
List.

------
gesman
Just use extension methods to List. Weirdly no one talks about it.

