Monday, 24 November 2008

Unit testing private methods.. not again!

This is a question often raised on TDD forums. How do I test a private (protected, internal, Friend) method. Those not so bold ask, should I? A typical answer is, if you want to test a private method, extract it into another class and make it public, and make the first class hold a private instance of this new class.

I think that this answer is too general. While newbies generally want a universal recipe (and this one is good enough), I'd start gathering more info. Like a Zen master wannabe, I'd ask a question which should be an answer to the original one. Oh, and then I'd ask some more.

So, why do you want to test it?

Is it just the idea that every method in a class should be tested? Then resist this temptation.

Another option is that your public method that calls your private method just started acting weird. Well, the next question, what portion of the code just changed that caused this behavior? Still another option, that you dared to write some code without writing the test first, and now you're trying to cover your ass. My humble advice would be to delete the bastard and start all over.

But there are several other important questions to ask. Does the private method have a well defined semantic meaning? If yes, why do you resist making it public? Are you afraid that someone will call it? Why? Are you lazy to document it?

The only reason I can accept here is that your private method puts a system into an inconsistent state. For example, you can have a public Transfer method, which takes money from one account and moves it to another. The two private methods would be manipulation the two respective accounts. If you call one of them, some money would be, say, taken from one account but not moved to the other. In this case, it is unacceptable to move the method to another class and make it public: calling it from outside would be a disaster. In this case, I would simply refactor the system so that no such method exists.

As this begins to sound very confusing, I'd give some reasons for choosing what to test.
  • A test is a usage example, a documentation in an executable form. A unit test is, more or less, a feature: you provide a code example on how to use your product. This might be wrong for desktop or Web applications, but the essence remains the same. It would be weird to provide a usage example for a private method.
  • One of the main reasons for hiding a method is "encapsulation", as the proponents put it. In other words, you want to hide the implementation details. This is usually a good idea, given that you have a solid reason to do so. Typically, the reason is that you might change it in the future. So, the simple logic conclusion is that you shouldn't test it, since it makes your tests brittle. On the other hand, if you make it public, nothing prevents you from making a changed version later, and keeping this one unchanged (or maybe enhanced, but keeping the same functionality).
  • Still another reason: if I make all these methods public, my API will be bloated, and my customers confused. This is a valid reason only for those who are lazy enough to write a good doc. Other possible solutions are: use the EditorBrowsableAttribute; move this method to another class (here we go again!), and place this class in the Internals namespace (or invent some ever more scary name).

Monday, 10 November 2008

Localizing the GridView date values

This is weird. I should have bumped into this problem ages ago. The problem is, whenever I'm trying to enter a localized date into a datagrid, it is saved as if it were an American date. I.e., 1.02.2008, which is the 1st of February, is saved as 01/02/08 (January 2nd).

Setting the page's Culture to Russian didn't work. A quick Reflectoring showed that ObjectDataSource uses InvariantCulture for parsing the supplied values.

The solution is sort of ugly: use the grid's Updating event and swap the string with the corresponding date:
    Protected Sub gridDjSchedules_RowUpdating(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewUpdateEventArgs)
        e.NewValues("TheDate") = Convert.ToDateTime(e.NewValues("TheDate"), System.Globalization.CultureInfo.CurrentCulture)
    End Sub

Looks like it's time to create a custom control..

Tuesday, 14 October 2008

In search of a developer-friendly CMS

After a lot of frustration with DotNetNuke, which was quite a while ago, and some recent adventures with mojoPortal (by the way, Joe, the maintainer and almost a single contributor for this project, does excellent job fixing all the bugs in no time after they are reported), I decided that no way I'm gonna use a CMS again, ever. Yes, I used it on my own site, http://sm-art.biz, but then, it required no custom modules.

The problem is, all these frameworks focus on how simple it is to get your site up and running, provided that you don't need any additional functionality, other than included in one of the million modules. When you start to add something that is unique to this site, you quickly realize that it's much simpler to start from scratch than follow the module development guidelines. At the very least, they require that youк module inherits from a base class, and is initialized in such a way that it is totally impossible to test it outside of the framework.

