Hacker News new | past | comments | ask | show | jobs | submit login

It's the same pattern that repeats itself all over the place. We've seen it with Java, with Ruby, heck, even with PHP. And now with JS: As applications grow, we tend to search for tools to help us cope with the growth.

After doing similar (but still slightly different) things over and over, we start building abstractions and with every thing our abstraction can't deal with, we make them more complicated.

Whether you use the official name of your thing ("factory") or you just run with your own idea and term - the outcome is always the same.

As a library, you feel the need to be abstract enough to cope with every feature request ever asked from your library and there will be tons of feature requests and tons of little parts people want to customize.

And honestly, don't we all prefer using some public API of a self-contained library to patching said library (think of the maintenance as that library gets updated)?

This isn't about ruining a language. This is about making the right choice of library or about the decision of building your own little customized thing that exactly fulfills your requirements (and nothing else), but as your requirements change, you're going to add abstractions yourself and suddenly you're back at the AbstractSingletonProxyFactoryBean, though you might have chosen a different name that nobody but your team understands.

As a library author, try to resist the feature creep; try to be opinionated ("I'm really sorry, but logging isn't pluggable. You want to log to your XML based logging server? That's fine, but you'll have to build your own thing or use a different framework") then you remain accessible and buzzword-free, even though that might cost you some users in the short term.

Language has nothing to do with this.




An abstraction is not useful, in fact it's downright harmful, if it does exactly the same thing as what it abstracts over, but more verbosely.

A lot of these patterns you're used to seeing in Java stem from Java's static nature.

Here's a car factory in javascript:

  function makeCar(dealerName) { return new Car(dealerName); }
And here's a configured car factory:

  var myCarProvider = makeCar.bind(null, "my dealer");
It makes precisely zero sense to create abstractions that reimplement core language features. That would be like writing a C function to add two uint8_ts, or a macro that expands to inline assembly to convert a float to an int32_t. You already have these things built-in, you don't need a gorillion enterprisey patterns.


This of course has nothing to do with staticness, and everything to do with Java's lack of first-class functions. In any flavor of ML, for example, nonzero-argument versions of this pattern are slightly cleaner (the exact pattern is unnecessary due to immutable state).

> or a macro that expands to inline assembly to convert a float to an int32_t

This sort of thing is actually useful on occasion, because it allows you to specify the exception handling you actually want (what to do on inf/-inf/NaN). Of course, it's better do it with a pure C function, but it's still a "reimplementation of a language feature".

Of course, you only ever do this when you want subtly different behavior than the language provides, so I think your larger point still stands.


Here's a car factory in javascript [...] And here's a configured car factory

Great, now where is the part where the code that provides the "my dealer" string can be completely unaware that makeCar() exists?

Oh, it doesn't exist? That's the problem that Angular DI is solving.


Here:

    var factory = makeCar;
The whole point of having first-class functions is that you can assign them. The problem a factory solves is precisely the lack of this assignability (which is why a constructor function is wrapped in a class).


Please put in the effort to understand at least the basic idea of dependency injection before deriding it in favor of "simpler" solutions that do not solve the same problem.

The specific pattern DI can solve here is:

- one module provides some capability by name

- another module makes use of that capability by name

- the two modules have no knowledge of each other, and no higher-level glue code is required to connect them together (and construct the appropriate objects at the right times).

Your "solution" does not satisfy the requirements because it requires you to manually construct the objects in the correct order. DI on the other hand will automatically analyze the graph of dependencies, invoking the appropriate factories in the right order.


Sorry to say, but klmr is right. Since functions are first-class objects and references are evaluated late, these issues are already managed by JS and native scopes. There's no need to recreate this functionality in a framework. You might just want to consider the entry point to your code.


P.S.: What this really is about: Unit tests originally designed to go with C/C++ do not work well with late binding. In fact this is a concept totally foreign to these languages. It's essential to understand that these kind of frameworks serve in the first line the purpose of test suites and only in second place the needs of developers. (Developers should not mistake themselves for test suites.)


You're completely missing the point and solving the wrong problem. Function or class, its a minor semantic difference for the same thing.

The issue is not how you define your factories, it's how you pass them around and use them in a component that has no knowledge of the outside world.


> it's how you pass them around and use them in a component that has no knowledge of the outside world

You pass it as a function argument.

    function needs_to_make_a_car(car_maker, road) {
      var car = car_maker()
      car.drive_on(road)
    }
To which you would probably reply: "But that's exactly what FactoryFactory/DependencyInjection/... pattern is!"

To which we would reply: "Exactly, which are just fancy words for the re-and-re-discovered concept of functional programming."


> To which you would probably reply: "But that's exactly what FactoryFactory/DependencyInjection/... pattern is!"

Actually no. DI does a lot that your simple examples do not. Notably, it understands the graph of dependencies between decoupled compoments and constructs provided values in the correct order.


