15 ms·
Why not inherit from List?
- bilalq 13y agoSo 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.
- RyanZAG 13y agoI don't really know of any OOP language that doesn't have some concept of multiple defined interfaces (eg HasPlayers, HasHomeCity), which is really what you'd want in this case rather than traits or modules.
- bilalq 13y agoThe problem with interfaces is that you can't implement any operations within them. Traits and modules do allow for that. Regardless, compositition makes more sense for this case anyway.
- RyanZAG 13y agoComposition making more sense is exactly why you'd use an interface, as the function inside the class depends on the internal implementation of how you'd store the players most efficiently while still giving a common method to access them. If you used a trait it would lock in the list implementation across all types that HadPlayers etc.
- kooshball 13y agoJava8 will allow method implementations in interfaces called default methods. http://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html http://docs.oracle.com/javase/tutorial/java/IandI/defaultmet...
- auvrw 13y agoso the (only?) difference between an interface and an abstract class in java8 is that interfaces can't have data members.
- nly 13y agoMy Java is rusty (which I'm happy about) but presumably you're still restricted to single inheritance, whereas you can implement multiple interfaces? It's kind of sad how C++s object model is now actually a lot less complex than Javas and still more powerful.
- auvrw 13y ago> whereas you can implement multiple interfaces? right, duh/derp.
- richardwhiuk 13y agoI disagree: - virtual / none virtual functions - virtual / non virtual inheritance - pointers and references - templates I have to say that multiple inheritance normally means you are using the wrong design pattern, and you should be doing composition instead.
- nly 13y agoAll functions in Java are virtual, C++ just lets you make the performance trade-off. Virtual inheritance is only useful in a few cases in complex inheritance hierarchies, and most never need to know it exists. The other 2 aspects of C++ are nothing to do with the object model.
- judk 13y agoAh, Java8. What Java5 should have been, but for Sun being too busy dying and taking Java with it. Java: 20th Century technology... TODAY
- lutusp 13y ago
- ericHosick 13y agoAlso a best known practice: http://en.wikipedia.org/wiki/Composition_over_inheritance http://en.wikipedia.org/wiki/Composition_over_inheritance
- auvrw 13y agoalthough the two aren't mutually exclusive http://en.wikipedia.org/wiki/Decorator_pattern http://en.wikipedia.org/wiki/Decorator_pattern
- Roboprog 13y agoYep. Team HAS-A list of players, not team IS-A list of players. While we're at it, "List <? extends Player>" might be a better type for the roster, since it allows players to be subclassed and still fit into the list.
- ishener 13y agoalso, composition is so much more clear and intuitive than abstract inheritance of objects. one should favor simplicity over sophistication always
- javajosh 13y agoA 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 13y agoJava Lists evolve in time.
- danielhunt 13y agoI 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 13y agoI 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.
- frou_dh 13y agoThe word "interface" isn't in an answer until the 7th one down. The corrupted-OOP straitjacket even suppresses useful terminology!
- tolmasky 13y agoDo 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.
- EdwardDiego 13y ago> 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 13y agoYep: using MyList = List<MyThing>; The only limitation is that you can't alias generic types, just fully instantiated ones.
- chebum 13y agoIf 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 13y agoStrictly 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.
- gbog 13y agoWould 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.
- fzltrp 13y agoClassic functional programming doesn't cover oop: there's no subclassing, thus no issue with using a list to keep your players. Furthermore, pure functional code doesn't mutate, so invariants can't really be broken, unless you meddle with the internal representation, in a way which would confuse the existing codebase to handle lists. So if you pick list as the implementation detail of your team type, then it will essentially be a list, and may be passed as such to list handling functions without issues. It'll be up to your code to handle the aftermath if your invariants were somehow broken in the process, which would be the proper thing to do. From the outside though, just like for any type, the implementation details shouldn't be exposed. For weakly typed languages, it should be documented to not rely on it. Edit: I was just handling the original question from an functional stand point. Of course nbouscal solution is much more usable and robust. What would probably happen, if you follow an agile methodology, or some similarly incremental process of development, is that you'd probably start with a list, and as new functionalities would be added, the type would evolve to a tuple or a record including the list. Any function would then have to be updated accordingly. But at least the team type should be made abstract somehow, to hide implementation details.
- nbouscal 13y agoOne 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.
- ucarion 13y agoI 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 13y agoYeah. That's what Eric Lippert called a mechanism vs. a business object. I like this differentiation
- nvarsj 13y agoOr 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 13y ago> 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).
- gesman 13y agoJust use extension methods to List. Weirdly no one talks about it.
- goblin89 13y agoI'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 13y agoReminds me of the tagline for Datomic, a "database of time-based facts".
- boomlinde 13y agoI 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.
- alkonaut 13y agoThe 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 13y ago>> 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.
- oneeyedpigeon 13y agoIn 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.
- ap22213 13y agoWhen 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 13y agoI 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 13y agoOO 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 13y agoGHC is written in Haskell. I don't know the line count, but I understand it to be a large project.
- dllthomas 13y agoLine count ~140,000, according to this: http://www.aosabook.org/en/ghc.html http://www.aosabook.org/en/ghc.html And of course with more abstraction and less boilerplate, one line gets more done on average. (Fixed the number above - read 139... as 199...)
- lomnakkus 13y agoIt 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.
- d0 13y agoIs 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 13y agoOne useful improvement: the roster of players should be a set, not a list. Duplicated player bugs become impossible.
- d0 13y agoExcellent point. HashSet<Player> then.
- crystaln 13y agoTLDR: 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 13y agoI 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.
- rmrfrmrf 13y agoIMO 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.
- valtih1978 13y agoYou should never use inheritance (aka OOP), http://valjok.blogspot.com/2012/10/polymorphism-is-not-better-than.html http://valjok.blogspot.com/2012/10/polymorphism-is-not-bette... It just does not make any sense and, as a good programmer, you must know that containment is preferred to polymorphism.
- cratermoon 13y agoIs a football team a kind of List? If not, then it's wrong to inherit from List.