12 ms·
The Brittleness Of Type Hierarchies
- tmitchel2 14y agoThe idea of composition over inheritance directly solves the issues discussed here... interfaces with a bit of DI work wonders. http://en.wikipedia.org/wiki/Composition_over_inheritance http://en.wikipedia.org/wiki/Composition_over_inheritance
- tomgallard 14y agoIf I were solving this problem in C# I'd probably lean towards defining specific properties of objects in interfaces, rather than just through a type hierarchy. So we might have the interface ISecurityWithIsin , IOption etc. This has the advantage of allowing a.) easy use of mocking, dependency injection for testing. b.) Classes can implement more than one of these interfaces. The question then becomes- where do you put your base, shared functionality (e.g. a method that is common to all stocks with Isin numbers). Possibly this becomes another set of classes...
- tomp 14y agoThe problem with interfaces is that you have to reimplement all the code for the basic functionality in every class. Scala-like traits, which are basically interfaces with optional implementation, should help here. Also, if you're using a flat type hierarchy where classes never inherit, but only extend interfaces, you're better of using a different language which natively supports a structural typing style, such as Go, OCaml, or Objective C.
- tomgallard 14y agoTrue- but in this case the classes looked relatively simple (i.e. they were carrying data, but not operating on it) Taking the interface approach further, your classes that operate on your data would take in objects by interface e.g.: public class IsinValidator ... public bool Validate(IIsinSecurity security) {}
- anuraj 14y agoJust use polymorphic associations (weak linking) wherever you want to enable code reuse and the implementation is not tightly linked to the class hierarchy. It is about how you think in terms of OO.
- colomon 14y agoIn the Moose / Perl 6 world, we use roles for this. (I know the idea is borrowed from elsewhere, but I don't know where.) As a Perl 6 programmer, I have almost completely given up on traditional inheritance, because roles capture everything I want from inheritance with greater robustness. To be fair to original article, this does mean I'm doing my best to avoid type hierarchies...
- cygx 14y agoI know the idea is borrowed from elsewhere, but I don't know where. It probably originated in Flavors, an object system for Lisp (see http://en.wikipedia.org/wiki/Flavors_%28computer_science%29 http://en.wikipedia.org/wiki/Flavors_%28computer_science%29)
- chromatic 14y agoIt was a combination of dissatisfaction with multiple inheritance (see Perl 5, C++), interfaces (see Java), and mixins (see Perl 5, Ruby), as well as an acknowledgement that aspects (see Java) and multimethods (see Common Lisp) solve part of the problem well and part of the problem poorly. Then Allison and I saw Dr. Black present the Smalltalk traits paper and she realized that their formalism was exactly what I'd been talking about informally, so we borrowed that instead.
- tomp 14y agoHow are roles different from mixins? The only difference that I can think of is that mixins are declared at class declaration, is that what you mean?
- chromatic 14y agoRoles have specific composition rules which forbid name collisions; there's no last-in wins rule. Roles also provide type allomorphism which is much less ambiguous than duck typing.
- JoeAltmaier 14y agoRight. Smaller, atomic classes help with this. But then you have a snowstorm of classes, all with more-or-less helpful names. That's complicated too. Probably the better answer though. Sometimes the code is complex because the problem is complex.
- anuraj 14y agoOne of the fundamental principles of OO is go for interface inheritance(design) as opposed to class inheritance(implementation). Better way to enable code reuse is through association. That is why languages like Java do not allow multiple class inheritance, but allow you to inherit from multiple interfaces. You are doing it the wrong way!
- JoeAltmaier 14y agoWrong for some problem spaces, very right for others. There are many problem domains, and no tool right for all of them.
- anuraj 14y agoVery right. It is about how you think in terms of OO. If you feel the problem space cannot be modeled easily, just don't go for OO. That said, many problem spaces are amenable to OO thinking. But some are not (especially the ones that deal with serial hardware interfaces), just go for procedural thinking in these cases.
- colomon 14y agoThe problem is you really want to inherit the implementations as well -- otherwise every one of the classes that use the Isin interface must implement its storage and accessors as well.
- gboyer 14y agoYou can just do both, though. Use interfaces for polymorphism, to define the interactions between your objects -- and then, if you want, use inheritance as one possible strategy for code reuse.
- colomon 14y agoThat works great if you're only using one interface in your class. What if you need to include two?
- hamidpalo 14y agoWe are faced with the dilemma - a lot of the code is now reliant on Isin, and NullReferenceExceptions are getting thrown all over the place because the field isn’t getting populated Sounds like it's catching bugs. You get to fix all the Isin places in a single unit test pass. Or you change the types and it's all fixed by getting it to compile. The examples provided by the author are absolutely horrible code. A type hierarchy can be more than two deep. How about adding another base type for securities with ISINs? Moreover, if you ever see code like security is Option or a switch based on the name of the type it is a great sign of poorly architected code. The solutions provided aren't really solutions at all. How would functional programming solve the problem? If anything a lot of functional languages are even more rigidly typed than C#.
- samspot 14y agoI feel you have missed the point of the article by criticizing the example. The suggestions you mention are discussed as possibilities, and his 'security is Option' is shown to give an example of bad code that arises during the quick fix process. The point is that these situations are common and it would be nice if your language made it easier to adjust your object structure when needed.
- singular 14y ago(Author here) I agree that the code is horrible :-) that's kind of the point - if you choose to hack up your code, then you end up with horrors like if(foo is Bar) { ... } else { ... }. The type declarations are, I'd argue, not horrible at the outset, but once you introduce the new requirements then it becomes incorrect. I even suggest possible solutions including adding depth to the hierarchy - the point is doing that means yak shaving, not doing it results in a definitely horrible object-orientated design because it no longer fits the problem. You can argue that you should have a better model from the outset given the coarse change which comes into play, which is potentially where my example starts to break down (I think it would be hard to find an example that would not break down in some way here, given the need to compress reality into a blog post), so take it as read that in reality the changes are often a lot more subtle than the example I give. The point is that you're having to make far-reaching, rigid decisions up front. I have encountered this over and over again. I am happy to admit it's an inadequacy in object orientated design on my part, and if somebody were to suggest approaches that helps mitigate this problem I'd be very happy, but I do wonder whether it's an inherent property of the whole approach.
- DanielBMarkham 14y agoHere's the thing: while you do not have to do a Big Design Up Front, there's no reason in the world why you can't have a lot of conversations around future behavior of the system as you go about working through your first few sprints. While good Agile teams can do whatever is put in front of them, there is an implicit assumption in project work: if you start out building as securities system you're not going to be changing over to a system to feed and care for circus elephants in the middle of the project. That is, there is a fixed and limited set of nouns and relationships which comprise 90-95% of the problem domain that can easily be discovered simply by talking about the problem. I'm not trying to disparage the author: this is a real problem. I'm just pointing out that mature teams cover the domain fairly completely in an informal fashion (perhaps a few hours of conversation spread out over a week or two) before writing anything. That's not design, that's just understanding the world of the customer. [Insert long rant here about how most programming teams have forgotten or hardly use any sort of analysis techniques] Of course, the best of these teams still run into the same problem down the road, but it should be a pretty long ways down the road. Like years. If not, you probably never really understood what the hell you were doing in the first place. (Not the programming part, the part about fully understanding the user) Type hierarchies can allow for flexibility easily. It's up to the team to spot where flexibility is going to be needed and put it in there. Brittleness is a risk just like any other project risk.
- CodeMage 14y agoI wish we could find a way to make non-programmers understand this. Just because programming doesn't involve moving lots of heavy stuff and putting it together physically, doesn't mean that there are no rules about how software needs to be built. People tend to focus on the happy fact that you actually can change your fundamental assumptions in software without loud, ugly demolition sounds, but they tend to stay blind to the fact that it still involves a lot of work. It's partly our fault, too. We're the ones who have been promising that "this time it'll all work out with this fancy new methodology we've discovered". I've seen a lot of people assume that "agile" means "clients can change their mind as often as they want and we'll take it in stride" and that we'll do so at zero cost.
- carsongross 14y agoAs always: It Depends (tm). Type hierarchies have their place, but can be overused and abused. My typical approach is to be fairly conservative with base classes, and rely on them more for shared implementations rather than polymorphism. Gosu also supports composition (See http://lazygosu.org/ http://lazygosu.org/ search for delegates) for shared implementations, but it is syntactically heavier-weight, even if it can be cleaner. For polymorphism, I'm more inclined to use interfaces. I think there is a place for explicit (java-style) as well as go-style (implicit) interfaces. The real culprit here is overdesign/premature abstraculation: you can go batshit early on in a project with almost any language feature and compromise your flexibility. Broadly, write as little code as possible, balanced with readability (e.g. don't go ape-shit with obscure macros) and using standard idioms, and let the underlying abstractions emerge when they are ready. The older I get, the more I feel like less code is the most important thing by a long shot.
- andrewflnr 14y agoUm, aren't shared implementations exactly what you're not supposed to use inheritance for? According to the Liskov Substitution Principle (http://www.objectmentor.com/resources/articles/lsp.pdf http://www.objectmentor.com/resources/articles/lsp.pdf), if it's not transparently substitutable, it shouldn't be a subclass.
- carsongross 14y agoProbably. But I don't care what the OO academics say: look how they designed JUnit. I find class-based inheritance most useful for reusing base implementations, and interfaces (explicit or implicit) for conceptual encapsulation. I wish Liskov the best of luck in her software writing.
- mattiask 14y agoAs I've become more experienced over the years I've come to believe that the prolification of patterns to "fix" oop problems (dependency injection, immutability, builder patterns, events etc etc) are a symptom of inherent flaws in the object oriented model. It feels like we're bending over backwards to fix a model that promotes lots of problematic designs while not doing much for resolving them (or supporting basic things like concurrency/parallelism). We can never predict all possible problems, or predict the future so no language can be "perfect", but a language could inherently be more agile and flexible. I wish I could say what a better model would be. I'd hazard a guess that its something more based on composition and functional programming than inheritance and classes. Perhaps even metaprogramming and/or code generation For now it seems OOP is the worst paradigm for programming, except all other paradigms of programming
- cageface 14y agoWhat's the alternative though? The minute you try to treat any group of data in a general way this problem arises. The minute you publish any kind of interface you've essentially signed a contract with all client code. All the common solutions to this problem (structural typing, COM-style versioned interfaces etc) create problems of their own. I think it's just a genuinely hard problem.
- mattiask 14y agoSome day there'll probably be a nextgen language that addresses at least some of these issues. One way to resolve some of them today however is code generation. While not appropriate to all kinds of solution I've had some success with building solutions, especially "enterprise solutions", where you model the problem descriptively (in say xml) and then generate the appropriate code. This way you can glue together "architecture" code with "meat code", (ie substantial methods) and have the agility to refactor and regenerate. The bulk of the code is often some kind of yak pattern shaving so you can save quite a lot of time. You're basically designing your own DSL (Domain Specific Language) and then writing the "parser/compiler"
- DeepDuh 14y agoBeat me to this but I like the objective C approach: You can save lots of specialized classes by extending existing ones with categories. And instead of a rigid class structure you usually check the availability of properties at runtime. It might be more verbose and in general more code but the code you have can be quickly adapted and is quite stable. I'd love objective c to become more multiplatform (which is of course the major downside for now).
- Bjartr 14y ago"Or do we find some other, less salubrious way around the problem?" Since salubrious means "good" or "healthy", this statement doesn't make much sense. If you're going to use words your readers are likely going to have to look up, at least use them correctly. EDIT: at least, it doesn't make sense insofar as I understood the intent of the sentence.
- singular 14y agoI meant less salubrious as in more seedy, less wholesome, a usage I've heard before, e.g. 'a less salubrious bar'. The idea was to imply an evil, dirty hack - I was trying to add colour to the post, but perhaps didn't succeed :-) I do try to endeavour to stick to the simplest possible expression, but sometimes still fall foul to the use of a word which I enjoy but in fact reduces clarity.
- ehosca 14y agoits a problem if you don't know your domain ... you have no business designing type hierarchies if you don't have a clue about the domain you are modeling.
- fusiongyro 14y agoThat's kind of a cop-out, don't you think? After all, when we embark on from-scratch coding projects, we must not be perfect experts in the domain or we'd already have code we can use, right? How are we supposed to become domain experts without writing the code? Attend night classes? :) The design the author winds up with has plenty of technical debt, but it's not hard to imagine winding up there, even with partial knowledge of the domain that is constantly increasing.
- eastern2 14y agoThe OP clearly has more than a clue about the domain. If you work in a complex domain for any length of time you realise that domains change. I don't just mean that requirements change but the actual real world domain changes. As it happens, I have been babysitting a large system in this domain since 1996. ISINs were hardly used then, and many types of derivative financial instruments hadn't been invented. Government regulations too keep changing, often bringing into existence completely new data points. 'Knowing a domain' is a meaningless concept in long-lived business system.
- mturmon 14y ago"Government regulations too keep changing" Excellent point. This puts some of the comments above about how agile methods could quickly identify these problems and address them by refactoring into doubt. You could build a very big system, understanding the domain well, and then have a regulation change introduce new issues that you could not have anticipated. Beyond finance, health care would be another setting where this could be important (HIPPA must have caused a lot of hasty alterations).
- cageface 14y agoHow do you design a type hierarchy that incorporates all future change requests? Can I borrow your crystal ball?
- anuraj 14y agoI think one of the fundamental issues here is how the programmer views OO as a programming methodology alone. OO is more a collaboration tool which helps large teams come up with complex functionality. The architect or lead designer comes up with system level abstractions and module contracts. The module designer then comes up with module level abstractions and interfaces. Finally the programmer is supposed to code to the interface given to him. Thus large projects can be managed better as each person knows their roles and responsibilities and system can be thought of as composed of blackboxes. This works only when the architect knows his job and module designers are good. Good programmers often do not make good architects (it is a different thing that often good architects are good programmers too). In OO design comes first, second and third; implementation comes last. This creates a situation where programmers do not have enough work towards the beginning of the project. But as any normal scenario, this text book version works only 80%. Remaining 20% are situations where we do not know the abstractions to begin with or implementation feasibility is questionable. This is where I use the programming resources to do prototyping of 20% functionality while the design is going on in parallel. In cases where abstractions may change, keep them at a very high level and evolve the design over time. By providing hooks to refactor and evolve the design over time, you can future insulate to some extent. As long as every programmer is not forced to think in OO design terms and is given a simple contract of coding to the interface it works. That said good architects are rare and the job requires some experience and expertise in abstract thinking and most programmers do not end up as one.
- achy 14y agoHow can he write such an article, stating that he used C# because he knows it, and not tackle the problem using the main resource for such issues in C# / Java: Interfaces. Using interfaces, you can decouple all of those classes from each other, and never have this issue in the first place. If the stated model is the way he would typically tackle a problem in C# then there are fundamental issues with his choices, something that is not a failing of the Type system.
- madhadron 14y agoApparently most of the readers have missed the point. He says up front that what he's describing doesn't really happen seriously in small pieces of code. The code example is an illustration, one that I thought was very clear. As for a solution? The only purpose of inheritance or subtyping is polymorphism. You may be doing polymorphism in a very roundabout way (if (isa(X)) { ...get a field from X... }), but it's still polymorphism under the hood. There's actually a very good argument against inheritance for polymorphism: you can't straightforwardly write a statically typed, polymorphic max function. You have to introduce generics to the language. That way lies the Standard Template Library and generic functions a la Common Lisp or Dylan (which is a pretty wonderful world). Now, in implementation you may want some of the polymorphisms to be due to the same fields being in the same memory offset in all subtypes, which seems different, but why must it be? Why shouldn't it be a declaration about a family of types? I may have to go play with that...though I think it's equivalent to how it's done in Forth. So much seems to be.
- bunderbunder 14y agoThere are compelling uses for inheritance polymorphism - GUI frameworks are generally examples of this technique put to good use. I suspect that OOP systems would be a lot less likely to go haywire like the author describes if the Liskov Substitution Principle were better-known among programmers. I'd even like to see it baked into a language. Get rid of overriding base methods. Instead the superclass's version is always called, and the subclass is only allowed to tack on some additional code that runs after the base method returns. Yes, returns - the subclass's code shouldn't be allowed any chance to modify the result. It shouldn't be allowed to directly modify non-public fields that belong to the base class, either. I suspect that a language with those kinds of restrictions on inheritance polymorphism would encourage developers to be a lot more thoughtful about how they design class hierarchies. Which they should be, since someone might get stuck with the results of those decisions for decades. Taking the C# example - Microsoft decided to push mutability in collection classes all the way down to the root of the object tree. Which creates a lot of pain for conscientious developers. Mutability isn't just a non-essential feature of most collections, it's also undesirable in a great many cases.
- Scramblejams 14y agoCould someone who understands CLOS well weigh in on how this problem might be approached from that point of view?
- mariusmg 14y agoYeah, because it's so hard NOT to use inheritance.....
- algolicious 14y agoI'm not quite sure what the issue is here. It turns out that the author modeled the domain incorrectly. At least that incorrect model is completely explicit in the code. If it weren't spelled out explicitly, the coupling that the author speaks of would be insidiously spread throughout the code. In order to make the change that the author wants, it's as easy as introducing a new abstract class. In fact, if all the existing code correctly assumes the existence of an Isin, we can create the following set of classes: abstract class BaseSecurity { public string Description { get; set; } public Exchange Exchange { get; set; } } then modify Security to derive from BaseSecurity: abstract class Security : BaseSecurity { public string Isin { get; set; } } Then you are done, except for two issues: first is that any serialized data needs to be regenerated, and second is that you can't trade BaseSecurities. However, this trading functionality can be written separately without disturbing the existing ecosystem of software. This is what your type hierarchy buys you. On the other hand, if we insist that this is not correct, and Security should have Isin removed, then we can add a new PhysicalSecurity between Security and the various implementations, and Stock/Bond/Trade can inherit from PhysicalSecurity. In that case, the problem is that a lot of code was written with the incorrect assumption that an Isin exists in every security. Now we have to take a step back and ask how to fix that code on a case by case basis. No matter what language you use, it's always possible to write bad code with incorrect assumptions, and in that case you must pay the price. Hopefully you would be clear with your client on the delays required. Now we can ask ourselves how a static language treats the situation differently than a dynamic language. The author seems to think a dynamic language would help, providing only praise in his description of them. With a static language, we can simply remove Isin from the definition of Option. This will cause a lot of compilation failures. However, every place where there is a compilation failure is a place in the code which had an incorrect assumption. Each of these incorrect assumptions must be considered individually. After all, this represents the model for a trading system, and any bugs would likely result in severe financial consequences. In a dynamic language, the definition could be changed, but there would not be any inherent mechanism to catch the now-incorrect calls. Instead, we would just get the NullPointerExceptions that the author complains about and which jeopardize the viability of the financial trading system. Perhaps the coders would have written beautiful unit tests that would help, but that could be the case in any static language as well. Of course, it's also possible that the coders would have created trivial unit tests or no tests at all. In any case, I see this situation as a win for static type systems rather than a loss.
- pnathan 14y agoI spent a few years using type hierarchies intensely in the early 00s and found the experience excruciatingly bad. The crystalline structure of your types quickly shatters on the shoals of reality and you are left taping the pieces together. After a particularly bad experience I generally stopped writing OO beyond simple structs. Around 2010 I started reading rpg's writings on Lisp and software development; that opened my thoughts to a different thought process of how to design software with objects that I haven't really finished working out. I do agree with you: the C++ modality of inheritance doesn't really work in many cases. It's a tool, but a tool that works badly often. I think a more CLOS or Haskellian viewpoint will yield better results in the long run.
- joeyh 14y agoHere I've translated the example to haskell: data Exchange = Exchange { bic :: String, name :: String } data Security = Security { description :: String, exchange :: Exchange, isin :: String } data Stock = Stock { security :: Security } data Bond = Bond { security :: Security, expiry :: EpochTime } data Trade = Trade { price :: Decimal, quantity :: Decimal, security :: Security } Now to add Option: data Option = Option { security :: Security, call :: Bool, lotSize :: Decimal, maturity :: EpochTime, strikePrice :: Decimal } And in the example, the problem is that the Option uses Security, which has an isin, which doesn't make sense for Option. In haskell, this is a sort of problem which is typically fixed by adjusting the data types. There are many ways they could be changed, some will model the domain better than others. Let's just make the same quick fix used in the example, of allowing isin to not be set: data Security = Security { description :: String, exchange :: Exchange, isin :: Maybe String } This means that isin is Nothing or Just a String. As soon as this change is made, every place in the program that directly accessed the isin will fail to compile. Fixing the compilation errors will involve adding a case to handle isin-less Securities. - foo (Security { isin = i }) = + foo (Security { isin = Just i }) = ... + foo (Security { isin = Nothing }) = ... The code does become somewhat ugly with these cases, but you know every case has been covered, and that it will work. Maybe later it's decided to go back and fix it to use the separation between physical and derivative securities that was originally considered but not done due to lack of time. It could then look like this: data Security = PhysicalSecurity { description :: String, exchange :: Exchange, isin :: String } | DerivativeSecurity { description :: String, exchange :: Exchange } Again this type change would drive a pass through the code, fixing it up to compile. foo (PhysicalSecurity { isin = i }) = ... foo (DerivativeSecurity {}) = ... Again you'll know when you're done because the program will successfully compile. In this case, splitting the data type seems to have led to better, clearer code. It might be worthwhile to factor out a helper type to simplify the Security type: data SecurityBase = SecurityBase { description :: String, exchange :: Exchange } data Security = PhysicalSecurity { base :: SecurityBase, isin :: String } | DerivativeSecurity { base :: SecurityBase } Although you may find this complicates other things as you "follow the types" and change the code to match. There are surely other approaches; so far this has stuck with simple data types, but typeclasses could also be used. You may want to constrain Bonds to using a PhysicalSecurity, and Options to using a DerivativeSecurity, and there are various ways that could be enforced. And so on. What was surprising to me coming to haskell from a background in loosely typed languages (and lowlevel langs like C) is that the types are not a straightjacket that is set in stone from the start, but ebb and flow as you refine your understanding of the problem domain. What well chosen types in haskell do constrain is the mistakes you want to be prevented from making. These days if I find myself repeatedly making a mistake in my code, I adjust the types to prevent that sort of mistake in the future. --- Side note: The above code will not compile as written, because it exposes an annoying problem in haskell's record syntax. There are several fields named "security" that conflict with one-another. This is typically dealt with by using ugly field names (stockSecurity, bondSecurity, tradeSecurity, optionSecurity), or more advanced things like lenses, or by putting the data types in separate modules and using module namespacing.
- aphyr 14y agoI'm in favor of decoupling data structure from interface entirely, via records + protocols. Hierarchies have their place, but ultimately can't deal with cross-cutting concerns. Mixins with structural typing is one way to approach the problem, but for formal contracts I prefer Clojure's approach: http://www.ibm.com/developerworks/java/library/j-clojure-protocols/ http://www.ibm.com/developerworks/java/library/j-clojure-pro...
- stephenjudkins 14y agoI'm surprised he didn't mention typeclasses as a solution to this general problem. In many cases it's an unambiguously better solution than inheritance. A particular strength is that typeclasses can be easily defined or overridden at call-sites as easily as at where data types are defined. OOP forces an uncomfortably close complecting of data and operations on that data, leading to the difficulties enumerated in this blog post.
- jdlshore 14y agoI commented on the author's blog, but I thought people here might be interested as well: I agree with Chris Parnin (in the comments of the author's blog)--this isn't a type hierarchy problem. It's an incremental design problem. It's true that inheritance should be used with caution, and this example (intentionally) overuses it, but the deeper problem seems to be that the author doesn't understand how refactoring and incremental design work. Let's stipulate that your initial guesses about a domain will almost always be wrong. In this example, the author assumed that all securities will have an Isin, but it turns out they don't. Options are a type of security that don't have Isin. One solution is to hack Option as a subtype of Security. As the author shows, this leads to a big mess. A much better solution is to refactor as soon as you notice that the domain is wrong. Here's how it works: Step 1: Notice that Options are securities, but they don't have Isins. Observe that the domain model is wrong. Smack yourself on the forehead. Step 2: Realize that Security is not in fact representative of all Securities. Rename it IdentifiedSecurity (or IsinSecurity, if you prefer). This is an automated refactoring in C# and Java, and will automatically rename all uses of the class as well. Step 3: Create a new superclass called Security and move Description and Exchange to that superclass, if desired. Step 4: Create Option as a subclass of the new Security superclass. Step 5: Enjoy your improved design. Some parts of the application (such as Trade) will be too conservative and use IdentifiedSecurity when they could use Security; those are easily fixed on a case-by-case basis as needed. For more about incremental design, see Martin Fowler's _Refactoring,_ Joshua Kerievsky's _Refactoring to Patterns,_ or the "Incremental Design" chapter of my book (http://jamesshore.com/Agile-Book/incremental_design.html http://jamesshore.com/Agile-Book/incremental_design.html). You can also see me aggressively apply incremental design in my Let's Play TDD screencast, here: http://jamesshore.com/Blog/Lets-Play http://jamesshore.com/Blog/Lets-Play .
- adavies42 14y agoi had a great experience with OO design in an internship in grad school. i was sole coder on a java project, a networked whiteboard app, and i had the time and flexibility to scrap the whole thing halfway through and rewrite it from scratch once i had some clue about the problem domain. the best thing is that since the main functionality was a vector graphics editor, i actually got to implement one of the classic blackboard OO examples in practice, and see how it really worked out: Interface Shape AbstractShape implements Shape TwoPointShape extends AbstractShape Rectangle extends TwoPointShape Ellipse extends TwoPointShape Line extends TwoPointShape Arrow extends Line Text extends AbstractShape Icon extends AbstractShape Raster extends AbstractShape (or something like that) obviously this was the product of a lot of refactoring (i think the TwoPointShape "insight" came to me relatively late in the project) of course, i ended up spending most of my time chasing ConcurrentModificationException and jumping back and forth between two or three boxes tracking down network synchronization bugs (client A begins moving an icon, client B begins moving the same icon, client C begins moving the same icon, client B releases the mouse, client C releases the mouse, client A releases the mouse: what happens?), but that taught valuable lessons too: specifically, all they really ever teach in school is what i call the data paradigm: your app starts, takes input, does stuff, stops. gui- and network-event-based programs, and how to design, test, and debug them, barely come up. of course once i got into industry, i got turned on to array programming and never looked back :-)
- darklajid 14y agoI stopped when I read this: Functional programming is enjoying a great upswing in interest and popularity these days. I wonder whether the stronger type systems of these languages... Functional programming == stronger type system? I thought I can use JS in a functional way without a lot of safety nets. Clojure isn't Scala. Is he right? What am I missing?
- ufo 14y agoWhoever wrote that was obviously referencing ML/Haskell