Thursday, 28 June 2007
Build your own CAB series
Just finished reading the series on WinForms programming patterns by Jeremy D. Miller. Although I've been reading a lot of stuff on "how-to-do-it" recently, these posts really stand out. First, they are a big fun to read. Sometimes Jeremy explains his ideas with the help of four musketeers applying these patterns while fighting with their enemies. But even back to the coding, Jeremy's language is clear and simple. And second, and here goes my respect, he doesn't make it a dogma. He nevers says "you should do it this way coz that's the right way doing this". He says, in these situations I usually do it this way, and it helps me because of this and that. But yes, you can also do it another way if you want, but beware of this and that.
Sunday, 24 June 2007
FreshReports intro
I began to plan for FreshReports when I tried in vain searching for an open source printing framework. I, of course, used Crystal Reports a lot, but when I switched from Datasets to ORM (I'm using Neo), I was wondering if it is possible to print data coming from the object-oriented dimension.
Actually, Crystal can do it, but in a very awkward way. Since the engine is relational-oriented, the business object printing ability is a nice add-on rather that a main idea, so you treat your objects as somewhat crippled data rows, with all consequences.
Some other frameworks are presumably better with that, but I realized that all of them (well, all I have seen) are trying to be a sort of MS Access reports for .Net. All are trying to get past the business layer straight to the database. All are presenting the same section structure: you have your main data source, sort of a table, and you group it by some field, just like an SQL query. And none are open source.
FreshReports is a totally different idea. Let's see it one by one.
Actually, Crystal can do it, but in a very awkward way. Since the engine is relational-oriented, the business object printing ability is a nice add-on rather that a main idea, so you treat your objects as somewhat crippled data rows, with all consequences.
Some other frameworks are presumably better with that, but I realized that all of them (well, all I have seen) are trying to be a sort of MS Access reports for .Net. All are trying to get past the business layer straight to the database. All are presenting the same section structure: you have your main data source, sort of a table, and you group it by some field, just like an SQL query. And none are open source.
FreshReports is a totally different idea. Let's see it one by one.
- It's open source. I wrote in in VB.Net with a little bit of LINQ (from the May CTP). Why VB not C#? Because I know it better (I still think that C# would work better here). I do plan to make a commercial editor, but for now there's a vary simple standalone editor that just allows you to edit the source manually and preview it with a predefined data source.
- It's extensible. You can add your own elements, aggregates, even sections. After I add layout rules and adapters, you'll be able to customize them, too.
- You have a simple xml format for the layout. So, in fact, you can program your own editor, or you can have it autogenerated.
- You have no predefined layout. You can have sections inside sections, or sections after sections.
- You can have any object structure. Your data source doesn't have to implement any interface, even IEnumerable.
- You can have any number of independent (or interdependent) data sources. Each section can pick an external data source, or a property of the containing data item.
- It's testable. From the beginning, I've been developing it in a TDD fashion (however, as I was a novice, I actually did integration tests, and only recently I started to write unit tests, but I understood a lot in the process). And I do plan to add a testing framework later. All testing is done with TypeMock, and I'd like to thank the guys again for the great tool. Without it, it would be impossible to test the calls to the Graphics object which is in the core of all printing.
Saturday, 23 June 2007
FreshReports Alpha is released
I have finally uploaded a more or less functional copy of FreshReports to SourceForge. You can find it here. Now I'm gonna take a break, make a couple of reports with it, and start spreading a word.
Sunday, 17 June 2007
When good programming practices are just too good
Recently I saw an article on CodeProject (the article itself is very good, so I'd rather not post the link here) that had a great explanation of things like MVC, flexible design patterns, dependency injection, programming with interfaces etc.
What struck me in this article is the ridiculous amount of code required to implement simple things. The author explained everything using a WinForm application as an example. So, the dependency injection was implemented not just for the form, but for a button as well! The author designed an interface for the Click event, and an extra class that implemented this interface and forwarded the call to the underlying button.
Now, I see it as an excellent example of good programming practices -- everything is very clear. But at the same time, it is an example of what happens when a good practice it taken to an extreme. Take a look at this snippet:
Now, a "good programmer" would design an interface that provides the Click event, then design a class (say, GoodButton) that implements this interface and use it as a button on your form. Every single method or property that you need with a button you should put into the interface and then implement in your control, propagating them to your underlying button. In a way, you have "triplication" of your code here, and you write a lot of code that actually doesn't do anything, except for making your program "well designed" and use "proven practices". Why? Because this way "you have no hardcoded dependency on your Button class". Doesn't it sound ridiculous? And think of it, you now has a class GoodButton that just got a hardcoded dependency, and you have your form that has a hardcoded dependency on your GoodButton. Unless, of course, you put the class name in your config and load it dynamically. This way you just have to change "Button" in your config without recompiling the application.
Sounds familiar?
Next, you, of course, is going to test this code. You want to test that each time the button is clicked, the method DoIt is called. In order to do it you, perhaps, will make it public (which is clearly against the best practices), have some mock tests etc. But think again -- do you really want to test this 1-liner? How many lines of test code do you want to write, how much time to dedicate to fix bugs in your test code? Do you really think you are going to refactor it at some time? If yes, just put a comment: "NOT TESTED: REFACTOR WITH CARE" next to it, and you'll feel fine. Use NUnitForms to test your GUI, use TypeMock to test your hardcoded dependencies, and use interface-first design where you feel you need flexibility. In other words, use "thought driven design".
We all love rules -- with them, you don't have to think much, you just apply what the smart guys think is the best. However, lately we younger generation begin rebel and declare these rules as "elitist crap". This happens in response to posts like this, when convenient tools are slammed down just because they don't force "good practices" on us. You can guess that the least thing I want is that something enforces me to do that somebody else thinks is "good". As a result, after I spend too much time on implementing some practice that I don't need, next time I prefer to forget about it. Same thing with testing -- after spending too much time on 100% testable projects, I discovered that I tend to skip testing totally, which proved to be very wrong decision.
So, my idea is -- follow the big guys' ideas, but don't take them as rules, rather as advices. Implement them in say 90% of your application, and carefully weight all benefits each time. This is especially true in places where the code is autogenerated (it's already flexible enough: it takes little effort to replace a dependency, and you can trust it and use without testing). Your business layer is a good place for these practices. Hell, you might even want to change your ORM tool at some point!
To summarise the whole Thought-Driven-Development-versus-Big-Guys issue:
- Always think about the benefits of the proposed pattern. Don't get content with words like "flexible" and "eliminates dependency", even more with "proven". Think about what "flexible" means in your case, and what is "inflexible" if you do it straight, and why it is bad to be "inflexible" here. Estimate how much do you have to change if you need that flexibility later.
- Always think about the costs. How much to you need to code just to make it flexible or testable. Note that you don't have to make it testable with TypeMock, for example, but that's considered a "bad" tool among purists, because it doesn't "enforce" us (meaning it liberates us a lot, actually).
- If you discover that thу proposed practice makes your design unnecessarily complicated, unclear, adds too much nonfunctional code, or in any other way distracts you from your main purpose, write a blog entry about it, or comment somebody's article on good patterns, in order to provoke thinking and stop people from blindly following the rules.
What struck me in this article is the ridiculous amount of code required to implement simple things. The author explained everything using a WinForm application as an example. So, the dependency injection was implemented not just for the form, but for a button as well! The author designed an interface for the Click event, and an extra class that implemented this interface and forwarded the call to the underlying button.
Now, I see it as an excellent example of good programming practices -- everything is very clear. But at the same time, it is an example of what happens when a good practice it taken to an extreme. Take a look at this snippet:
Private Sub OkButton_Click(...) Handles OkButton.Click
DoIt()
End Sub
Now, a "good programmer" would design an interface that provides the Click event, then design a class (say, GoodButton) that implements this interface and use it as a button on your form. Every single method or property that you need with a button you should put into the interface and then implement in your control, propagating them to your underlying button. In a way, you have "triplication" of your code here, and you write a lot of code that actually doesn't do anything, except for making your program "well designed" and use "proven practices". Why? Because this way "you have no hardcoded dependency on your Button class". Doesn't it sound ridiculous? And think of it, you now has a class GoodButton that just got a hardcoded dependency, and you have your form that has a hardcoded dependency on your GoodButton. Unless, of course, you put the class name in your config and load it dynamically. This way you just have to change "Button" in your config without recompiling the application.
Sounds familiar?
Next, you, of course, is going to test this code. You want to test that each time the button is clicked, the method DoIt is called. In order to do it you, perhaps, will make it public (which is clearly against the best practices), have some mock tests etc. But think again -- do you really want to test this 1-liner? How many lines of test code do you want to write, how much time to dedicate to fix bugs in your test code? Do you really think you are going to refactor it at some time? If yes, just put a comment: "NOT TESTED: REFACTOR WITH CARE" next to it, and you'll feel fine. Use NUnitForms to test your GUI, use TypeMock to test your hardcoded dependencies, and use interface-first design where you feel you need flexibility. In other words, use "thought driven design".
We all love rules -- with them, you don't have to think much, you just apply what the smart guys think is the best. However, lately we younger generation begin rebel and declare these rules as "elitist crap". This happens in response to posts like this, when convenient tools are slammed down just because they don't force "good practices" on us. You can guess that the least thing I want is that something enforces me to do that somebody else thinks is "good". As a result, after I spend too much time on implementing some practice that I don't need, next time I prefer to forget about it. Same thing with testing -- after spending too much time on 100% testable projects, I discovered that I tend to skip testing totally, which proved to be very wrong decision.
So, my idea is -- follow the big guys' ideas, but don't take them as rules, rather as advices. Implement them in say 90% of your application, and carefully weight all benefits each time. This is especially true in places where the code is autogenerated (it's already flexible enough: it takes little effort to replace a dependency, and you can trust it and use without testing). Your business layer is a good place for these practices. Hell, you might even want to change your ORM tool at some point!
To summarise the whole Thought-Driven-Development-versus-Big-Guys issue:
- Always think about the benefits of the proposed pattern. Don't get content with words like "flexible" and "eliminates dependency", even more with "proven". Think about what "flexible" means in your case, and what is "inflexible" if you do it straight, and why it is bad to be "inflexible" here. Estimate how much do you have to change if you need that flexibility later.
- Always think about the costs. How much to you need to code just to make it flexible or testable. Note that you don't have to make it testable with TypeMock, for example, but that's considered a "bad" tool among purists, because it doesn't "enforce" us (meaning it liberates us a lot, actually).
- If you discover that thу proposed practice makes your design unnecessarily complicated, unclear, adds too much nonfunctional code, or in any other way distracts you from your main purpose, write a blog entry about it, or comment somebody's article on good patterns, in order to provoke thinking and stop people from blindly following the rules.
Subscribe to:
Posts (Atom)