Do not name a class the same as its namespace, Part Three

Do not name a class the same as its namespace, Part Three

Rate This
  • Comments 18

(This is part three of a four part series; part two is here, part four is here.)

Part Three: Bad hierarchical design

The reason we humans invented hierarchies in the first place is to organize a complicated body of stuff such that there’s a well-defined place for everything. Any time you see a hierarchy where there are two levels with the same name, something is messed up in the design of that hierarchy. And any time you see a hierarchy where one of the interior nodes has a single child, again, something is probably messed up.

Krzysztof points out in the annotated Framework Design Guidelines that the fundamental point of namespaces is not actually to allow you to disambiguate two things with the same name. (Ideally there would never be a situation where two things had the same name in the first place; coming up with a mechanism to enable that problem and then deal with it seems counterproductive.) Rather, the point of namespaces is to organize types into a hierarchy that is easy to understand.

That is, the point of namespaces is not just to keep similarly-named things separated, but rather, to group things that have something in common together so that you can find them. If you don’t think that there are two or more things that could go into a namespace, then it is probably not a good namespace.

My original example was a namespace MyContainers.List containing a class List. Could any other class go into MyContainers.List? No. The right design is to either move the class List into the MyContainers namespace, or to make the namespace MyContainers.Lists, plural, and have it contain more than one thing, say, MutableList and ImmutableList.

The commonality that groups a set of types into a namespace could be anything. System.Collections.Generic groups collection types by an implementation detail: they’re all generic. System.IO groups types by related functionality. Top-level namespaces like “System” and “Microsoft” group things by whether they are part of the core functionality of the platform, or are Microsoft-specific extensions to it. But the point is that each of these namespaces groups a large number of things by some shared characteristic. A namespace containing a type of the same name indicates a failure in the design of the hierarchy.

Next time: It makes a bad situation worse.

