www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - The interaction of encapsulation and properties in D

reply =?UTF-8?B?Ikx1w61z?= Marques" <luismarques gmail.com> writes:
Maybe this has already been discussed (if so sorry), but I would 
like to ask your opinion about the following.

The condensed version: because we have properties, is it wise to 
use public member variables in D, in some circumstances?

The long version, starting with the context: AFAIK, standard 
encapsulation wisdom says you should never expose member 
variables directly, like this:

     class X
     {
         public int phone;
     }

     void main()
     {
         X x = new X();
         x.phone = 555123;
     }

The usual admonitions apply, such that you might want to change 
the internal representation of the 'phone'. So, as with every 
problem in life, you decide to add one more level of indirection, 
and you implement some form of getters and setters:

     // in Java or in D without using properties...
     class X
     {
         public int _phone;

         void setPhone(int v)
         {
             _phone = v;
         }

         int getPhone()
         {
             return _phone;
         }
     }

     void main()
     {
         X x = new X();
         x.setPhone(555123);
     }

With such encapsulation, once you realise the silliness of having 
a phone number stored in an int, this is easy to change without 
breaking the client code:

     // in D without using properties
     class X
     {
         public string _phone;

         void setPhone(int v)
         {
             _phone = to!string(v);
         }

         void setPhone(string v)
         {
             _phone = to!string(v);
         }

         int getPhone()
         {
             return to!int(_phone);
         }
     }

     void main()
     {
         X x = new X();
         x.setPhone(555123); // did not break the old client code
         x.setPhone("555 123"); // the new stringified API also 
works
     }

Of course, in D we would do this with properties instead:

     class X
     {
         public string _phone;

          property void phone(int v)
         {
             _phone = to!string(v);
         }

          property int phone()
         {
             return to!int(_phone);
         }

          property void phone(string v)
         {
             _phone = v;
         }

          property string phoneString()
         {
             return _phone;
         }
     }

     void main()
     {
         X x = new X();
         x.phone = 555123;
         x.phone = "555 123";
     }

But when we use the property syntax to implement the 
encapsulation the client code does not have to change; there is 
source code compatibility at the client side: the same client 
code can be used for both the the direct member variable 
implementation and the property accessors implementation, as long 
as we are willing to recompile. The client code is already 
shielded from such implementation issue; in a certain sense the 
'phone' "field" was already encapsulated.

Why, then, should one use (traditional, property based) 
encapsulation *proactively*? After all, if we later decide that 
we want to sanitize the setter inputs, or add logging, or trigger 
an action, or something like that, we can always refactor the 
public member variable into the respective property accessors.

So why not, instead, just use a simple public member variable, 
when that is cleaner (less boilerplate code), more 
straightforward, and not obvious that we will really need the 
extra indirection?

A case where such reasoning would not apply is when source code 
compatibility is not enough, such as when you have a D library. 
But this could be solved by something like...

     class X
     {
          property public int phone;
     }

...which would under the covers generate getter and setter 
properties, so that if you later wanted to encapsulate it without 
breaking binary compatibility you could do so.

So, summing it up: even assuming that performance is not an 
issue, does the advice to always encapsulate your member 
variables (as one would do, for instance, in idiomatic Java) 
actually make sense for D, or would you recommend using public 
member variables when that is more straightforward, and the 
indirection is not yet needed?
Jul 10 2013
next sibling parent =?UTF-8?B?Ikx1w61z?= Marques" <luismarques gmail.com> writes:
(Of course I forgot to change the variable to private in the 
versions with accessors)
Jul 10 2013
prev sibling next sibling parent "Jesse Phillips" <Jesse.K.Phillips+D gmail.com> writes:
On Thursday, 11 July 2013 at 02:55:24 UTC, Luís Marques wrote:
 So, summing it up: even assuming that performance is not an 
 issue, does the advice to always encapsulate your member 
 variables (as one would do, for instance, in idiomatic Java) 
 actually make sense for D, or would you recommend using public 
 member variables when that is more straightforward, and the 
 indirection is not yet needed?