So, here are the qualities of a CMS I wish existed. Has anybody seen this puppy?

1. Respect the composition-over-inheritance principle. Don't make me inherit, let me use a plain old ascx control.
2. Make it simle to plug this control into my site. Don't make me write these manifests and put sql scripts in a predefined place. However, I do want this possibility to exist, and also custom installer classes.
3. Use Convention over Configuration, it's the latest trend.
4. Make it decoupled. I don't want to see a column "totalforumposts" in the users table.
5. Use Ivonna for testing, I don't want a buggy product :)
6. I want to go from a regular site to a CMS and back in small steps. I want to be able to use various parts of it independently and switch off other parts if necessary. It should be more like a framework. There shouldn't be one "switch to a CMS" step, but rather start using various pieces one by one.

Until I see something like this, I'll be trying to write my own.

Tuesday, 29 July 2008

A Dose Of Code

While developing Ivonna, I often have to figure out how various stuff in System.Web works. Sometimes Reflectoring it is enough; however, there are some particularly gigantic methods, full of loops and branches, that are hard to figure out. In any case, Reflector gives a static picture, while I need to see it live. Sure now you can download the source and debug through it (although I've never managed), but the real hackers don't use debuggers, preferring asserts and console output.

Anyway, I needed to inject some code (or stop the debugger) at some particular method call in some assembly I don't have the code of. I've been using the TypeMock's MockMethodCalled event for it. After a while, it became tedious, so I decided to put some repetitive code into a class. Soon I found that it could have a couple of more useful features, but I wanted it to be really simple, and I didn't need much, so I'm keeping it to one class, three methods, three relaxed evenings of development.

How does it work.

  1. Create an instance of Njector using the target type (this is something like creating a mocked instance).
  2. Add one or more injected pieces of code (something similar to adding expectations).
  3. Call the main code, in which an instance of the target type is created, and the target methods are called.
  4. The injected pieces are invoked before, after, or instead of the target methods. You also gain access to the target instance, target method parameters, and return value (in case you use the After injection).


Example
This is something that I actually used to figure out how the System.Web.HttpMultipartContentTemplateParser class parses the input using the ParseIntoElementList method. This method has several loops and branches, so it's not easy to find out what's happening given a particular piece of data. What you see here is just the preparation; after that I prepare the input data and call the framework code using Ivonna, but that's outside the scope of this post. GetPrivateField() is an utility extension method doing guess what.

var web = Assembly.GetAssembly(typeof(System.Web.HttpRequest));

var parserType = web.GetType("System.Web.HttpMultipartContentTemplateParser", true, true);

var inject = new Njector(parserType);

inject.After("GetNextLine", null, delegate(object target, MethodBase method, object[] parameters, object retValue) {

Console.WriteLine("GetNextLine: {0}", retValue);

Console.WriteLine("Pos={0}", target.GetPrivateField("_pos"));

Console.WriteLine("line={0}", target.GetPrivateField("_lineStart"));

Console.WriteLine("partDataStart={0}", target.GetPrivateField("_partDataStart"));

});

inject.After("AtBoundaryLine", null, delegate(object target, MethodBase method, object[] parameters, object retValue) {

Console.WriteLine("AtBoundaryLine: {0}", retValue);

});

inject.After("AtEndOfData", null, delegate(object target, MethodBase method, object[] parameters, object retValue) {

Console.WriteLine("AtEndOfData: {0}", retValue);

});

inject.After("ParsePartHeaders", null, delegate( object target, MethodBase method,object[] parameters, object retValue) {

Console.WriteLine("ParsePartHeaders");

});

inject.Before("ParsePartData", null, delegate(object target, MethodBase method, object[] parameters, object retValue) {

Console.WriteLine("ParsePartData Before");

Console.WriteLine("Pos={0}", target.GetPrivateField("_pos"));

Console.WriteLine("line={0}", target.GetPrivateField("_lineStart"));

//System.Diagnostics.Debugger.Break();

});

inject.After("ParsePartData", null, delegate(object target, MethodBase method, object[] parameters, object retValue) {

Console.WriteLine("ParsePartData After");

Console.WriteLine("Pos={0}", target.GetPrivateField("_pos"));

Console.WriteLine("line={0}", target.GetPrivateField("_lineStart"));

Console.WriteLine("partDataLength={0}", target.GetPrivateField("_partDataLength"));

});

//Prepare the data

//Do the POST

//The framework creates an instance of System.Web.HttpMultipartContentTemplateParser and calls its ParseIntoElementList method,

//which in turn calls the methods like GetNextLine, AtBoundaryLine, etc.

//After the GetNextLine method is called, our code is executed, and we're able to see the result.


Download the source here.

Update: there's a much better project now, called CThru, developed by TypeMock.

Thursday, 29 May 2008

Multiple AppDomains on a single Web

I was getting some weird errors when using a Neo ObjectContext on a Web as a global variable. This thing is supposed to be dependency-injected into factories and stuff, but I've set it to be a static property long time ago and saved me a lot of effort, since most stuff became possible with the Neo code generation tool.

This was long before I learned that singletons are evil.

Anyway, I've been getting very weird errors. Such as, I see a page with a record that should have been deleted, I refresh the page and it's not there, I refresh it again and it's there etc. It looked as if there were two contexts, and the record has been deleted from only one of them. However, since a Context was a static variable, there couldn't possibly be two contexts, right?

Or so I thought. Recently, I've been investigating the whole remoting thing in connection with Ivonna, and I discovered the trivial fact that different static variables exist in different AppDomains. So, I thought that maybe there are two AppDomains for the same site? A quick experiment showed that this is true indeed, if there are two (almost) simultaneous requests.

And the moral of the story is.. dunno, will figure out tomorrow.

Wednesday, 14 May 2008

LOLCode, DLR Edition

This is a code snippet that actually works.
HAI

CAN HAS STDIO?
VISIBLE "HAI WORLD!"

I HAS A CODE ITZ "CRAZY"
VISIBLE CODE

I HAS A NUMBR
LOL NUMBR R 2
VISIBLE NUMBR TIEMZ 2

I HAS A FIB
I HAS A A ITZ 1
I HAS A B ITZ 0

It's written in LOLCode.NET, a brand new .Net language that quickly gathers support among agile developers. Read the full story on the Scott Hanselman's blog.

Sunday, 11 May 2008

Activating the Record

Lots of things happened since I last cared to blog. Here's the first one, chronologically. While I'm trying to not get too excited about Dependency Injection, I realized that it's about time to use some services in Inka, and I sort of needed an IoC container. My first idea was to use StructureMap by Jeremy Miller, but I wanted an automocking container for my tests as well, and I was forced to choose Castle Windsor. Or so I thought at that time -- it turned out that, first, I didn't have time to implement these things, and second, StructureMap also has an automocking container. But that's not the point.

The point is, I thought, hell, why not move to a decent ORM as well? Meaning Castle ActiveRecord.

My first ORM was Neo (Net Entity Objects, I think the name has been invented before The Matrix). Unlike Castle AR, which is really a nice interface for NHibernate, Neo is Active Record in a true sense. I'd even say, it's Typed DataSets Done Right. Each entity class is a wrapper over the DataRow class, so we can track the state (is it added, deleted, or modified) automatically. Each object is created via a factory, and added to the context automatically, so it can appear in your query results without being saved to a database. Cool. And also very convenient for testing.

Speaking of testing, I still can't figure out how to test AR applications without a database connection.

So, Neo is not for purists (nor is AR), they want POCOs. But I''m just a script kiddie, so it's OK for me. Neo's got a code generator, so I quickly enjoyed the idea and put all sorts of stuff into my templates, including some UI-related things. Don't blame me, I haven't heard about SoC in these days. In return, I managed to do some boilerplate stuff extra quickly.

Unfortunately, there's a very small community around Neo, and the development seems to stop. I've fixed a few bugs, but never got to publishing the fixes (only recently I learned that I had violated the LPGL license). Also, it's pretty simple, and perhaps won't cover more complex situations, including fat query results.

I'm only learning Active Record, and it has some very nice points (including a great graphic schema editor, Active Writer), but I've had some really weird moments with it. One, for example, is that I've been getting some weird exceptions that I wasn't able to reproduce in my tests. Almost intuitively, I invoked a Scope constructor at startup, and the problem was gone, although I never used the created scope variable anywhere! Turned out there's some dirty game with shared (static in C# :) variables here and there. I used to wonder why people hate these statics, now I know!

I'd like to learn more about AR, but now I'm totally thrilled by Ivonna being released soon, so I'm leaving the applied programming world for a while..

Tuesday, 26 February 2008

Oops

Hmm.. finally my layout engine for Inka is ready. Or so I thought. All tests are green, well, some are yellow, but come on. I was sure that I over-tested it and began planning to dramatically reduce the number of tests -- I start a demo app and discover that everything looks wrong!

Thursday, 21 February 2008

Just can't help it!

Well, finally I'm at the point where I have to prepare an installer for Ivonna. Actually, I'm stuck at this point for about two weeks. Not because of installer itself, but because of the documentation. No, I diligently wrote all the xml comments, believe me. But these comments have to make way into a nice docs, preferably with some custom ("conceptual" in Sandcastle terms) content. And it has to be in Html Help 2.x format. This morning I finally discovered that my help can be viewed in the Document explorer. Was it because of the electricity been cut for a moment?

Anyway, here's my recipe. I'll be using VS 2005 with the 2007 SDK, Sandcastle January 2008 release, and DocProject 1.10.0 RC.
  1. Install the VS SDK.
  2. Install Sandcastle. It is important to install it after the SDK, because the SDK contains its own version of Sandcastle, which is too old. Anyway, if you installed Sandcastle first accidentally, just edit your DXROOT environment variable so that it points to the Sandcastle's folder.
  3. Download and extract the presentation file patches. This fixes a bug in Sandcastle when you don't have a root topic.
  4. Install DocProject. A major annoyance is a bug in the 1.10 RC version: the Add-in looks for the Project toolbar that has more than 40 items. Mine had only 38. The suggested workaround is to download the code and patch it manually, but I couldn't afford that, so, instead I wrote a macro that added 3 ugly entries to my Project menu. Anybody interested? Update: there's a 1.10.1 version that supposedly fixed this bug.
  5. Add a DocProject to your solution. You can add a DocSite project instead if you need online help (it will build the offline help as well).
  6. You might want to modify some templates offered by the DocProject wizard. But don't remove the feedback section entirely -- you'll get a JavaScript error later.
  7. Modify the AssemblyInfo.cs file to reflect your documentation title and organization.
  8. Don't ever rebuild the project -- it'll lose some essential files.
  9. In the project wizard or later in the DocProject properties (available as an additional context menu item for your project), select "build html 2.0".
  10. You might want to select the fastest build option -- choose "none" in the "build assembler options".
  11. Now, after building the project, you have an HxS file in your output folder.
  12. Unlike a chm file, you can't open it directly. Instead, you have to register it and navigate to it using the special ms-help protocol.
  13. So, how do you register it?
  14. The manual steps recommended copying a certain merge module from the sdk directory and manually editing it with Orca.
  15. Like I was going to do that.
  16. Actually I even did a first step.
  17. But it turned out that there's a new kind of extensibility projects called "Help Integration Wizard". What you want is create a merge module that you'll add to your installation project. The wizard is pretty trivial, and it stores all its settings in the CollectionFiles folder, so you can edit them manually later.
  18. At the first step, I chose the help file that I discovered in the Help/bin folder of my DocSite project.
  19. The wizard showed me the structure of my future help. It contained one topic, called "sample topic" or something. it was on the left pane, and on the right there were my topics, conveniently excluded from the future help. I renamed the sample topic to "Ivonna", and added my topics under that root topic.
  20. I also chose the namespace for my help -- ivonna.docs.
  21. However, building the project didn't work for me, the postbuild event (the one that actually edited the merge module) failed. Invoking it manually from the command line failed as well. So, I opened the executable in the Reflector, and it turned out that it has been designed specifically in order to hide the possible cause of an error. For example, the exception was coming from a logging statement that was inside a catch block, so it was cleverly hiding the real exception. Also, all exception messages came from some native calls, so, for example, "File not found" turned into "Invalid handle".
  22. So, I just wrote something that mimicked this app, and it somehow worked. Now I have a patched merge module that I can add to my installer. Update: It turned out that the problem was with the path -- it contained cyrillic characters. Once I created another project in a different location, everything went smooth.
  23. Now, you should add the merge module from this project to your setup. You should probably create a Help folder under your app folder, and choose it for the merge module files. Choose the merge module in your setup project, and choose the folder in the Properties, KeyOutput -> MergeModuleProperties -> Module Retargetable Folder.
  24. By the way, you can see all installed help namespaces via a handy utility located here: \Program Files\Visual Studio 2005 SDK\2007.02\VisualStudioIntegration\Archive\HelpIntegration\Unsupported\Namespace.exe
  25. You probably want to include a shortcut to your documentation. It should be something like this: "%CommonProgramFiles%\Microsoft Shared\Help 8\dexplore.exe" /helpcol ms-help://ivonna.docs. Note that I put my namespace after the "ms-help://" stuff. Yes, you are even able to view it in IE.
  26. After you successfully made the installer, you discover that when the documentation opens it shows an empty page. If you want an introduction or something to appear, you should register it as "DefaultPage". Details can be found here.
  27. Modify your shortcut like this: "%CommonProgramFiles%\Microsoft Shared\Help 8\dexplore.exe" /helpcol ms-help://ivonna.docs /LaunchNamedUrlTopic DefaultPage.
By the way, you can download Ivonna from my new site here, and view the online docs (produced with DocProject) here. I'll be blogging about Ivonna on my site's blog.

Monday, 11 February 2008

Inka moved

Inka has finally changed its Unix name at SourceForge. You can find it at https://sourceforge.net/projects/inka.

Also, I'm finally building me a site, and there'll be a lot about Inka and how it should be used. Find it at http://sm-art.biz.

Tuesday, 29 January 2008

Binding It Twice

Last week I, like a lot of other Asp.Net newbies, got a weird validation error on my page saying, "Invalid postback or callback argument". The funny thing is, I've got another error two days earlier, even more weird, saying,

Item has already been added. Key in dictionary: 'ID' Key being added: 'ID'

The weird thing about this error is that I was adding an item to a GridView via a regular mechanism, using the prescribed DetailsView and ObjectDataSource, no funky MVP stuff. The "ID" in this error refers to the key column.

What caused the error in both cases was calling the DataBind method on postback, which you shouldn't usually do if you have ViewState enabled. In the second case, I blindly called DataBind on the page itself.

So, the rule for other newbie who happen to read this:
Whenever you init your page, call DataBind for the GridView only if you haven't set the DataSourceID, and always wrap it with "If Not IsPostBack".
If you add/change your record via a DetailsView that is bound to the same DataSource, you don't have to call DataBind on postback, since the DataSource will notify the GridView of the change.
If you add/change your record via some other means, call DataBind after it has been changed.

Here's my stack trace for Google:
[ArgumentException: Item has already been added. Key in dictionary: 'ID'  Key being added: 'ID']
System.Collections.Hashtable.Insert(Object key, Object nvalue, Boolean add) +2904181
System.Collections.Hashtable.Add(Object key, Object value) +11
System.Collections.Specialized.OrderedDictionary.Add(Object key, Object value) +49
System.Web.UI.WebControls.DataKey.LoadViewState(Object state) +88
System.Web.UI.WebControls.DataKey.System.Web.UI.IStateManager.LoadViewState(Object state) +4
System.Web.UI.WebControls.GridView.LoadDataKeysState(Object state) +310
System.Web.UI.WebControls.GridView.LoadControlState(Object savedState) +450
System.Web.UI.Control.LoadControlStateInternal(Object savedStateObj) +118
System.Web.UI.Page.LoadAllState() +212
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +109

Saturday, 26 January 2008

WAMPed up!

You know, I'm not a big fan of LAMP. Let's say, I frown when I hear something good about it, and I hide my smile when I hear something bad. OK, at least I don't write that Windows Must Die in any other forum.

Apparently the Gods decided that it's time that I become more tolerant. First I had to make an Asp.Net site with mySql. It was sort of OK until a nasty bug deleted almost all our forums, and then they began to reappear out of some sort of mySql's recycle bin. But that's a different story.

Now I have to maintain a PHP project, and I decided that, hey, let's be tolerant! PHP is a cool object oriented language now, let's give it a try!

Installing Apache was no problem at all. I ran the default page, and it said in big bold letters, "It works!". That was cool.

Installing PHP was pretty straightforward too. But it was too late, so I went to sleep.

OK, now I'm going to check if my PHP works. Starting Apache and... there are lots of message boxes saying that httpd "This application has failed to start because php_mbstring.dll was not found" and then lots of others with different dll names. The poor beast tried again and again, so the messages won't stop until I killed it mercifully.

I decided to repair the installation and switched off all the extensions. It started nice, no message boxes. But the test PHP page I tried to view still showed nothing. In fact it showed the PHP source.

Later I discovered one possible cause. As somebody suggested, my PHP page contained something like <? phpinfo() ?>. This simplified syntax, it turned out, is switched off by default, you should write <?php phpinfo() ?> instead. Correct me if I'm wrong. But now that everything works, I can't verify this.

So, what did I do? I installed one of the available WAMP packages: EasyPHP. Works like a charm.. almost. A truly Windows-oriented application, it had minimum installation options. It even didn't ask me which of the three products (Apache, PHP, mySql) I want to install! And I was quite thankful, believe me. At least I didn't have to deal with environment paths, console commands, and huge ini files.

Saturday, 12 January 2008

Readonly properties and testability

Sometimes an execution step requires that a property value is calculated. This value is used then in the subsequent step. When you want to test the second step in isolation, you usually mock the property. If you use "traditional" mock frameworks, or manually constructed stubs, the property should be virtual, and your object should be injected into the process somehow. This makes your design extendable, since whenever somebody wants a different behaviour, she can override the implementation of this property, and inject the custom object into the process.

Recently I discovered that the most flexible solution would be to create a special object that just transfers the data between the steps. You don't have to inject anything, if you want some custom data for the second step, you just create your data object manually. Without the dependency injection and inheritance requirements, the steps become completely isolated. You need some sort of controller to drive the flow, and your users can easily implement another controller with any of the steps replaced with different objects and different behaviour.

However, sometimes you, like me, are attached to the old fashioned idea of keeping behaviour with data. You need your class to expose the property and calculate its value:

Public Overridable ReadOnly Property SomeProperty() As SomeClass

Get

Dim result As New SomeClass

'Set some properties

Return result

End Get

End Property


The obvious solution is to calculate the property if you haven't set it yet, but still make it settable:

Private _someProperty As SomeClass

Public Property SomeProperty() As SomeClass

Get

If _someProperty Is Nothing Then _someProperty = CalculateValue()

Return _someProperty

End Get

Set(ByVal value As SomeClass)

_someProperty = value

End Set

End Property


Protected Overridable Function CalculateValue() As SomeClass

Dim result As New SomeClass

' Set some properties

Return result

End Function


A similar pattern can be applied to value objects. The trick is to make the associated private variable nullable:

Private _someProperty As Nullable(Of Boolean)

Public Overridable Property SomeProperty() As Boolean

Get

If Not _someProperty.HasValue Then _someProperty = CalculateValue()

Return _someProperty

End Get

Set(ByVal value As Boolean)

_someProperty = value

End Set

End Property


Protected Overridable Function CalculateValue() As Boolean

'Do stuff

End Function


Finally, let's try a similar thing with enumerables. Typically, we expose an enumerable as a readonly property, and fill the values at the initialization stage. Here we do a similar trick, but on a certain condition. We add a boolean property AutoCalculateValues and calculate them only if it is set to true, which is the default behaviour:

Private _autoCalculateValues As Boolean = True

Public Property AutoCalculateValues() As Boolean

Get

Return _autoCalculateValues

End Get

Set(ByVal value As Boolean)

_autoCalculateValues = value

End Set

End Property

Private _someProperty As IList(Of SomeClass)

Public Overridable ReadOnly Property SomeProperty() As IEnumerable(Of SomeClass)

Get

If _someProperty Is Nothing Then

_someProperty = New List(Of SomeClass)

End If

Return _someProperty

End Get

End Property

Protected Overridable Sub CalculateValues(ByVal list As IList(Of SomeClass))

list.Add(New SomeClass)

'...

End Sub


This time, the property is not settable, but the idea that it is initialized by default but the initialization can be switched off.

So, what are the advantages? In terms of testability, we can set the desired values directly, instead of using mocks, which simplifies our life a bit, and our tests become even more clear. But the main point is about reuse. Testability with mocks implies that one can modify the behaviour of our class by inheritance. A user of our framework can create a descendant of our class, overriding the property, and inject it into the workflow, just as we do in our tests. Making the property settable, on the other hand, makes it possible to extend the behaviour by composition: a user can, for example, add another class that sets the property in response to some external conditions. Another, more important, possibility, is to set the property manually. For example, on a Web page, the block elements are usually go under each other, but sometimes you want to position your div manually. You don't override the div class, you just tell it, "don't calculate your position, I'll do it myself". Note that the old inheritance possibility is still there -- you can override the CalculateValue() method if you want.

So, the system definitely becomes more open. This will definitely make the users of your component happier, but also opens more possibilities for misuse. For example, I could set the value that would put the system into an invalid state. However, the same result can be "achieved" by inheritance, only with more efforts. So, apart from validation, the best solution is writing docs properly. And remember that what seems like misusing to you might be a perfect way of using for others.

I'm using these ideas for some time with Inka, and I discovered that my design has improved. Each time I'm tempted to mock a parameterless method, I'm trying to convert it into a property, and then make it settable. I discovered that I have less dependencies and better decoupling, less pulls and more pushes, less methods that cover all possible situations, more small classes specifically designed for each situation.

Inka Layout, take three

The next release if Inka is being delayed, again and again. I decided to rewrite the layout engine. I already wrote about that ( see here, for example).

In short, first I tried Need Driven development: implement it step by step, mocking out the next step before implementing the current. This became a disaster: too much upfront design for me. Without seeing the whole picture, I was never sure that it works, much less that it works the way it should.

My second attempt was much better. At least it began better. I implemented a quick and dirty solution first, then added a few more integration tests and made my solution a bit more complex, then began to refactor. The refactoring stage took forever though. I still haven't finished it in the last release, so I was too shy to publish the code. With only integration tests in place, I wasn't seeing the correct direction of refactoring, so I was doing it blindly, and it took ages to figure out why this particular test broke when I did this simple refactoring.

Now it's the third attempt, and I've already abandoned version 3a. But I don't want to talk about it yet, until I see the results. I should say that the trunk version is the same as used in the last release, since I decided to put the new experimental version in a separate branch.