How (not) to write Factorial in Java
chaosinmotion.com
chaosinmotion.com
This is not fair; Java has moved beyond this. In modern best-practices Java it is no longer necessary or even possible to write big piles of gratuitous OO spaghetti. Today, experienced Java programmers build their applications on top of huge, industry-standard, highly polished piles of architectural bullshit, wrestling with which leaves them with no time to write any code of their own.
It's not worth it.
To the extent Java correlates with bad software, IMHO the causation is more likely to be that the environments (in the vein of clueless corporate management, waterfall, http://www.halfarsedagilemanifesto.org/) are more likely to pick Java for their development.
That's the point. Java, it seems, make it tempting to "overarchitect" applications into excessively generic and modular beasts early in their lives when a much simpler implementation would be perfectly adequate and much simpler to extend and maintain.
I wonder if I will actually ever use one of those factories again in that app.
But you know what? Once we've established that you can write bad code in any language.. I'd prefer to deal with bad code in Java than almost any other language I can think of (and I've seen a fair bit). Maybe Python wins out, but I'd rather debug bad Java code than bad C++ or bad Ruby for sure.
The nice thing about Haskell is that it's really, really hard to write things that accidentally ignore purity, which eliminates a whole class of errors and bad designs. The type system only helps you if you let it, though.
I've looked at a friend's Haskell program for Huffman encoding, that used raw recursion in a undisciplined way instead of abstracting the control flow out into, say, filters, folds and maps. That was the closed I ever got to ugly Haskell.
Gut feeling says they'd be even more productive. Terrifyingly so, in fact. The sad fact is, though, that we'll never be able to conclusively prove a statement like that.
Every book I've read on Java shows even the "Circle inherits from Shape" examples to be ridiculously overarchitected and encapsulated (I am aware of why they are this way for illustrative purposes, however).
But its a good idea to do some of this stuff where and WHEN its appropriate. You can design a space shuttle to go to the grocery store doesn't mean you should.
I have seen my fare share of useless hard to understand and over engineered code in C, Groovy, Java and Lisp
A good book about this by Joshua Kerievsky is Refactoring To Patterns he talks exactly about this, the dangers of using patterns and the dangers of not (yes he uses Java in his examples)
And indeed I also have a feeling that premature optimization and general architectual astronaucy are deemed very highly by Java folks.
Don't get me wrong - I think that JVM is a marvel. I could also find greater joy in writing COBOL for a living. (at least my coworkers wouldn't be rambling constantly about stuff that is wrong and don't matter).
And that is also why the antarctic has its name...because there are no bears there, just penguins.
In python, it would be 15 lines, and simple. No utils, factories, algorithms or singletons.
I imagine it could be done in more reasonable java than this, but could it really be done cleanly and compactly?
When we see problems like this, it's right to ask questions about what the language design encourages its programmers to do. Additonally, languages have "personality" just like people do, and those personalities evolve with the user base of the language as they adapt to the language's environment.
The question of whether this type of code is more common in Java than say Python or Perl is empirical, and my gut says it is. Java is the commoditized, corporate language par excellence. The features it implements and the way it implements them have attracted the kinds of programmers who would write code like that. That's not to say that Java code forces its users to write code that is so inelegant. That's just to say that it creates incentives that often push toward ugly code. C++ does that too. On the other side, Python (to name one example) seems to set up its environment in such a way as to encourage pretty code.
There's no clear-cut good or bad here, only interesting trends in the code produced in various languages.
http://www.nhplace.com/kent/PS/Lambda.html
Languages are more than just syntax and semantics. There's also a whole culture that grows up around the language, a set of "best practices" and common idioms. And the one that's grown up around Java seems to value abstraction and design patterns and convoluted, overgeneralized solutions.
This doesn't apply to all Java code, of course. You can write lean Java just like you'd write lean C or lean Python. But a non-trivial amount of the Java jobs out there expect you to layer frameworks on top of frameworks and jump through an amazing number of hoops just to get something done.
The time to use it is when your business users can't agree -- one says do it that way, another says do it this way. Or they say it's one way, but they're not sure if it's going to change or not. That's a perfect time to cover both bases.
A tiny bit of abstraction applied well can save immense heartaches down the road. A bunch of abstraction applied poorly can make your life miserable. Part of "growing up" as an OOP developer is learning what to do when.
Also, this is a good example of why founders and startups use so little abstraction -- nobody knows what the problem is or what's going to change. It's all complete guessing. So might as well get something out the door today instead of playing mind games about what might happen or not based on your imagination of what a user might be. (This is also the reason functional programming maps so much better to startups: you don't have a spec. You just want one thing to go in and another thing to come out. If that isn't what people want, you have to iterate. Completely different paradigm than working in a business environment where somebody gives you ten bucks to go make well-defined change in system X)
Making things flexible may not sound pretty at the start. But ending up changing 3 apparently completely separate systems to accomodate the fact that you changed the version of some software and the output format needs to be different (hey - why would we do immediate representation, output's not going to change).
It looks silly in the example, because it's a simple operation. Now substitute 'factorial()', for 'provision_customer_config()'. You WILL change the backend at some point. You WILL change the endpoint at some point. You WILL change the way the function works completely, but will have to retain the previous procedure for existing users. It's a when not an if question. There should be a saying similar to that about people doing backups: there are 2 types of programmers - those who write modular code and whose who will.
I think this is the real problem with this particular example - because the author wanted to make this into an anti-pattern joke, he didn't actually think about what a Real Programmer actually trying to make good use of the extra keystrokes would do in this situation.
The "requirements" that he's using to build up this bloat appear to be:
1) The result might be really, really, big, so no factorial function worth a damn should be limited to a normal int.
2) We might want to swap out implementations of our factorial function, for instance, to cache intermediate results, to use a loop, to use recursion, etc.
3) We should be able to add new implementations without modifying class files already written, and we should be able to select implementations at runtime.
Fine, then. These could be 100% realistic requirements if we were talking about some more substantial or important piece of machinery than a factorial function, so we really might have to satisfy these things, in which case the three-liner would be completely and utterly unacceptable.
So let's say we're in that situation, then. A good Java developer still would never write the code the way it came out in that post, because the abstraction doesn't go far enough, and it's obvious. Almost every time he writes "FactorialAlgorithm", we could just as well replace that with "Algorithm" (or maybe better, "BigIntegerAlgorithm"), and now instead of building up a whole bunch of architecture just to support one stupid algorithm, we've spent a bit of effort to make all of our (big integer) algorithms swappable. That memoization stuff could almost immediately be refactored up and out of the individual implementations, so it would take but a single method call or flag to add that ability to an algorithm, and so on.
I'm not saying that the particular architecture that gets built up to do this stuff is necessarily pretty, that Java-style OOP is an optimal (ha!) or even passably efficient way to achieve these constraints, or that you should do these types of abstractions before you know you absolutely have to. By all means, if you don't have any other requirements on the table, write the naive factorial, FFS! But when you've got to abstract things, rule #1 is to make sure you pull everything "up" far enough, otherwise you're never going to extract enough value out of the added complexity to keep yourself sane.
It may not be true in all situations, but I think this benchmark is a good one, as it accounts for the environment of the developer. If the code is only used once, it isn't worth it to abstract it. Once you have two implementations and need to add a third, you may realize they have commonalities and so abstracting and refactoring make sense.
No XML, SOAP, EJBs, Connectors, Spring.... it barely qualifies as a (obfuscated) Java architecture.
http://code.google.com/p/fizzbuzz/
http://code.google.com/p/fizzbuzz/source/browse/#svn%2Ftrunk...
This smacks of the oft-ridiculed Java AbstractFactoryFactoryInterface. But let me put it bluntly: AbstractFactoryFactoryInterface's are how you write real, modular software–not little fart applications.
http://magicscalingsprinkles.wordpress.com/2010/02/08/why-i-...
[N.B. I'm not saying there isn't a lot of truth in the factorial article, it's just you have to know which challenges just need a one-liner function and which require an AbstractFactoryFactoryInterface]
All we've done here is forced the programmer to adhere to some ridiculous interface which requires just as much understanding of the internals of the code as overriding the method in the first place. You also end up with hard-to-understand naming schemes, which leads to two points of confusion:
1. Which one of the provided query factories does what I actually want? 2. If I need to write one myself, what would I call it?
PerQueryTimingOutQueryFactory? You've added more parts to that name than would have been included in an option to the query function! Oh, and it still doesn't include the information necessary to run, because it folded the timeout specification down into a HashMap. One more thing for the end-programmer to understand.
There are good places to use dependency injection. There are also places where Factories are exactly the pattern you need. But in most dynamic languages, you can achieve these effects implicitly through inheritance and closures, and it cuts down the complexity of the code immensely with only a minor cost to reusability.
This guy works for Twitter. They control Querulous. He could have just added a :timeout argument which takes an number or a function returning a number to the query method, but now you have to understand all this additional architecture to get anything done.
And thus you demonstrate using Java for prolonged periods may cause brain damage.
I'm not sure how to actually prove that, though.
After being exposed to Java for prolonged periods of time, my Python programs became classes with a main method that was called in an ifmain condition...
I was participating in an obfuscated code contest a while back (making a simple command line multi-function calculator), and since I knew I couldn't compete on line noise I decided to try obfuscation through architecture. Somewhere around writing a RightSideOperandInterface (so I could catch division by 0), it managed to get into my head that it was actually more robust and extensible than doing it any other way. I came to my senses slightly after submitting it.
Now, if the code was packaged as an API with very good documentation, then this might be a moot point. In my personal experience, this has rarely been the case.
The problem is that the people who come up with these designs are the ones that consider refactoring bad/hard/risky. They know very well that they need a hammer, but 200 what ifs later, they have this monster-factory, instead of just getting a bloody hammer, then, when they also happen to have a screwdriver and a saw, they build a toolshed, and refactor.
You can have a LoggingQuery(TimeoutQuery(Query())) or a EverythingQuery(timeout = true, logging = true).
From what I've seen, @nk loves composition.
Do you say:
if (customerID = 328281) {
...
} else {
...
}
Or do you go ahead and build the architecture that lets you specify which class or method to run per customer? The situation does come up often enough to have to make a decision.I'm working on a project which has tended to say "if (customerID = ..." in the past, but I decided to move to the more configurable/pluggable approach. I'm actually not sure if I made the right decision.
If your project makes one exception, a whole bunch more are likely to follow.
0 customers asked for it: Don't abstract it.
1 customer asked for it: Corner-case it.
2 customers asked for it: Strategy pattern.
3 customers asked for variations: Config option.
If you find that "1 customer" (being a series of disparate customers) all ask for various minor tweaks to the way something works, then before you have half-a-dozen disparate forks in your logic you probably do want to pull that out and use a strategy pattern where applicable. It doesn't take many clever sidesteps in a clean algorithm to make a nightmarish scenario later on.
Save yourself the time and bother to dig back in again and solve it at least for the foreseeable future with less overhead than what it would take to tackle the same task again, typically getting in to do the job takes at least as long as doing the job in situations like that (just to make sure you didn't break anything by accident, test the works and so on).
If the overhead is constant and a 'small' fix like that takes 5 instead of 4 hours including all the extras then that's a trade-off worth making. Unless of course you're really under pressure.
It's just a verbose way to perform the same abstraction. Use it only if function pointers are unavailable or considered unsafe in your programming language.
Listen to 37Signals when they say that it's OK for your customers to outgrow your software. http://37signals.com/svn/archives2/growing_in_vs_growing_out...
This is more of a rant against "developers" with "really bad habits" than it is against the language.
http://www.edge.org/3rd_culture/boroditsky09/boroditsky09_in...
Boroditsky, L. (2003). Linguistic relativity. In L. Nadel (Ed.), Encyclopedia of cognitive science (pp. 917–922). London, England: Macmillan.
Boroditsky, L. (2001). Does language shape thought? English and Mandarin speakers' conceptions of time. Cognitive Psychology, 43(1), 1–22.
To the linguistics question I just had a discussion about this with my boss (who's a linguist by training, currently on leave from his PH.d which has something to do with Greek, he's also been programming a lot longer than I have). One of the points he had was that the Sapir–Whorf hypothesis doesn't just say speaking German affects how you think in German, it affects how one thinks as a whole. To that we're both a bit skeptical, being a programmer probably affects ones cognition (or ones cognition causes one to be predisposed to becoming a programmer, whichever!), but the specific language one uses... probably not. On the other hand, within programming it's clear that different languages (or at least their communities) have different personalities. A good example of this is Python and Ruby. Semantically these languages are remarkably similar (in the context of all programming languages), however their communities have seized upon the small differences and as a result the cultures (accepted best practices and such) are fairly different. Another good example of this is what are known as "design patterns", which were originally supposed to be a language-neutral way of expressing how code could be architected, however in practice some of these really make no sense with certain languages, because e.g. the language has builtin-features which obviate the need for such things.
Java doesn't force you to over-architect solutions. Wrapping everything in classes doesn't make your code over-architected by definition.
For Java, the culture is heavily towards writing "solutions" like the overengineered mess in TFA. For python, it's different. Same for Ruby or PHP or .NET or...
Note that for none of those languages are you "forced" to act like the culture says you probably will. Nothing in Ruby forces you to unit-test, for instance, but the culture leans so heavily that way that it's more shocking when you find a team not doing it.
Maybe this "pattern" is less indicative of someone who comes from a "Java" background than it is someone who comes from an "enterprisey" background.
It just so happens that the overlap between the two is huge.
I hate to always be the defender of Java but it irks me to see articles that claim to lambast Java but really are about this so-called "culture" that has popped up.
OOP seems to be "difficult", so there are lots of "tutorials" around that "teach" OOP by introducing and combining all those brainless constructs, without teaching common sense. what you get is code monkeys who believe that this is decent source code.
every code base should have at least one person dedicated to simplifying it and questioning every design decision, so all this "just in case" abstraction gets the knife.
Pretty much everything in our ostensibly OO system could be static methods and it would work just fine. Objects rarely have instance variables, or if they do they are just used to set up a method call. Object lifetimes are measured in single-digit lines of code, and messaging between them is extremely rare.
But that's what people have been taught.
It could be Java (of course), C# (just about as likely), or VB (when they get the idea to use objects), or Python (yes, I've seen it).
OOP is only "difficult" if you try to learn it using a language that makes it difficult. Sadly, Java is one such language.
In Java, if I think I might need a pluggable architecture later, I'm tempted to go ahead and make it from the start, since my object model will need to support it.
Anyone who programs like that is a bad programmer. That's the fact. It's nothing to do with the language being used.
The biggest complaint I have with many Java developers is that they develop a whole bunch of really bad habits.
A programming language is just a tool. You can use it as you wish to. There is absolutely nothing inherant in Java that suggests you should create a mess of Singletons Factories etc. And if you're a good programmer - you won't.
Our job as 'hackers' is to not do things the 'accepted way'. To think outside the box. To go against 'accepted wisdom'. That's kinda the point.
So just because some idiot in a cube somewhere creates 18 Factory classes an hour in his IDE, and somehow that's become "accepted wisdom" about how Java should be used, it doesn't have any bearing whatsoever on how you use it. I'd like to think we're all better programmers than that here.
Yeah it can, why can't I ignore culture?
I download a tool, and use it as I wish to within my company.
In practice, it's very hard to do. Try living in a foreign country for a time.
So "Java the language" to me is a useful tool. It's in no way connected to "Java the culture" or "Enterprise Java" which are both total garbage.
If I was evaluating somebody's python, I would consider that a significant mark against it. I would question how well they understood the language and what hidden gems I would find as a result.
If you are able to grok code at a higher level, that is awesome. Recognize that the majority of engineers aren't in that bucket and that style does matter.
If it didn't, why would anybody have coding guidelines?
I've dealt with enough co-workers to know this isn't hypothetical. There's a slow backlash against this kind of thinking, but it's slow. 5 years ago, I was told that singletons and utility classes were always evil because they were "unmaintainable.)
On the other hand, I've been in situations where the factory/pluggable architecture was the right decision, especially in consultingware where you build the product for your first client, then you go after an RFP that's "pretty close" but don't want to end up maintaining two, and three, and seven code bases which often have the same bugs. That's the problem this pluggable architecture solves.
If you have 7 clients paying you a million bucks to have exactly what they want (out of a market of... say 25-100) and 50k a year in maintenance, this makes sense.
Most of the time, though, YAGNI.
Or work with people who don't really know what they're doing. Or have to maintain or modify their code.
False. If I'm starting a company around a product built with Rails, it behooves me to stick to cultural norms (in Rails' case: TDD/BDD, Continuous Integration, so forth) because otherwise it's difficult to find good developers that will work on the product.
Our job as 'hackers' is to not do things the 'accepted way'. To think outside the box. To go against 'accepted wisdom'. That's kinda the point.
That's not the point, it's just usually a means to an end. As you say, doing effective work in a Java shop requires either writing better Java, or using a language like Clojure. But in some cases it's faster to go with accepted wisdom rather than combat it, even if we disagree. In some cases, shockingly, the accepted wisdom is beneficial.
Ignoring cultures around languages out of hand is arrogant, brash, and probably wrong. If after you've examined the culture you find you don't agree, that's completely different. You can only think outside the box once you've examined the box.
I point this out, because I've been bitten by not checking for this in people only to find they have no idea what they did right that one time, and most everything else they've done is just crap.
This heavyweight Java stuff is everywhere. DriverManager. Security SPIs. Hibernate. Spring. EJBs. JNDI. You cannot do enterprise work in Java without running into this culture. And just like poisonous people will poison you, so do poisonous design patterns. I was hacking on a Clojure project, and writing a data access layer, and without thinking started writing a factory-based indirection layer for abstraction purposes before realizing I didn't need one...
Couldn't they just use memorization by passing a hash map in a tail-recursive method and save on a few hundred lines of code?
I'm guessing some other people were thinking "Oh how true! Java is such a bad language" which I think is a shame. Not particularly to Java, but to themselves.
If you think it's Java's fault when you write utter crap in it, you're not a very good programmer to start with. Languages don't force people to write bad code.
I've coded professionally in static and dynamic languages, and enjoyed myself in both worlds for different reasons (I guess I just enjoy coding on a basic level). I recently came back to Java after a multi-year hiatus, and suddenly found myself back in a world where people tend to program in patterns such as the above. The funny part is, my experience has ultimately been positive.
I was recently in the position of having to patch and modify a very large Java library (written by some well respected Google guys) because I needed it to do something different from its original intent. The OP would have hated this library--when I cracked it open I found innumerable factories, strategies, observers, visitors, etc. etc. After studying the library for a while (which was frustrating because as a developer I wanted to just dive in and write some code), I started to realize that the patterns were well-utilized and built with anticipation of expansion. In the end, I was able to inject my code into the midst of the patterns at some key strategic points and make fundamental changes to the library. Most importantly, I did so feeling like I had the implicit blessing of the developers, and that the general algorithmic flow of the library was not impacted.
What I've found after my return to Java is that the language is so boringly, stultifyingly structured that an amazing thing happens--you can actually weave together an application with thirty dependencies on other libraries and have it work. Libraries aren't walking over eachother and behave in predictable fashions. Developers aren't dynamically injecting code into core libraries because they can't.
Now, the library I was patching was the kind of library that deals with real world server-side complexity (managing data across multiple stores, with support for testing environments, etc.). That's where these types of patterns shine. They don't shine in examples like the OP's. If a developer in my organization always wrote code like that, I would hope they would be let go. But if they wrote code like that where it was needed and useful, I would be quite happy with them! Conversely, if a developer thought such patterns were never necessary and were never useful, I would think they needed to learn their craft a bit more.
In the end, it's all about pragmatism and experience. Over the years I've been bitten for over abstracting and under abstracting. Over the years you develop a weather sense for how extensible a particular piece of code needs to be.
Bingo. It's easy to sit back and say "never guess about extensibility that might be needed in the future." But I posit that there are degrees of guesswork; that is to say, there's a difference between a wild assed guess with no basis at all, and a guess based on years of experience solving similar problems in a related domain. Experience counts for something, and part of that is noticing familiar patterns (not "design patterns" necessarily, just patterns in the general) that tend to crop up in certain situations. I think making some speculative decisions based on the latter form of guesswork can be a net positive more often than not.
Of course I don't have scientific evidence to back that up, but I believe it very strongly based on my own experience.
[1] Whole thing here: http://www.joelonsoftware.com/articles/fog0000000018.html
...it does not allow us to select the algorithm we wish to use at runtime
I'm an embedded programmer so this kind of thinking is alien to me. But really, everyone plans for runtime configurability but is it really needed that often?
It's very common to invoke different algithms based on specific instances of the data. To give a baby example, its typical that one uses a different sorting algorithm as a function of size and data distribution and key type.
Theres a popular fft package, FFTW, that is based on this as well.
The issue I have is articles like this always try to make things stupidly configurable and general - like "I want to be able to hot swap this algorithm at runtime".
http://discuss.joelonsoftware.com/default.asp?joel.3.219431....
The worst part is when the layer of abstraction is justified as an intervention against potential technical debt: a small cost now to avoid possible interest payments in the future. When, in reality, the small cost has to be paid over and over, even if it turns out that the requirements never change in the particular direction that the abstraction anticipates.
Occasionally the abstraction does actually provide enough benefits to outweigh its future costs, but failure to recognize the existence of those costs leads to poor decisions.
The amount of superstructure piled on top of a simple thing in concept is making programming much less accessible than it needs to be. By the time you understand the scaffolding you've lost sight of the problem.
The clojure example elsewhere in this thread it a really nice breath of fresh air, unfortunately it still needs all of java to run, and the number of times I've run in to obscure classpath issues and runtime problems are large enough to remind me that clojure is not entirely free from this sort of heritage either. That does not reflect on the language, but clojure has enough java roots that some of the issues are quite familiar.
Just look at the horrible mess that is ASP.NET assembly loading: you've got the bin/ directory, Temporary ASP.NET Files, and the Global Assembly Cache all competing when you load libraries.
In Javaland, we at least have Maven :)
is your friend. I've used that many times for stuff that I need to deploy across different releases of the same OS that as a rule have their devtools removed for security reasons.
My development system is typically geared towards desktop use (ubuntu atm), the servers run either redhat, debian or some other flavor and the static trick gives you a slightly bigger executable but the upside is that it is simply drop in and go.
Calculating factorials (pretending for a moment that it is occasionally worthwhile thing to do) is only ever 2 or 3 lines and it makes no sense to create a flexible implementation, especially if it is also has to be made efficient.
On the otherhand, what about a templating engine? How much string concatanation do you have to do before you can justify using a framework? Now obviously some languages have it built in, e.g. in groovy you can reference variables in scope within strings. This is all very well, and is definitely a nice feature, but ... the abstraction remains more leaky than a dedicated templating engine. In line templates are inextricably linked to the code and this has its disadvantages.
When there are clean conceptual breaks to be made in code it is often desirable to do so. I believe it is for this fundamental reason that lisp has not taken off because although it is very powerful, it discourages the creation/maintenance of library code, but in fact high quality library code is better in many situations than just using powerful language features.
"The Evolution of a Haskell programmer" is brilliant. Funny and insightful at the same time. Make sure to read the bottom part: "But seriously, folks, ..."
Really though, I think this is more related to culture that Java (and many Java books) tends to nurture. Anybody that's read a fair share of "Enterprise" Java code has seen FooBarFactoryFactoryFactoryImpl classes.
Hopefully as dynamic languages on the JVM become more popular, this will change. But I find many "Enterprise" developers aren't really interested enough to look outside the box.
"jRuby? I think I've heard of that, what's wrong with Java?"
"It's not a real programming language unless it's statically typed."
(defn factorial [n] (reduce * (range 1 (inc n))))
It's almost criminal how simple it is, yet it's pretty correct. It works for int, float, BigInteger, etc., treats negatives as 0, and throws on non-numeric types.(Edit: needed an inc in there)
In Scheme you could even write DEFN, again, one line.
[Edit:
Not RANGE you don't. Let's see how long it takes to get it under 5 lines]
Actually there might be an interesting contest, cross-language code golf.
f:{*/1+!x}
Which could be paraphrased as "product fold 1 plus [array of numbers 0 .. x-1]", or "(reduce #'* (1+ (upto x))". f=:!
f=:*/1+i. NB. included implementation because it's not a built in, but is still funny factorial n = product [1..n]
Also polymorphic to all of those types and results in 1 for negatives.If you want the polymorphic "memoizing version":
factorials = scanl (*) 1 [0..]
factorial = (factorials !!) factorial:{prd 1+til x}Actually it is incorrect for floats.
For non integer types, the factorial is given by the gamma function [1]. So, the factorial of 3.5 is Gamma(2.5) which is 3.3325 [2] not 6 as your function suggests.
[1] http://en.wikipedia.org/wiki/Gamma_function [2] http://www.wolframalpha.com/input/?i=gamma%283.5%29
Then see what excuses come forth when you suggest Haskell.
Haskell is lazy while ML is strict, and Ocaml adds "OOP" to ML so it's kosher.
I'm not trying to troll, but are there any Java programmers who don't write code like this? I've never seen "real" Java code, which is to say production code, but every time I had to work with Java in school it was pretty much described by the above quote; and what's more, the libraries I had to interact with often made me feel like I was forced to do the same thing.
import javax.swing.JFrame;
import javax.swing.JLabel;
public class HelloWorldSwing {
public static void main(String[] args) {
JFrame frame = new JFrame("HelloWorldSwing");
final JLabel label = new JLabel("Hello World");
frame.getContentPane().add(label);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}
Hello world in Shoes: Shoes.app do
para "Hello, world"
end
Hello world for Hibernate: (from here: http://www.javaworld.com/javaworld/jw-10-2004/jw-1018-hibern...) package hello;
public class Message {
private Long id;
private String text;
private Message nextMessage;
private Message() {}
public Message(String text) {
this.text = text;
}
public Long getId() {
return id;
}
private void setId(Long id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Message getNextMessage() {
return nextMessage;
}
public void setNextMessage(Message nextMessage) {
this.nextMessage = nextMessage;
}
}
Session session = getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
Message message = new Message("Hello World");
session.save(message);
tx.commit();
session.close();
The equivalent in ActiveRecord: class Message < ActiveRecord::Base
end
message = Message.new(:text => "Hello, world")
message.save
You have to set up your schema in both, so I've left that out.These are just the first two that came to my head. I ditched Java so long ago that I'm sure a few hundred more have come up.
Priceless :)
Here's the thing. Most code that is written in the world is not difficult code. It's pushing data between clients, databases, and consumers. Wrap that shit up in a web interface. Put a transaction log on top of it. Whatever. It's not hard. But some idiot with an MS in CS has to justify their existence. This should be a solved problem by now and we get every Tom, Dick, and BS fucking CS trying to come up with their own solution, complete with articles and white papers on how wrong you are for not doing it their way.
I'm sick of the judgmental nature of it all. There is a common phrase in my current office--a certain bit of buggy code is "Doing it Wrong" (and yes, the capitalization happens). In the cases I've seen there, there was certainly a wrongness to the code, but there is no "right" solution, and the proposed fixes weren't any more "right" than the original, they were just politically favored patterns.
I need to get out of programming because I hate programmers. The stereotypical programmer is a culture fascist. He sees one way and only one way.
Math.pow(z/Math.E, z)
should be:
Math.pow(tmp2/Math.E, z)
So from this I conclude that there are two types of java programmers: those that over-architect their solutions, and those that don't even both to test the code they put in blog posts ;-)
I know he would have caught these bugs had he needed to implement these for a real system. But there is nevertheless an important point here. He is 100% right that over-architecting is a bad idea, and that correctness is more important than architecture. But complaining about a factorial function not handling negative numbers, and then "fixing" the problem by implementing the Gamma function, is doing the same thing all over again. It's really easy to fix the factorial implementation to do something reasonable for negative numbers; it's much harder to implement a Gamma function that works for all input. Why bother unless you really have to?
We only use one implementation of it. Ever.
From that context, the factory function makes this article a great example of over designing. And certainly in some senses the point of the article is quite accurate.
However, there exists another less common context for the factorial function--serious usage of the function. Not as an introductory example to the language, but as the basis of actual software that has purpose. In this context questions like memory usage, processing power, and even mathematical precision come in to play. Is BigInt large enough for the required use? Maybe you need to use a class based number type capable of having a limitless value. Perhaps you are targeting a distributed processing system, or a single super computer. Will your computing have memory limitations, processing limitations? Suddenly, when considering real applications for the factorial function, all those satirical over-design considerations are now principal necessities that you'd curse the programmer for not having implemented.
Ultimately, the key is scope. It's necessary to know the intended scope of the functionality you are coding. You need to consider who (will use code), how (the code will be used) and where (the code will be used [platform]). In Java, the big GET is the DRY philosophy. An immense library of code is available for your reuse. And it's certainly true when perusing the code base of those libraries you'll see a lot of what might seem like over-designing. However, when you consider the intended scope, it's rarely actually over-designed. Because frankly the scope of most code in the library boils down Everyone (who), Any purpose (what), Any platform (where). In such a scope, simple three liner utility functions seldom are sufficient or appropriate.
http://groups.google.com/group/rec.humor.funny/browse_thread...
Java: because it pays the bills, not because it's good. (TM)
Seriously, I kinda knew what to expect when I waded into the article, BUT, his "punchline" at the end about actually learning the domain of the problem, and jumping in a completely different direction about a O(1) way to generate an approximation in some cases, rather than further crap-plating the "solution", was GOLD.
* Density could accurately refer to a number of possible things here.
Just because your manager thinks you can decide how a project's going to look when done before you've done it, doesn't make it true.
Oh and Amen to 'Brevity is the soul of wit'.
Needless to say, I spent most of yesterday cursing.
I still believe that pointing out ways that authors can improve the usability of their web pages is useful feedback.
People's vision varies, but it might be worth a check at a black and white gradient with your display to see if it is wonky.
I keep one from the bad old days of CRTs at http://jim.studt.net/bw.gif if you want to take a peek. If you see wide, uniform areas then your display needs work. (As does my gradient, now that browsers have a white background by default. You can't tell if there is a flat spot on your righthand edge.)
It helps to make a little circle say 1/10th the width of the image with your hand, and look through that. See if each section looks like a gradient in isolation. Your brain lies to you a lot.
->"Having a problem? Thinking you might have one in the future? Just add an abstraction layer! Abstract your troubles into nothingness!"
But a really smart developer would know not to use Java in the first place ;-)