A question on naming schemes - when frequent collissions with the BCLs are present

A

Anders Borum

Hello,

(this question has been on my mind for some time now, so I'm just trying to
describe the problem the best I can and hopefully get the discussion
started.)

I'm looking for suggestions, feedback and advice regarding naming schemes in
public APIs where collissions between business objects (BOs) and types in
the BCL are frequent. I'm working on a API (for a CMS) that provides all the
well-known tiers (data, repository etc.), including a few UI tiers, that
extend types in ASP.NET (e.g. Page, MasterPage, UserControl).

As far as I'm concerned, recommendations and best practices suggest naming
business objects (or entities in general) according to their primary
function or usage (e.g. User, Role, Page). When APIs that co-exist in the
same domain, there is a desire to use the same name for an entity. If the
developer wants to use these APIs at the same time, he is forced to either
fully qualify all type references (which is bloated and unreadable) or alias
namespaces (also leads to less readable code).

After much thinking back and forth, this situation lead me (with much
resistance) to prefix my public business objects with "Cms" (look at
SharePoint, they went with "SP" are prefix for all public types). I'm
uncertain, whether this is the right decision because it feels both right,
so to speak;

Easy as in "it's easy to distinguish objects in the CMS API from similar
types in the BCL" and wrong as in "prefixing feels bloated and less clean".
Here are a few examples from two assemblies in the API.

[SphereWorks.Cms.Data].Page (reside in seperate assembly)
[SphereWorks.Cms.Publishing.Model].CmsPage (reside in seperate assembly,
describes a hierarchical node in a navigable tree)

[SphereWorks.Cms.Data].User
[SphereWorks.Cms.Publishing.Model].CmsUser

and in the UI department:

[SphereWorks.Cms.Web.UI].CmsWebPage (subclasses System.Web.UI.Page,
represents a templated control)

...

As you can see from above, the term "Page" perfectly describes an entity or
business object in each domain, yet they share the same name. A developer is
typically going to work with several similar types at the same time;

// override method in CmsWebPage (subclasses System.Web.UI.Page)
protected override void OnLoad(EventArgs e)
{
CmsHttpContext context = CmsHttpContext.Current;
CmsPage page = context.State.Page;
Page template = this.Page;

new ControlLoader(context).Load(page, template);
}

using namespace aliasing, the following scenario is possible:

// override method in CmsWebPage (subclasses System.Web.UI.Page)
protected override void OnLoad(EventArgs e)
{
Model.HttpContext context = Model.HttpContext.Current;
Model.Page page = context.State.Page;
Page template = this.Page;

new ControlLoader(context).Load(page, template);
}

Highest priority should be "look and feel" of the API to the developer; it
feels painful to force the developer to alias namespaces each time they want
to interact with the API because of collissions.

(Now that I'm feature complete, I'm refactoring the entire API for unit
testing and I guess that the introduction of "I" (as in IUser, IPage,
IContext etc.) is going to let the developer work against the interfaces
instead of the concrete business objects.

As mentioned before, the SharePoint team decided to go with the "SP" prefix,
which at first seemed unnatural, but with years of experience from this API
(unfortunately ..), it seems quite natural and it also makes it easy to
share info with other developers ("just get the SPWeb and .." or "yes, the
SPFile object supports ..").

(thanks in advance)

With regards
Anders Borum / SphereWorks
Microsoft Certified Professional (.NET MCP)
 
P

Peter Duniho

[...]
As you can see from above, the term "Page" perfectly describes an entity
or
business object in each domain, yet they share the same name. A
developer is
typically going to work with several similar types at the same time;

I'm quoting this passage because IMHO it's one of the most central points
here. In particular, I'm not convinced that "the term 'Page' perfectly
describes an entity or business object in each domain". In a context
where the options are limited, perhaps "Page" all by itself is
sufficient. But perfection is hard to come by, and in the examples you're
citing, it's clear that you have a variety of different kinds of pages,
and that there may in fact be a "more perfect" description than simply
"page".

Even the code you posted is suggestive of this point:
// override method in CmsWebPage (subclasses System.Web.UI.Page)
protected override void OnLoad(EventArgs e)
{
CmsHttpContext context = CmsHttpContext.Current;
CmsPage page = context.State.Page;
Page template = this.Page;

new ControlLoader(context).Load(page, template);
}

The mere fact that one variable you have named "page" and the other you
have named "template" strongly suggests to me that whatever kind of "Page"
is referred to by "CmsWebPage.Page" is in fact a special "template-y"
page, and thus the type name could easily incorporate that information (or
even be simply "Template" instead of page, for that matter).

The fact is, I don't think that prefixing is really all that bad an
approach, when it's really necessary. I would prefer to use namespaces
(or even type containment where appropriate) to qualify type names, and
leave the type names as simple as possible. But as you've already noted,
this can get out of hand when you're dealing with types that are by design
going to be used regularly in contexts that require qualification.

There's lots of precedent for using prefixes, and it's not the end of the
world to follow that approach in .NET.

But, the very first thing I'd look at always is whether the simple name
that seems so "perfect" for so many different types really is in fact that
perfect. I know in my own code, I have found that as appealing as a
simple one-word type name is, even absent a naming conflict it turns out
that using a more specific, more descriptive name is helpful, and of
course doing so helps resolve any potential naming conflicts that might
crop up.

I can't speak for your own code base. For all I know, you've got one of
the few exceptions where that approach just doesn't work. But, the fact
that your post doesn't mention that as a possibility in other situations,
and that your own code seems to suggest viable alternatives along those
lines, makes me wonder if you've really sufficiently considered that as an
option.

Pete
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Top