Search code examples
c#interfaceencapsulationgetter

Can an interface be used as a means of encapsulation in terms of clear code?


I have a Person class. It has private fields name, age and public methods for getting fields (getters). This class also implements interface IGetPublicInfo.

public class Person : IGetPublicInfo
{
    private string name;
    private int age;
    private string secret = "someSecret";

    public Person(in string name, in int age)
    {
        this.name = name;
        this.age = age;
    }

    public string GetName()
    {
        return name;
    }

    public int GetAge()
    {
        return age;
    }

    public string GetSecret()
    {
        return secret;
    }
}

Interface:

interface IGetPublicInfo
{
    string GetName();
    int GetAge();
}

Company class code code:

class Company
{
    private IGetPublicInfo person = Person("Bob", 32);
}

Friend class code:

public class Friend
{
    private Person friend = new Person("Bob", 34);
}

Can an interface be used as a means of encapsulation in terms of correct code? In the example above, the company class only knows about the public part of the person. The friend class knows all the information. I don't quite understand when to use interfaces.


Solution

  • Interfaces are commonly used to achieve abstraction, not encapsulation. As per common definition for example from wiki:

    Encapsulation refers to the bundling of data with the methods that operate on that data, or the restricting of direct access to some of an object's components, encapsulation is used to hide the values or state of a structured data object inside a class, preventing direct access to them by clients in a way that could expose hidden implementation details or violate state invariance maintained by the methods.

    I.e. encapsulation is about hiding internal object data structure, not about restricting access to it's public methods based on context. In your current implementation encapsulation is achieved by the getter methods itself not by the fact of using an interface, i.e. internal implementation with fields is hidden from the end user by the provided methods. And interface in this case allows you to abstract your object to provide only valid public data in particular context.

    I don't quite understand when to use interfaces.

    Usually I recommend to think about interfaces as contracts which component implements and as any other abstraction - when correctly designed they allow to build less coupled systems which usually is considered a good thing. One of the biggest usecases for interfaces is to group related (or not that much) components when they needed to be used in the same way.

    For example in this particular case if your system would work not only with people but with pets also you can have next abstractions:

    interface IGetPublicInfo {...}
    interface IGetSecretInfo { string GetSecret(); } // possibly not needed, but for the sake of interface segregation principle lets have it
    interface IGetFullInfo : IGetSecretInfo, IGetPublicInfo {}
    
    public class Person : IGetFullInfo {...} // specific person implementation
    public class Pet : IGetFullInfo {...} // specific pet impl, for example secret is just "bark"
    

    And then Friend works with IGetFullInfo

    public class Friend
    {
        private IGetFullInfo friend = new Person("Bob", 34); // or new Pet(...)
    }
    

    P.S.

    Few small notes:

    • You can have a slightly more succinct implementation with expression bodied properties
    • Since strings are immutable in C# and int is a value type the in parameter modifier seems redundant in this case
    public class Person : IGetPublicInfo
    {
        private string name;
        private int age;
        private string secret = "someSecret";
    
        public Person(string name, int age)
        {
            this.name = name;
            this.age = age;
        }
    
        public string Name => name;
        public int Age => age;
        public string Secret => secret;
    }
    
    interface IGetPublicInfo
    {
        string Name { get; }
        int Age { get; }
    }