In my quest to never repeat myself, here’s another little trick that I use.
Say we have a number of operations represented by methods that all take the same parameter. Here’s the static data creation code in Suteki Shop again:
public void Insert1() { var sessionFactory = configuration.BuildSessionFactory(); using (var session = sessionFactory.OpenSession()) using (var transaction = session.BeginTransaction()) { InsertRoles(session); InsertAdministrator(session); InsertRootCategory(session); transaction.Commit(); } }
It’s really boring that we have to repeatedly write ‘(session)’. Is there anther way? Oh yes! Enter my ‘ApplyTo’ extension method:
public static void ApplyTo<T>(this T arg, params Action<T>[] actions) { Array.ForEach(actions, action => action(arg)); }
Now our Insert method looks much cleaner:
public void Insert() { var sessionFactory = configuration.BuildSessionFactory(); using (var session = sessionFactory.OpenSession()) using (var transaction = session.BeginTransaction()) { session.ApplyTo( InsertRoles, InsertAdministrator, InsertRootCategory ); transaction.Commit(); } }
Isn’t that nicer?
This lets you treat "plain old methods" almost like extension methods, with the added benefit of being able to batch multiple calls into a single statement. Nice!
ReplyDeleteYou saved a whole *no* lines of code and made it more difficult to understand.
ReplyDeleteNeat use of language features though. :)
This comment has been removed by the author.
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteNo mike, it is not nicer, it is overdesign :-). What you started with is more readable than the outcome
ReplyDeleteNo it isn't nicer. Why don't you make use of OO feature where have a class responsible for this stuff where you have a session data member that can be accessed by its private methods.
ReplyDeleteHi Podi,
ReplyDeleteI agree, if you don't initially know what ApplyTo does, it's a little confusing. But this is a simple example. I might well end up with ten or more functions all taking the same parameter, surely there's no need for all that repetition?
Hi Ramon,
Yes, that's a reasonable approach, but I like to avoid side effecting functions as much as possible. By passing the state as an argument to a function, it's more obvious, predictable, and easier to test. Also it gives you greater flexibility, in my example I could define my static data generating functions as static methods of any class.
Sorry Mike, I agree with everyone else - this is just over design, it isn't shorter, it's definitely not clearer and there are better ways of avoiding passing state in repeatedly.
ReplyDeleteAs Ramon says, this likely should be encapsulated into a class with the state passed in as a constructor.
Your comment about avoiding side effects doesnt make any sense?
Hi Kieran, Don't be sorry, it's good to have an opinion :)
ReplyDeleteIf you build a class that takes ISession as a constructor argument and then have void -> void methods that act on the session, you are writing side effecting functions. It's a perfectly valid way of going about things, but I prefer to pass in state to a function if at all possible.
Interesting, Mike. Do you have any more examples nearby?
ReplyDeleteHi Duncan, I'll try and remember to post them as I think of them :)
ReplyDeleteI did consider the case where you have a single function that you wanted to call many times with different arguments:
DoSomething(arg1);
DoSomething(arg2);
...
DoSomething(arg10);
It would be nice if we could write this:
DoSomething.ApplyTo(
arg1,
arg2,
...
arg10);
But you can't use a function as an argument to an extension method, the compiler throws it out. You can write this:
((Action)DoSomething).ApplyTo(
arg1,
arg2,
...
arg10);
But that's starting to look pretty ugly. The case when you have multiple arguments to a function is even worse, mainly because Tuples aren't a first class language feature of C#.
Now, you have to supply, say, a "root" password to InsertAdministrator.
ReplyDeleteHow would you handle this *new* requirement?
Hi @awerlang,
ReplyDeleteOf course it only works if each function is applied to the same argument(s). One solution would be to have some kind of context object that you could add both the session and your credentials to, but whether that makes sense or not depends on your particular programming problem.
This comment has been removed by the author.
ReplyDeleteMy point is, this kind of solution is not robust enough, as new requirements arrive.
ReplyDeleteUnless you only plan to use it in throw away, repetitive code.
Regards