The interaction of encapsulation and properties in D
"Luís
"Luís
Wed Jul 10 19:55:09 PDT 2013
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?
More information about the Digitalmars-d
mailing list