The buddy classes are drowning DRY
The notion of buddy classes has appeared at first in ASP.Net Dynamic Data, and now has made its way to RIA services. I don’t really bother to keep track with all the new tech from Microsoft, but this post came to my attention.
What is a buddy class? Let us take this example as an example, which was generate by a tool:
public partial class MyClass { public int MyProperty { get; set; } }
And say that we need to add some attribute to that, to add instruction for tools support. We can’t do that, because MyClass is generated by a tool. ASP.Net Dynamic Data came up with the following scheme:
[MetadataClass(typeof(MyMetadataClass))] public partial class MyClass { public int MyProperty { get; set; } } public class MyMetadataClass { [Range(1,100)] public int MyProperty{ get; set; } }
This make me feel ill.
DRY is one of the few truly universal CS principles, and here we have a idiom that not only ignores it, but encourage repeating ourselves over and over and over again.
No doubt this is an easy solution, easy to document, easy to implement, easy to work with. It is also a maintenance nightmare, and cause sever harm to the project. If you want to create external metadata to generated classes, there are other ways of doing that, violating DRY as a systematic approach is a mistake that will come back to haunt you.
Comments
You're not the only one hating buddy classes - an ugly hack that presumably was quicker than doing the proper thing.
[)amien
wow buddy classes, I hadn't heard of it yet, but this looks ugly. Hope too never see it again.
It is a huge mistake taking this "buddy" class approach. If only they had extended the tools to allow the flexibility to add attributes to properties.
The whole thing sets off a South Park line: "I'm not your buddy, pal. I'm not your pal, friend."
Hi found this one pretty horrible as well when I seen Mikes post. Funny thing is the MS DSL tools have their own, much better, solution to this problem, i.e. the double-derived pattern.
Well at least there is a way to solve the problem (customize the metadata of original classes). I do not like it much, but I can not really criticize the idea (after all, NHibernate was using XML for similar task for years and most people were ok with that).
What approach would you prefer? I would say use lambda expressions, hopefully they will get to that. Otherwise, double-derived works pretty well, I was always using it for any codegen I did.
Thankfully in Dynamic Data (not sure about RIA Services yet) you can replace the Metadata provider to something else if you'd like (xml, fluent interface etc).
Andrey,
I actually really like NH's XML style.
I think they are laziness on the part of the tool that is generating the code. The tool should provide hooks to add attributes without a clunky solution like this.
Couldn't agree more.
If you're writing a buddy class just to add some behavioral attributes, XML is a much cleaner way. In the example Oren has given (validation) the visitor pattern should have been used.
When Microsoft started asp.net MVC, I thought yeah, they are finally gonna start using patterns and practices themselves instead of advocating them to the world. But it was just a fluke.
Just my 2 cents
Having an external XML file still violates the DRY principal doesn't it?
Buddy classes and XML both seem to promote not getting compile time help.. maybe .NET needs a better (consistent) story for 'stood off' data attributing.
@Stephen: The problem is not the generation of code with or without attributes. If you have ever programmed WinForms in .net 1.x VS generated code in the same file as you was writing your code. Partial classes were introduced into .net 2.0. Using a partial class to separate generated code from user written code seems a good thing at first. Most problems in 1.x were not related to the generated code, but generated code changed by the user that needs to be parsed back for use with a designer.
As I wrote earlier, Dynamic Data was probably better off implementing the visitor pattern. And what about keeping to two classes in sync when the data model changes.. Another benefit when using the visitor pattern is that the developer has auto completion and (code) validation at compile time.
Maybe David Hayden should give a few P&P presentations to some Microsoft architects?
@Dave, ok but I'm not sure how that effects what I said- my comment was purely that XML and especially buddy classes are imperfect for attributing data (and I'm not specifically talking about System.Attribute, I'm talking generally about how members can have data attached to them)..
The most simplistic example I could give was.. I have this 'fact' about property X.. how do I let people find that? attributes would be a good way, but what if I cannot attribute it.. tons of systems have this requirement but there seems to be three main ways people do this: attributes (not always possible but good), xml/config (great but the compiler won't help you anymore).. or by directly interacting with the related service (such as fluent interfaces) I'm wondering if theres a better way..
I have no idea why they didn't do something like the way Design attributes are broken out in WPF, and use something similar to 'IRegisterMetadata'. Insanely simple, very DRY, and doesnt interfere with either the generation tool, or the generated code. it also avoids the double inheritance paradigm, which is also just another 'work around'.
It's getting a bit old with these NIH/ their code sucks posts. At least when "there are other ways of doing that" is not followed by any suggestions on how to do it better.
I certainly agree that the code smells, but it's a working solution to a problem caused by an oversight in the language.
The MetadataClass is akin to a FNH mapping class, although the relation between the classes is decorated to the primary class. A FNH approach could be better, but the discovery process requires a bit more from the framework.
public class MyClassMetaData : MetaData <myclass
{
}
It's a heap load of connascence, and that's certainly a bad thing!
It's one thing to be lazy about design in writing application code; HOWEVER being lazy with Framework Design can lead to structural failures across many applications going forward. Beyond the DRY rot, there is a serious loss in readability and communication of intent, as behavioral abstractions have leaked out of MyClass.
The CLI team needs to revisit Properties with .NET 5. The issue of tooling (IDE & compiler) for Attributes and ChangeNotification (XAML binding) including wiring up events (particularly with partial classes).
Want more buddy hell?
blog.dotnetwiki.org/.../...odelsWithContracts.aspx
Code contracts is another official feature coming out of MS that encourages the same practice.
@ Daniel
To quote the post you link to: "Since interfaces cannot have a method body, we use a ‘buddy’ class to express those contracts".
So you only need to use a 'buddy' class for interfaces, classes don't need them, because there the contracts can be expressed in the methods directly, no buddy class needed.
and to top it off
"All implementations of that particular interface will be instrumented with the runtime checks by the rewriter at compile time."
excellent DRY instead of buddy hell methinks...
Why don't they just allow you to annotate the interface itself with the contracts rather than having to maintain the "buddy" ?
Comment preview