Thread overview
Passing reference data to class and incapsulation
Nov 28, 2014
Uranuz
Nov 28, 2014
bearophile
Nov 28, 2014
Uranuz
Nov 28, 2014
mark_mcs
Nov 28, 2014
mark_mcs
Nov 28, 2014
Daniel Kozák
November 28, 2014
In D we a several data types which are passed by reference: dynamic arrays, associative arrays. And sometimes we need to pass these reference data to class instance to store it inside. One of the principles of object-oriented programming is incapsulation. So all class data should be only modyfiable via class methods and properties. But I we pass reference data to class (for example as parameter in constructor) we still can change these data from initial code and break some internal logic of class for modifying these data. Example:

class Foo {
    this(int[] b) { bar = b; }
    private int[] bar;
    //Some methods
}

void main() {
    int[] bar = [1,2,3,4,5];
    Foo foo = new Foo(bar);
    //There I could do some logic with class
    bar[2] = 6; //I modify class without some checks from class
    //And there I might pass *bar* somewhere outside and break incapsulation
}

Same situation happens when I assign reference data to properties. I can check or do something with data at the moment of assignment, but I can't control that someone will modify using initial reference from outside. So do you copy reference data in constructors or properties? Should it be? Or call site should be responsible for not escaping reference somewhere outside and not modifying these data badly?

There also possible some situations when these data should be shared between different pieces of code (for example different class instance could reference the same memory area). But I think this is not very good and safe approach and should be avoided if possible.

But I think storing reference to class inside another class could be good approach because there could be different type of relations between classes. For plain data types we often have types of relations: *ownership* or *aggregation*. But classes can usualy have more types of relations: *usage* or when one class subscribes for events of another.

Is there some good links to read for these questions that you could advice. After several hours of googling I haven't found good topics about these problems. And I have not enough time for reading big book.
November 28, 2014
Uranuz:

> Same situation happens when I assign reference data to properties.

Someone has suggested to solve this problem with an attribute, like "owned", that forbids to return mutable reference data owned by a class/struct instance.

Bye,
bearophuile
November 28, 2014
> Same situation happens when I assign reference data to properties. I can check or do something with data at the moment of assignment, but I can't control that someone will modify using initial reference from outside. So do you copy reference data in constructors or properties? Should it be? Or call site should be responsible for not escaping reference somewhere outside and not modifying these data badly?

I'd probably do one of three things:
    *  Clone a private copy;
    *  Pass the parameter as an immutable ref;
    *  Change the method signature to take a std.typecons.Unique!T
to express the fact that I want transfer of ownership.

Try to encode as much information as you can into the method
signature to describe your intentions.
November 28, 2014
V Fri, 28 Nov 2014 06:19:37 +0000
Uranuz via Digitalmars-d-learn <digitalmars-d-learn@puremagic.com>
napsáno:

> In D we a several data types which are passed by reference: dynamic arrays, associative arrays. And sometimes we need to pass these reference data to class instance to store it inside. One of the principles of object-oriented programming is incapsulation. So all class data should be only modyfiable via class methods and properties. But I we pass reference data to class (for example as parameter in constructor) we still can change these data from initial code and break some internal logic of class for modifying these data. Example:
> 
> class Foo {
>      this(int[] b) { bar = b; }
>      private int[] bar;
>      //Some methods
> }
> 
> void main() {
>      int[] bar = [1,2,3,4,5];
>      Foo foo = new Foo(bar);
>      //There I could do some logic with class
>      bar[2] = 6; //I modify class without some checks from class
>      //And there I might pass *bar* somewhere outside and break
> incapsulation
> }
> 
> Same situation happens when I assign reference data to properties. I can check or do something with data at the moment of assignment, but I can't control that someone will modify using initial reference from outside. So do you copy reference data in constructors or properties? Should it be? Or call site should be responsible for not escaping reference somewhere outside and not modifying these data badly?
> 
> There also possible some situations when these data should be shared between different pieces of code (for example different class instance could reference the same memory area). But I think this is not very good and safe approach and should be avoided if possible.
> 
> But I think storing reference to class inside another class could be good approach because there could be different type of relations between classes. For plain data types we often have types of relations: *ownership* or *aggregation*. But classes can usualy have more types of relations: *usage* or when one class subscribes for events of another.
> 
> Is there some good links to read for these questions that you could advice. After several hours of googling I haven't found good topics about these problems. And I have not enough time for reading big book.

You can wrap type and add suport for copy on write, something like this:

struct CoW(T:E[], E) {
    T data;
    bool change = false;
    alias data this;

    this(T v) {
        data = v;
    }

    this(this) {
        change = false;
    }

    void opIndexAssign(E v, size_t p)
    {
        if (change is false) {
            data = data.dup;
            change = true;
        }
        data[p] = v;
    }
}

alias cowIntArr = CoW!(int[]);

class Foo {
    this(cowIntArr b) { bar = b; }
    private cowIntArr bar;

    void printBar() {
        writeln(bar);
    }

}

void main(string[] args)
{
    cowIntArr bar = [1,2,3,4,5];
    auto foo = new Foo(bar);
    bar[2] = 6;
    writeln(bar);
    foo.printBar();
}


November 28, 2014
On Friday, 28 November 2014 at 08:31:26 UTC, bearophile wrote:
> Uranuz:
>
>> Same situation happens when I assign reference data to properties.
>
> Someone has suggested to solve this problem with an attribute, like "owned", that forbids to return mutable reference data owned by a class/struct instance.
>
> Bye,
> bearophuile

Yes. Problem is even if you have property that controls correct assignment. If you have getter that returns mutable reference type and you try to access some fields of it or apply index operator (for arrays or AA) *host* cannot control corectness of these assignments or cannot react to these changes (in order to do some changes in model).

The way I see is to create my own type of Array for this that can notify *host object* about changes so it could refresh the model or do some checks.

Another way is to write interfaces in a way so we cant't escape uncontrolled references to mutable data.

What do you think of it? Is there any patterns that could help to solve such problems?

I think that writing your own type every time when you need to enforce consistency and safety is not very good))
November 28, 2014
> Yes. Problem is even if you have property that controls correct assignment. If you have getter that returns mutable reference type and you try to access some fields of it or apply index operator (for arrays or AA) *host* cannot control corectness of these assignments or cannot react to these changes (in order to do some changes in model).
>
> The way I see is to create my own type of Array for this that can notify *host object* about changes so it could refresh the model or do some checks.
>
> Another way is to write interfaces in a way so we cant't escape uncontrolled references to mutable data.
>
> What do you think of it? Is there any patterns that could help to solve such problems?
>
> I think that writing your own type every time when you need to enforce consistency and safety is not very good))

In my opinion, this is an application design problem; not a language design problem.

Getters break encapsulation: they expose an object's internal representation to client code. I tend to try and confine them to DTOs, which are generally immutable anyway.

If you're worried about encapsulation, why not add a method to the object that modifies its own internal state?