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)

Reply via email to