Encapsulate interface implementation of property

101 Views Asked by At

I have an Object class that allows objects to be linked together.

A -> B -> C (one way linking)

When a link is formed between two objects by calling Link(IObject other), I want to set IsReferenced on the Next object.

So in the above example, A references B, but B does not reference A, B just has it's IsReferenced property set to true.

However, I only want the IsReferenced setter to be available to classes that implement the IObject interface. i.e the IsReferenced should only be able to be set inside the Object class, outside code should not be able to set it.

However, the code fails to compile as IsReferenced when accessed via the interface is read only.

 public interface IObject
{
    bool IsReferenced { get; }
    IObject? Next { get; }
    void Link(IObject next);
}

public class Object : IObject
{
    public bool IsReferenced { get; }
    public IObject? Next { get; private set; }

    public void Link(IObject next)
    {
        Next = next;
        next.IsReferenced = true;
    }
}

I can resolve the issue by doing the following :

public void Link(IObject next)
{
    Next = next;
    ((Object)next).IsReferenced = true;
}

But this won't work for my scenario, as I need to be able to Mock<IObject>

Is there a better solution other than making the IsReferenced a public setter?

1

There are 1 best solutions below

4
Astrid E. On

Is using a helper method to 'activate' IsReferenced an option for you?

By calling a helper method on the linked object with the object that has just linked it, the linked object can itself verify that it has in fact been referenced.

Here is a suggestion where OnLinkedBy() is the helper method:

public interface IObject
{
    bool IsReferenced { get; }
    IObject? Next { get; }
    void Link(IObject next);
    void OnLinkedBy(IObject other);
}

public class Object : IObject
{
    private IObject? _next { get; set; }

    public bool IsReferenced { get;  private set; }
    
    public IObject? Next
    {
        get => _next;
        private set
        {
            _next = value;
            
            if (_next is not null)
            {
                _next.OnLinkedBy(this);
            }
        }
    }
    
    public void Link(IObject next) => Next = next;
    
    public void OnLinkedBy(IObject other)
    {
        if (other.Next == this)
        {
            IsReferenced = true;
        }
    }
}

Now, if you do

var objectA = new Object();
var objectB = new Object();

objectA.Link(objectB);

Console.WriteLine("ObjectA is linked: " + (objectA.Next != null));
Console.WriteLine("ObjectA is referenced: " + objectA.IsReferenced);
Console.WriteLine("ObjectB is linked: " + (objectB.Next != null));
Console.WriteLine("ObjectB is referenced: " + objectB.IsReferenced);

the output will be:

ObjectA is linked: True
ObjectA is referenced: False
ObjectB is linked: False
ObjectB is referenced: True


Note: If you also want to be able to dereference, this simple approach will not cover your need.