
Don't be stupid: Grasp SOLID - nikic
http://nikic.github.com/2011/12/27/Dont-be-STUPID-GRASP-SOLID.html
======
fleitz
"But honestly: Most of the code being written around the globe is an
unmaintainable, unreusable mess."

Yeah it is, and that's completely fine because most software is written to
solve a particular problem and doesn't need to be reused. SOLID and GRASP have
great applications but honestly for most software YAGNI is a much better
principle to follow. If you're building libraries, or frameworks SOLID and
GRASP are essential, if you're building applications your software is likely
mostly glue. I don't worry too much about reusing glue.

This is what you get when you folow SOLID and GRASP all the time:
[http://publib.boulder.ibm.com/infocenter/cicsts/v2r3/index.j...](http://publib.boulder.ibm.com/infocenter/cicsts/v2r3/index.jsp?topic=/com.ibm.cics.ts23.doc/dfhpj/dfhpj7b.htm)

All you really need to know about GRASP is that it advocates the factory anti-
pattern. If you're using the factory pattern you've likely chosen a language
that couldn't figure out the separation of concerns between allocation and
initialization and has STUPID baked into the core of it.

~~~
AndrewDucker
Factories are a perfectly good solution to the problem of "I have numerous
possible ways of carrying out task X, and I want to use one of them that will
not be specified until the application is running."

So if I want to write some input to a user-defined place then giving the user
the list of options and then passing the option to a WriterFactory to give me
the correct Writer makes sense. You have to put the choice of which Writer to
use in different scenarios somewhere, so why not a class by itself?

~~~
fleitz
Factories are a good way to solve the problem of not being able to pass a
function that creates an object. They're generally an OO kludge around passing
an anonymous function, because for some reason passing a function needs to be
insanely difficult. And so another class is created to manage the thing that
the constructor was supposed to do in the first place (manage the creation of
objects) but if you called it the constructor pattern they'd have to admit the
language was broken.

OO languages that support passing functions generally don't have very many
instances of the factory pattern, and languages that have abstracted
allocation of memory and initialization of memory have even fewer instances of
it.

That's why I consider the factory pattern to be an anti-pattern; you've chosen
a language that's ill suited to the task you are solving and instead of fixing
the real problem (can't pass functions that create objects in a reasonable
amount of effort) you're inventing kludges.

I've only seen a handful of cases where accepting a parameter of type () -> T
or x -> T doesn't solve the factory pattern issue.

~~~
AndrewDucker
I don't understand, I'm afraid. How does function passing negate the need to
choose between multiple implementors of an interface?

~~~
fleitz
The interface of a factory pattern is generally a single method, so most
factory patterns are an interface with a single function.

Normally a factory class looks like this:

    
    
      class MyFooFactory : FooFactory {
        Foo createFoo() { return new MyKindOfFoo(); }
      }
    

createFoo has a type of () -> Foo, that is to say a function that takes no
parameters an returns an object of class Foo. Sometimes the pattern is String
-> Foo, but the more general case is () -> T

When you need to use a factory you'll create it and then pass it to the object
like:

    
    
      FooFactory f = new MyFooFactory();
      FooUser fu = new FooUser(f);
      fu.doSomething();
    

And FooUser will have some method in it that does something like

    
    
      public doSomething() {
      Foo foo = fooFactory.createFoo();
      // does something with the foo.
      }
    

If FooUser took a parameter of () -> Foo instead of FooFactory your code would
now look like this:

    
    
      FooUser fu = new FooUser(() => new MyKindOfFoo());
      fu.doSomething();

~~~
AndrewDucker
Yes, in the case where a Factory class contains one method that takes no
parameters and just contains a "new" call, you can get rid of the class and
pass in the method.

I was more thinking of the kind of thing I mentioned in my earlier comment,
where you have an IWriter interface, a bunch of different classes that
implement it, and a Factory class that passes back the correct one for
different situations. I completely agree that a whole class that does nothing
but call "new" is complete overkill, and almost certainly just there to fit
into a pattern because someone thinks that Patterns Are Awesome.

The kind of thing I'm thinking of is this:

[https://github.com/andrewducker/FeedThisToThat/blob/master/F...](https://github.com/andrewducker/FeedThisToThat/blob/master/FeedThisToThat/src/feedthistothat/Writers/WriterFactory.java)

------
breckinloggins
Doing things like avoiding singletons and concrete class references (tight
coupling) at all costs IS premature optimization! It's just premature
architecture optimization, rather than performance optimization.

Sure, if you know ahead of time that some construct is going to be trouble,
don't use it. BUT, if you're writing "Mom's Recipe Book", I don't see the
point in avoiding a simple singleton DB class just because you fancy that
someday you won't be able to handle the spectacular load that Mom's Recipe
Book is sure to generate.

The "avoid singletons and tight coupling always" argument invariably leads to
J2EE-esque code and, before you know it, you find yourself writing a
DBFactoryConnectionInterfaceProxyFactory or something.

I'm not saying this advice is always bad, just that taken religiously it can
lead to code that is over-engineered for the task at hand.

Lastly, let's say Mom's Recipe Book DOES become the next Twitter. What's wrong
with removing the singleton then? If you are replacing one bit of boilerplate
code with another, grep and replace across the project works just fine,
doesn't it?

~~~
AndrewDucker
It's about testing. How are you going to write tests for your code when
calling the SaveMyLovelyChocolateBrownieRecipe method invokes a database call?

Having the database connection passed in to the RecipeSaver class means that
you can pass in a fake one instead, and test it.

~~~
ubernostrum
In my world, we solve the "what if it needs to write to the DB" problem by...
having a DB.

Seriously, creating a test database as part of the test run, and populating it
from fixtures and letting the code interact with it the way it will in
production is actually very, very simple.

This is, essentially, the YAGNI principle; sure, in theory I can come up with
a use case where that code needs to talk to something that isn't a database,
but A) it's not going to happen in real life and B) re-architecting
_everything_ in the name of that possibility is premature architecture
astronautics that will cost much more time and effort than just saying "we
aren't gonna need that" and moving on.

~~~
hello_moto
It's not just DB. What about 3rd-party Web Services (Facebook, Amazon, etc).

Also, can you run the whole thing in your continuous integration build?
Setting up Database to run on a CI server sometime is problematic (depending
on how you setup your CI, if you're using Amazon EC2 instances...). Rails
solved this problem to some extend by using SQLite but one needs to install +
configure SQLite for various machines.

Speed is another thing. The more you write tests that hits the DB, your
automation-test will take a performance hit and you'll have to do something
eventually.

What if you have caching by utilizing some sort of caching server? that needs
to be installed and configured as well.

What if the only way to test the code is to deploy it to the server and write
raw HTTP GET/POST because those are the only entry point?

In 2011, where there are better tools and known practice, I don't think we
should labelled some of these practices as YAGNI and Premature Optimization.

First of all, it's not YAGNI if you have to refactor or write another
framework, tools, or some sort of patches in order to have an acceptable build
time.

Second, it's not Premature Optimization if the tools, the libraries, and the
environment provide you with ways to implement them in no time.

I think the excuse of Premature Optimization is used a lot in a context where
it doesn't mean what it supposed to mean.

~~~
ubernostrum
_I think the excuse of Premature Optimization is used a lot in a context where
it doesn't mean what it supposed to mean._

Which may be why I didn't use the phrase.

In all seriousness, though, I can sit down and come up with dozens -- heck,
hundreds -- of "well, what if" cases. And you know what? I don't care about
'em.

I went through the phase of abstractly decoupling everything to the Nth degree
because of "what if" cases. And I paid for it; I was overlooking simple and
often more elegant solutions to actual problems in favor of complex solutions
to hypothetical problems.

Does using a real database instead of decoupled
MockableWriteableDataStorageInterface mean my tests take a few extra seconds
to run? Yup.

Does the time I'd save running tests by writing and passing in a
MockableWriteableDataStorageInterfaceImplementation outweigh the increased
complexity and thus mental load of the codebase? Unlikely.

And, importantly: does testing with something that isn't a real database
potentially cover up bugs that only happen when I use a real database? Yup,
and _that_ happens often enough that the whole mockable thing just goes out
the window right there.

Anyway. I think most developers go through this phase, too, and I think it's
similar to the way people spend disproportionate amounts of time worrying
about dramatic but unlikely things (shark attacks, terrorists, etc.) rather
than dealing with the mundane but extremely likely things right in front of
them.

~~~
hello_moto
Those "what ifs" had hit me in a few projects already. This is why spring-test
module from Spring Framework is very helpful if you're doing Java projects.

I don't have to deploy "war" to Tomcat or Jetty. I can mock the Request and
Response object.

We do have integration-tests that test specifically component that interact
with databases. But there are another set of unit-tests that test the
business/process workflow and assume the database will return something
relevant. We do this because we want to write many tests with focus
specifically to business/process workflow related code, not the database
layer.

This way we can write more tests without paying the taxes of: performance and
setup (when you have a huge database models, it's not fun to prepare the
models for each-single-unit-test that has to touch the database even though
the purpose of that unit-test is not to test the database).

So far this model has worked well for us (i.e.: no more excuses that build
takes too long or writing unit-tests take too long due to the setup both
infrastructure and preparing huge model).

------
z0r
I have no idea how the 'optimization' example could be considered as such. The
advice to not engage in premature optimization is often repeated, but it's not
really illustrated here. No comment on the rest of the article.

