Do non-member functions improve encapsulation in D?

Gary Willoughby via Digitalmars-d digitalmars-d at puremagic.com
Mon Apr 21 03:21:54 PDT 2014


On Monday, 21 April 2014 at 08:33:21 UTC, Lars T. Kyllingstad 
wrote:
> This is the tricky part, an it is where I have a hard time 
> deciding which to use.  For example:
>
>     struct File {
>         private int fileno;
>         void read(ubyte[] buf) {
>             core.sys.posix.unistd.read(fileno, buf.ptr, 
> buf.length);
>         }
>     }
>
> Why, or when, is the above preferable to the following?
>
>     struct File {
>         private int fileno;
>     }
>     void read(File f, ubyte[] buf)
>         core.sys.posix.unistd.read(f.fileno, buf.ptr, 
> buf.length);
>     }
>
> I still haven't heard any fact-based, logical arguments that 
> advise me on which style to use, and so far it seems to be just 
> that -- a matter of style.

In this case i would say go with the method and not a non-member 
function. There are two reasons for this. First the method uses 
private state which is itself a good indicator. Second 'read' is 
an action of 'File'. This is encapsulation in action because you 
are defining logical actions to be performed on the state of a 
class. It's a well formed unit with associated behaviours.

It is confusing to think 'if this was a non-member function in 
the same module i can also access use the private state'. Yes 
that's true but just because you *can* access it, it doesn't mean 
you should! In fact you should have a very well defined reason 
for doing so.

Classes should nearly always be nouns and methods nearly always 
be verbs. Nearly always because there are always exceptions. For 
example i like to name methods which return bool's starting with 
'is'. e.g. isOpen, isRunning, etc. The rule is follow 
encapsulation[1] as much as possible when designing classes.

I found accessing private state in a module is useful when 
*initialising* said state when constructing objects. In this case 
the module can act like a very helpful factory. In a recent 
project i have a rigid class design of an application and it's 
child windows. Other windows should be able to be opened and 
their id's generated automatically and internally. These id's are 
only available as read-only properties. One window in particular 
i needed to create with a specific non-generated id. I couldn't 
include this data in the constructor as i don't want anyone to 
control the id's but *i* needed to this one time. This case 
fitted well into the D module design and allowed me to create a 
window, override its private id and move on knowing that could 
not be tampered with by anything else. This design turned out to 
be very clean and without any baggage of a unnecessary setter 
methods or constructor parameter.

I used to be of the ilk that thought all programming could be 
done using only classes and designing everything in a very strict 
OOP way. D has broken that way of thinking in me purely because 
of things like UFCS and a module's private access to members. Now 
i understand that you can actually achieve a cleaner design by 
moving towards these things instead of having everything as a 
class. First and foremost you must try and achieve a good OOP 
design, this is essential. Then use UFCS and module private 
access features to keep things clean and simple.

Keep things logical, simple and straightforward.

[1]: 
http://en.wikipedia.org/wiki/Encapsulation_(object-oriented_programming)


More information about the Digitalmars-d mailing list