In general it seems people just use public fields. (I do, and sometimes annotated with property). Considering the history this makes sense; optional parens "addressed" properties (see property discussions). On of the main problems has been that public fields can be passed to ref parameters while this is not true for getters. I see this as a deficiency in property but don't recall all the detail of the problem.
Jul 10 2013
prev sibling next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Thursday, July 11, 2013 04:55:09 =?UTF-8?B?Ikx1w61z?=.Marques 
<luismarques gmail.com> puremagic.com wrote:
 So, summing it up: even assuming that performance is not an
 issue, does the advice to always encapsulate your member
 variables (as one would do, for instance, in idiomatic Java)
 actually make sense for D, or would you recommend using public
 member variables when that is more straightforward, and the
 indirection is not yet needed?

In general, I would strongly advise against using public fields if you ever might want to replace them with property functions. The reason for this is that property functions really don't do all that great a job of emulating variables. For instance, taking the address of a variable gives you a completely different type than taking the address of a function does, and variables can be passed by ref, whereas the result of a property function can't (unless it returns by ref, which would ruin the encapsulation that it provides). In general, if you use public variables, you're just going to cause yourself trouble in the long run unless they stay public variables forever. - Jonathan M Davis
Jul 10 2013
parent Jacob Carlborg <doob me.com> writes:
On 2013-07-11 07:26, Jonathan M Davis wrote:

 In general, I would strongly advise against using public fields if you ever
 might want to replace them with property functions. The reason for this is
 that property functions really don't do all that great a job of emulating
 variables. For instance, taking the address of a variable gives you a
 completely different type than taking the address of a function does, and
 variables can be passed by ref, whereas the result of a property function
 can't (unless it returns by ref, which would ruin the encapsulation that it
 provides). In general, if you use public variables, you're just going to cause
 yourself trouble in the long run unless they stay public variables forever.

I use public fields sometimes but as you say, there are problems. One can also run into problems with returning structs by value. The compiler would also need to do property rewriting to work flawlessly. -- /Jacob Carlborg
Jul 11 2013
prev sibling next sibling parent Jacob Carlborg <doob me.com> writes:
On 2013-07-11 04:55, "Luís Marques" <luismarques gmail.com>" wrote:

 A case where such reasoning would not apply is when source code
 compatibility is not enough, such as when you have a D library. But this
 could be solved by something like...

      class X
      {
           property public int phone;
      }

 ...which would under the covers generate getter and setter properties,
 so that if you later wanted to encapsulate it without breaking binary
 compatibility you could do so.

I would really like to have this. It's like in Scala, all public fields are implemented with methods. -- /Jacob Carlborg
Jul 11 2013
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Thursday, July 11, 2013 09:37:56 Jacob Carlborg wrote:
 I use public fields sometimes but as you say, there are problems. One
 can also run into problems with returning structs by value. The compiler
 would also need to do property rewriting to work flawlessly.

Property rewrites would definitely help (and I agree that we should have them), but they're far from enough to make variables and property functions interchangeable. I think that the only way that that could be done is if public variables could have additional restrictions put on them somehow (e.g. make it illegal to take their address or pass them by ref). Otherwise, there will always be a few things that will work with a public variable but not a property function. A programmer can certainly choose to use public variables and just put up with broken code when swapping them with property functions later if you're in control of all your code (especially if you rarely use public variables in contexts where a property function would be illegal), but if you're releasing a public API which could be used by anyone, then I would argue that it's far better to just not use public variables, because the risk of code breakage is too high to be worth it. Also, if we could make it so that something like property int value; got lowered to property functions for you, then we could eliminate the boilerplate that frequently leads people to want to make member variables public rather than declaring property functions. - Jonathan M Davis
Jul 11 2013
prev sibling next sibling parent "Dicebot" <public dicebot.lv> writes:
On Thursday, 11 July 2013 at 02:55:24 UTC, Luís Marques wrote:
 ...

I often find it cleaner to consider D minimal hard encapsulation unit to be a module, not a class - it just maps naturally to protection and module system. That means avoiding aggregate methods as part of stable API/ABI. The latter is also an issue with directly exposing public fields - while paren-less call allows to replace them with getter/setter with no syntax (API) change, ABI will change anyway.
Jul 11 2013
prev sibling parent "Mike James" <foo bar.com> writes:
On Thursday, 11 July 2013 at 07:50:38 UTC, Jonathan M Davis wrote:
 On Thursday, July 11, 2013 09:37:56 Jacob Carlborg wrote:

 Also, if we could make it so that something like

  property int value;

+1 It would be a great timesaver and give a reason for property to exist :-) -=mike=-
Jul 11 2013