(This is part three of a four part series; part two is here, part four is here.)

  • I think you could make a pretty darn convincing argument that namespaces are in fact intended to disambiguate different things with the same name. Why are they called "namespaces" and not "categories" or "taxonomies"?

    It actually makes sense if you think about it. In many languages (C, Objective-C, PHP) the big problem with libraries is that there's no way to make sure that the names used in one library won't also be used in somebody else's library. For example, the HTML library has good reason to have a Table abstraction and the DB library has good reason to have a completely unrelated Table abstraction. Unfortunately, the only way to be able to use them together would be to have each library use a prefix, so you'd have HtmlTable and DbTable. But then you have to hope that everybody uses a prefix and that the prefixes don't collide, which could be a problem when Dan Brown decides to use Db as the prefix for his HTML library. And since the prefix has to be typed in every time one of the names is used it has to be short, which encourages collisions. Hierarchical namespaces are the perfect solution to this problem.

    As for organizing types into an easy-to-understand hierarchy, why would the compiler care? The compiler doesn't need to understand the hierarchy; programmers do. At that point it's a documentation issue which could be addressed with attributes or documentation comments, and the compiler wouldn't have to care if you use the same name for both the genus and species.

    Of course you could make the analogy to a filesystem: A filesystem has hierarchical directories to group files together, not to allow two files to have the same name. However, that's different because I frequently browse the filesystem hierarchy directly, while I only browse the BCL namespace hierarchy directly when I use Reflector. Otherwise I just browse the documentation which need not have any relation to how the compiler sees the classes the same way the Win32 API documentation is hierarchical even though it is all in a flat namespace.

    Furthermore, it seems like if the only purpose was to organize types, a type should be allowed to be in multiple places. Why should I have to decide whether ImmutableList<T> goes into MyContainers.Lists, MyContainers.Generic, or MyContainers.Immutable? I should be able to categorize it as Generic, List, and Immutable all at the same time.

  • I dont think Eric ever said that namespaces arent there to disambiguate different things. Of course namespaces are there to use in such way and should in fact be used that way.

    The point is that it should not ONLY be used thinking in that particular functionality. If you do, then having a class with the same name as the containing namespace is obviously not a problem, same as having a file named as the containing folder isnt a problem at all (to follow your analogy).

    But when you look at the  bigger picture of namespaces and that apart from helping avoid name-collisions they should be used to represent logical and well thought heirarchies, then having a namespace containing a class with the same name doesn't seem such a good idea.

  • I wasn't commenting on the actual purpose of namespaces; I was just making the argument that if somebody said "come up with a way to disambiguate different things of the same name", I would have come up with namespaces and called them "namespaces". If somebody had said "come up with a way to organize types into a hierarchy that is easy to understand", I would have come up with something entirely different which wouldn't be called "namespaces" and probably wouldn't be useful for disambiguation.

    As for naming a class the same as a namespace, let's say I have a class called Animal, and from that I derive classes like Mammal and Amphibian, and so on. What's a good namespace for this collection of classes? "Animals" seems like as good a name as any. Now what do I call a class that is a container for a bunch of objects all derived from Animal? "Animals" seems like a perfectly good name for that, too. Of course I could call it AnimalList or AnimalCollection or IEnumerableOfAnimals, but Animals is just as meaningful without implying anything about the underlying datastructure.

    Is my hierarchy so ill-thought-out that I have ended up with a name collision? Or is it just that sometimes logical names happen to coincide? Consider this completely contrived hypothetical example: "namespace First.Second { class Hour{} class Minute{} class Second{} }".

    The one time this bit me was in generated code. It automatically put all the generated types into a namespace Foo.Types, and one of the generated types ended up with the name Types.

  • Well, for me AnimalCollection seems far better than Animals because it makes it obvious that it's collection of animals. Moreover it's not a good idea to have two classes within the same namespace that differ only in single letter. If you stick to .NET naming guidelines (namespaces in plural, collections with Collection suffix) then you won't run into this kind of problems.

    I can agree that as you said "sometimes logical names happen to coincide", but in my opinion it's a sign that your hierarchy wasn't as well thought out in the first place, so it's probably time for some refactoring.

    BTW, your second example is so contrived that it should be taken out and shot. ;)

  • > Now what do I call a class that is a container for a bunch of objects all derived from Animal? "Animals" seems like a perfectly good name for that, too. Of course I could call it AnimalList or AnimalCollection or IEnumerableOfAnimals, but Animals is just as meaningful without implying anything about the underlying datastructure.

    That's why we have common naming guidelines, which say that you should have called your class "AnimalCollection", and the namespace, "Animals".

    >  Or is it just that sometimes logical names happen to coincide

    public Color Color { get; set; }

    > The one time this bit me was in generated code. It automatically put all the generated types into a namespace Foo.Types, and one of the generated types ended up with the name Types.

    Which is why C# (and VB) still does let you reference such a type, even if not in the most convenient way.

  • @Gabe

    sorry but the animals example doesnt work for me. The class should be names AnimalCollection which is the way the Framework is designed too: System.Windows.Forms.FormCollection, not System.Windows.Forms.Forms

    I see no name-collision at all. Having to name a class slightly differently without losing one single bit of the name's meaning is not such a big deal IMHO.

  • I guess my point wasn't quite clear enough. I meant that having a class named Animals in a namespace named Animals doesn't indicate a failure in the design of the hierarchy -- it merely indicates a failure to follow the .Net naming conventions.

  • Well, it depends on how you look at it. I consider it a flawed hierarchy because it has a flawed naming convention, even if its conceptually sound. Naming is many times just as important. You could create a great hierarchy from a conceptual standpoint but still manage to make it almost completely useless through horrible naming conventions. So IMHO having a class named Animals in a namespace named Animals IS a flawed hierarchy...not conceptually but still flawed by a serious usability issue.

    When you think up the hierarchy its easy to spot the Animals issue beforehand and name the class AnimalCollection.

    If you validate hierarchy design only from a conceptual standpoint then I understand your point of view. I try to not separate conceptaul correctness from sound naming when I design as both can normally go hand in hand without it being a problem.

  • Well, I'm (mostly) with Gabe on this one. Namespaces are primarily about partitioning the namespace.

    Any sort of taxonomy is a secondary concern. That's not to say it is unimportant, but it is not the *primary* purpose of the language feature. Perhaps C#'s namespaces were more inspired by Java packages, which are more about the classification.

    However overdoing the hierarchical thing can lead to more fragile code as too many assumptions are often pushed into a non-functional aspect of the code.

    I had a similiar discussion on Stackoverflow here: http://stackoverflow.com/questions/2108172/c-namespaces-comparison-to-java-packages/2108209#2108209

  • Design is what is more of us (starters) do not care a lot about and are more interested in getting things done. Do appriciate your effort as you higlight some very basic things which are of really great importance.

  • I like the namespaces which ends with "Model", such as XY.ObjectModel, XY.ServiceModel, XY.TableModel, XY.SecurityModel and so on. I found it rather practical, becasue it express some kind of 'abstraction', which are independent by its nature, and they are 'wide' enough to serve as a base for 'taxonomization'.

    The namespace 'authoring' (hehe) is usually easy if you develop a platform, but much harder to find good namespaces if you create a complex business application, which has complex dependencies betwenn components, through workflows for example. You create namespaces for responsibility layering usually, and for 'componentization', but they are normal to each other. My question is which goes fist? Responsibility layering namespaces or logical component namespaces?

  • Gabe: "And since the prefix has to be typed in every time one of the names is used it has to be short, which encourages collisions."

    You can create short namespace aliases for solving this problem. eg.:

    using OM = XY.ObjectModel;

    OM.User user = OM.User.Create(...);

    Using short aliases for namespaces are rather common. When you create n-tier application, where the entitites has type representation in every tier in worst case, that can be tricky scenario. Or when you create DTO-s for your entities, you also face with this problem.

  • Gabe: "However, that's different because I frequently browse the filesystem hierarchy directly, while I only browse the BCL namespace hierarchy directly when I use Reflector."

    No! When yo got a new library you should browse the contained types and ns sturcutre with the VS Object Browser at least, or browse the API doc, it takes only 5 or 10 mintutes. If that library is well structured, that provide a big picture about the library. You will know the main contepts, ideas by simply visiting how the types are strutured in the assembly. It gives some important information about the 'maturity' of that component. If you find some clear concepts, or guiding principle in the first 5-10 minutes, that library is a mature product probably. Well created namespace structures are very important.

  • Fix: "I like the namespaces which ends with "Model", such as XY.ObjectModel, XY.ServiceModel, XY.TableModel, XY.SecurityModel and so on. I found it rather practical, becasue it express some kind of _aspects_, which are independent by its nature, and they are 'wide' enough to serve as a base for 'taxonomization'."

    Separating types by aspects can be good idea.

    ps. Sorry for flooding.

  • Szindbad: When I said "And since the prefix has to be typed in every time one of the names is used it has to be short, which encourages collisions.", I was talking about languages like C and Objective-C that don't have namespaces. That's why all functions in Xlib start with X and all Cocoa functions start with NS. Nobody would want to type XWindowSystemCreateWindow, but if they had namespaces they would be able to have something like "using XWindowSystem; CreateWindow..."

    And when I said I only browse the BCL namespace directly with Reflector, I meant that I use the documentation or an object browser to look at the hierarchy. In other words, I use some other tool to show me the hierarchy, which tool could have generated the documentation from some metadata besides the namespace mechanism. And the metadata that generates this documentation need not form a strict tree, as the namespace mechanism does;it could form a graph instead, for example.

    As I said before, Foo.ImmutableList<T> should be able to be categorized under GenericCollections, Immutable, and Lists. A strict tree structure does not allow this but other forms could.

Page 1 of 2 (18 items) 12