Click here to Skip to main content
15,885,141 members
Articles / Programming Languages / C#

Friends and internal interface members at no cost with coding to interfaces

Rate me:
Please Sign up or sign in to vote.
4.40/5 (15 votes)
18 Jun 2012CPOL4 min read 29.6K   33   8
Good coding practices sometimes lead to neat solutions of seemingly unrelated issues. Do you code to interfaces? - Get friendship at no cost.

Preface

My coding experience shows that once a particular class has more than one public method, I start loosing control over the exact functionality of that particular class. So, I try to emphasize the interface of any class. The result is that almost all my classes, even the very internal ones have corresponding interfaces.

Programing to an interface is a way of coding. If you are there, you will easily adopt the ideas from this article. If you are not sure what programing to an interface means, I hope, you will fill the gap as soon as possible.

In other words: "Start program to an interface".

Introduction

In this article I will try to get a solution for two specific problems

  • Constructing objects from a predefined class only - aka friend access
  • Defining interface members with an internal access

But first - my understanding of the statements above.

Problem Statement

Constructing objects from a predefined class only

Let's code a transaction manager

C#
interface ITransaction { }
class LoadingTransaction : ITransaction() { }
C#
class TransactionManager
{
    ITransaction StartLoading()
    {
        return new LoadingTransaction();
    }
}

I would say that hiding the public constructor of the LoadingTransaction class suggests itself. The design above surely imposes that any transaction should be created by a transaction manager only.

Interface members with internal access

While the web is full of excellent answers to "Why interface can't have internal members", I still think that the question itself is a mix of two orthogonal concerns:

  • Who is allowed to implement a particular interface members
  • Who is allowed to use that particular interface member

Now consider the following member of an ILogger interface

C#
interface ILogger
{
    ...
    void Reconfigure();
}

Any class that implements the ILogger interface is ought to implement the Reconfigure() method. No doubt here. But, what we may want is to restrict the initiators of a reconfiguration process - the callers of the Reconfigure() method.

Indeed, suppose the reconfiguration trigger is an internal detail of our assembly - then we want to express this fact explicitly in our design.

No one should call the Reconfigure() method from outside.

That what the internal access modifier on the Reconfigure() method should mean.

Implementation

Before I am ready to step into the promised implementation I need a way of total separation of an interface and its implementation.

Decoupling an Interface from a Specific Implementation

Given the transaction manager we started with, let's emit the ITransactionManager interface and its implementation as we did with the transaction itself.

C#
interface ITransaction { }
class LoadingTransaction : ITransaction() { }
C#
interface ITransactionManager 
{ 
    ITransaction StartLoading();
}
C#
class TransactionManager : ITransactionManager
{
    ITransaction ITransactionManager.StartLoading()
    {
        return new LoadingTransaction();
    }
}

(See my article why I use explicit implementation)

I always struggle not to deal with any concrete implementations.

Let's hide the public constructors of both LoadingTransaction and TransactionManager classes

C#
class LoadingTransaction : ITransaction() 
{
    public static ITransaction New()
    {
        return new LoadingTransaction();
    }
    
    private LoadingTransaction()
    { }
}
C#
class TransactionManager : ITransactionManager() 
{
    public static ITransactionManager New()
    {
        return new TransactionManager();
    }
    
    private TransactionManager()
    { }
}

The New() method is a kind of a factory method that accomplishes the separation of an interface and its implementation. (The same may be achieved by using a dependency injector container)

From this point no one (especially later maintainers) do not deal with any concrete instances of our classes.

For instance, a little benefit from this way of coding

C#
void main()
{
    var manager = TransactionManager.New();
    manager.StartLoading();     // good; still using ITransactionManager interface
}
Regardless of the usage of the var keyword the code above is not coupled to a concrete TransactionManager instance.

What is not true with a common coding practice

C#
void main()
{
    var manager = new TransactionManager();
    manager.StartLoading();     // bad! coupled to a concrete TransactionManager implementation
}

The New() method is the crucial point of the friend access solution, so look at it once again carefully.

Now Comes the "Is My Friend Trick"

We are finally ready to express friend access explicitly
C#
class LoadingTransaction : ITransaction() 
{
    public static ITransaction New(TransactionManager onlyAccess)
    {
        return new LoadingTransaction();
    }
    
    private LoadingTransaction()
    { }
}

Surprisingly we require a concrete instance of the TransactionManager class as a parameter of the LoadingTransaction.New() method call.