DI is a design pattern. A design pattern is a conventional way to structure code to solve a particular type of problem. A way to structure code is not a thing that can analyze graphs.

Maybe you're talking about some particular DI framework. That would be a library with functions and classes to make implementing the DI pattern easier. There are many such frameworks, and some of those frameworks may well do automatic graph analysis. But, graph analysis is not inherent in the pattern.

Your specific framework is not the general pattern.


Except you don't touch on other aspects of what makes IoC containers useful in the real world.

I've seen XML configurability used as an extension point for code that would otherwise be closed. I've seen bugs happen because the lifecycle of objects have been mismanaged. You're conflating that things can be done more elegantly using functional programming to think it belongs in a functional paradigm.

IoC containers in .NET are easier to use and more elegant because of functional programming constructs, but their existence doesn't remove the need to have them.


> I've seen XML configurability used as an extension point for code that would otherwise be closed.

Take a step back, and think why that 'extension point' had to be closed to begin with. You're probably going to find either an architectural flaw, or a problem with your tooling that should not have required this sort of thing in the first place.

Also disturbing is that you are using XML to add a degree of expressiveness missing from whatever tooling you are using.


Hmm. So small example on what I saw recently was being able to swap the authorization/authentication piece from hitting a service to users defined in XML for locally running the project. Architecture flaw? No. Problem in tooling? If you have a convincing enough argument, yeah. Does Spring feel like it lets you do too much in XML? Hell yes. It's awesome when it works. It feels too convoluted when you don't need it.


> functional programming.

That's not functional programming.


i don't think you actually understand these patterns or how they're used. it's useful when you have multiple implementations of `Car` that you want to use under different circumstances. Or when `Car` is dependent on static configuration that might depend on the host it's running on, or might be different in development/testing/production.


I don't think you understand functional programming.

    function makeAppropriateCar(cargo) {
      if(cargo == "people") return new Automobile();
      else if(cargo == "boxes") return new Truck;
      else ...
    }

    function makeLocalCar(settings) {
      var car = new Car({'color': settings.get('new_car_color')});
      car.set_origin(settings.get_hostname());
      return car;
    }

    function makeDebuggingCar() {
      if(global_settings.DEBUG && !global_settings.PRODUCTION)
        return new Car({'loglevel': LOGLEVEL_DEBUG});
      else if(global_settings.DEBUG && global_settings.PRODUCTION) // testing
        return new Car({'loglevel': LOGLEVEL_INFO});
      else  // production
        return new Car({'loglevel': LOGLEVEL_WARNING});
    }


Ok, now try maintaining that when there are 15 types of cars and cars are used in dozens of services. You're imperatively expressing a dependency graph that you could (and would benefit from) defining declaratively at a larger scale.

I'd recommend reading the user guide for Guice for balance before you respond. It's worth seeing how nice DI can be.


What does your example have to do with functional programming?

You've written two factories, pretty much exactly how AngularJS uses them, in an imperative style.

Regardless of which, this doesn't in any way solve the issue Angular Services are designed to solve (IoC).


This example actually really sucks. Advocating for case statements shows that either you've never worked on a truly large project or you've never had to go back and modify your own code 2 years after the fact.

If you really understood functional programming, you'd see an if/case statement as an opportunity to replace conditional with a function.

I started to write up an example, but since we're talking about the imaginary abstract Vehicle implementation, it's not worth dignifying.

If programmers didn't switch jobs every 12-18 months and didn't see 100K LoC as a "large project", these "all frameworks suck" rants might turn into useful discussions of how one can organize code such that it's understood by more than the original author. That goes for frameworks too. If you have to rely on one of the core developers responding to a post on Google Groups or SO as your support mechanism, you've made the world less simple.


It's a fine example. Are you saying that 3 case statements is too many?

Why optimize the code to support cases that don't actually exist yet?

Or even better, why optimize the code in ignorance to how it will need to be optimized in 2 years?


None of these are examples of functional programming except that you wrote some functions.


> As applications grow, we tend to search for tools to help us cope with the growth.

Actually, we tend to search for tools to help us not learn another language better suited to the domain we found ourselves in.

The point that JavaScript is prototype-based and, therefore, some of these patterns make little or no sense (or could be expressed in much simpler terms) appears to be lost.


"Language has nothing to do with this."

Great, thanks. Hey, by any chance do you think there's no single greatest language, and that people should use the right tool for the job? Terrific, me too.

Saying "language has nothing to do with this" misses the point of this post entirely. It's about how people bring the same unwieldy practices wherever they go regardless of the language.


The title of the post was "you have ruined javascript" and I maintain that JS has in-fact not been ruined just the same as all other languages haven't.

Abstraction bloat in libraries is not something that's specific to languages and a language can't be ruined by libraries suffering from abstraction bloat (unless all of them suffer cough java cough)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: