Partial class implementation

Robert Fraser fraserofthenight at gmail.com
Tue Jul 17 14:30:46 PDT 2007


janderson Wrote:

> Classes should be small and as minimal as possible.  I think this is 
> part of Meyers argument.  Anything that can be placed outside the class 
> should be.  If it can't because that gives assess to something that 
> could potentially be misused by the world outside the class, thats a 
> case to make it a member.  Therefore you get this highly easy to use 
> class that is less bug prone.  It knows what is job is, its small so its 
> easy to maintain.
> 

I agree so far...

> > In particular the getter/setter/property pattern is
> > encouraged by such a design, and overuse of this pattern can degrade a
> > class to a simple aggregation of data, defeating the purpose of
> > encapsulation entirely.
> >> 
> 
> This is true.  I don't think the example of a point was the best 
> example. Herb Sutter and Bjarne Stroustrup's often argue that if the 
> class variables have not constraints (invariants) then the class should 
> really be a C struct (where everything is public).

I disagree here, because you are making the assumption that there will never be a need to refactor (with D's property syntax, this is somewhat mitigated since a public field can be turned into a method without breaking client code, however if something that was a stack-allocated class later needs to be given dynamic dispatch, this is not as much as possibility).

Further, however, this mindset encourages thinking of classes/structs as aggregations of data. For a better example than a point, think about an AST node in a compiler (what I was thinking about when I wrote the OP, though now we're pretty off-topic). A generalized AST node will have a set of children and a parent, and a subclass implementing a conditional expression may have a condition, a then expresson and an else expression. It's possible that all this should be properties with getters and setters. However, if you think more deeply about it (I'd suggest staring at a picture of a bonsai tree...), the parent, condition, then expression and else expression will only be set once, and, depending on how you generate the tree, this may be at construction time. So, having setters for these properties is unnecessary and potentially dangerous if other coders will be using your class.

In fact, very few classes I write have explicit getters/setters/properties. Classes, IMO, are aggregations of methods, not data, and the data serves to hold state relating to invocations of those methods.

Which, I guess, is the main philosophical difference between the ways we approach defined types. I (coming from a Java/C# background), quite simply don't use free functions except in very rare situations where they don't fit within a class. There's room for two different kinds of programmers in this world.

> > 2. The architecture is less nimble for client code. If a function
> > that at one time needed only access to the public interface later
> > needs member access, the non-member function will become a simple
> > proxy for the member function, or, worse, the public interface could
> > be expanded.
> 
> But this is a good thing.  It means you either have to go back to the 
> drawing board with the class interface or add the non-member into the 
> interface. Its pretty easy to remove a non-member function.  Its much 
> harder to remove a function once it becomes part of a class.  Note that 
> if you want to extend a class there are other ways to do it (namely 
> using a  component).

Why is it easier to remove a non-member function than a member function? Client code relying on the function will break either way.

> Also if you find your creating proxies, then there was probably 
> something wrong with the design in the first place.

Initially, yes. But if you're refactoring your code and something that was a free function needs to become a method (needs access to private members), what other choice do you have (if client code is already using the function)?

> > But to each his own, I guess. That article certainly had some good
> > points.
> > 
> > That said, I was thinking about partial class implementation mainly
> > with regards to related virtual functions (i.e. Implementing the same
> > abstract/interface/override function across multiple subclasses all
> > in the same file, while other parts of those classes are defined
> > elsewhere. This will help logically group large class hierarchies,
> > IMO.
> 
> While I'm not against the idea.  I think, its a much better idea to have 
> lots of small cohesive component classes rather then a large class. 
> I've seen so many large classes and I've always been able to break them 
> down into small chunks.  I must admit though it can sometimes be quicker 
> in the short run to simply throw everything into the same class.

I didn't mean a large class, I meant a large class _hierarchy_ (of smaller classes). I was thinking specifically about how, in the DMD source code, Walter added the inlineCost method to every AST node in a single file (inline.c), which, IMO, is cleaner than implementing the inlineCost method along with all the other stuff relating to that AST node.

Simpler example, to make it clear what I meant (assume these classes are large enough to justify being in separate modules):

Version 1 (now):
-------------
module animals.core;
interface IHungryAnimal
{
    public void eat();
}
----------
module animals.mammals;
class Bat : IHungryAnimal
{
    public void fly() { ... }
    public void eat() { ... }
}
--------------
module animals.anphibiphans;
class Frog : IHungryAnimal
{
    public void ribbit() { ... }
    public void eat() { ... }
}

++++++++++++++++
++++++++++++++++
++++++++++++++++

Version 2 (With partial classes):
--------------
module animals.mammals;
partial class Bat
{
    void fly() { ... }
}
--------------
module animals.anphibiphans;
partial class Frog
{
    void ribbit() { ... }
}
-------------
module animals.eating;
interface IHungryAnimal
{
    public void eat();
}
partial class Bat : IHungryAnimal
{
    public void eat { ... }
}
partial class Frog : IHungryAnimal
{
    public void eat { ... }
}




More information about the Digitalmars-d mailing list