But the only one who has that concrete instance is the TransactionManager itself!

Take a brief and recall

C#
class TransactionManager : ITransactionManager() 
{
    public static ITransactionManager New()
    {
        return new TransactionManager();
    }
    
    private TransactionManager()
    { }
}

The factory method New() returns an ITransactionManager interface. The constructor of the TransactionManager class is private and so inaccessible.

There is just no way to obtain a concrete instance of the TransactionManager class.

Indeed, the only one who can call the LoadingTransaction.New(TransactionManager onlyAccess) method is the TransactionManager itself or any of its descendant.

Internal members

It seems that the idea is clear: "Requiring a concrete instance of a class, while restricting the construction access of that class".

Now it is pretty easy to come up with this code

C#
public class Internal
{
    internal Internal()
    { }
}

public interface ILogger
{
    ...
    void Reconfigure(Internal access);
}

We require a concrete instance of the Internal class as a parameter of the Reconfigure() method call. But that required instance may only be constructed within our assembly.

An Accomplishing Touch

There are two ways our friend accessed method might be compromised

C#
class LoadingTransaction : ITransaction() 
{
    public static ITransaction New(TransactionManager onlyAccess)
    { }
}

Accessing the friend method with a null reference

C#
void main()
{
    var transaction = LoadingTransaction.New(null);
}

What may be solved by just a run-time validation

C#
class LoadingTransaction : ITransaction() 
{
    public static ITransaction New(TransactionManager onlyAccess)
    {
        if (onlyAccess == null)
            throw new AccessViolationException();
    }
}

The second flaw is accessing a friend method by casting to a concrete instance

C#
void main()
{
    ITransactionManager manager = TransactionManager.New();
    var transaction = LoadingTransaction((TransactionManager) manager);
}

The solution here is a little bit more complicated. But the idea is pretty much the same. We require a concrete instance of a nested FriendsOnly class, while hiding its constructor.

C#
class LoadingTransaction : ITransaction
{
    public static ITransaction New(TransactionManager.FriendsOnly access)
    { }
}
C#
class TransactionManager
{
    private static FriendsOnly _meIsFriend;

    public class FriendsOnly
    {
        public static void GrantAccessPermissions() { _meIsFriend = new FriendsOnly(); }
        private FriendsOnly() { }
    }

    static TransactionManager()
    {
        FriendsOnly.GrantAccessPermissions();
    }

    public ITransaction StartLoading()
    {
        var transaction = LoadingTransaction.New(_meIsFriend);
        ...
    }
}
However, I would simply ignore the second flaw, since casting to a concrete class is already a dirty thing to do.

Conclusion

The accessibility obstacle may be handled by nested classes as well. However, the method represented by this article is superior in my opinion. It does not compromise the design nor the coding style.

Once you adopt the practice of programming to interfaces, you get the friend access for almost no charge.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Chief Technology Officer Cpp2Mtl Integration Solutions
United States United States
My real name is Reuven Bass. My first article here was published under the Mamasha Knows pseudonym. It worked. So, I stay with Mamasha for a while. (If it works - do not touch it)

Programming became my life from thirteen. I love coding. I love beauty. I always try to combine coding and beauty.

RB

Comments and Discussions

 
GeneralMy vote of 3, low coupling means low cohesion. Pin
Nicolas Dorier29-Jun-12 20:32
professionalNicolas Dorier29-Jun-12 20:32 
GeneralRe: My vote of 3, low coupling means low cohesion. Pin
Reuven Bass2-Jul-12 11:06
Reuven Bass2-Jul-12 11:06 
GeneralMy vote of 5 Pin
PaulLinton20-Jun-12 13:15
PaulLinton20-Jun-12 13:15 
GeneralRe: My vote of 5 Pin
Reuven Bass21-Jun-12 6:15
Reuven Bass21-Jun-12 6:15 
QuestionHow to solve the flaw and why? Pin
Paulo Zemek19-Jun-12 5:06
mvaPaulo Zemek19-Jun-12 5:06 
AnswerRe: How to solve the flaw and why? Pin
Reuven Bass19-Jun-12 9:43
Reuven Bass19-Jun-12 9:43 
QuestionFirst Pin
ycg16691119-Jun-12 0:14
ycg16691119-Jun-12 0:14 
AnswerRe: First Pin
Reuven Bass19-Jun-12 10:31
Reuven Bass19-Jun-12 10:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.