Machinarius
Machinarius

Reputation: 3731

How to ensure only A class can create an A.B instance?

I have a broker class that issues request objects and expects them to be delivered back to it with a couple properties changed to sensible values. The problem is that the consumers of said broker must never change a couple readonly properties of that object nor be able to create a different request instance to cheat that readonly protection or the broker will break and throw an exception. I want to find a way to make the compilation fail if any class save for the broker tries to create a request object.

I think sealing the instantiation of the request objects so it can only be done from inside the broker itself is a neat idea coupled with readonly properties so request processors can never cheat the system but i am having a hard time doing so. I tried a child class with a private constructor like this:

public class PermissionsRequestBroker {
    public PermissionsRequest Test() {
        return new PermissionsRequest();
    }

    private class PermissionsRequest {
        private PermissionsRequest() {

        }
    }
}

But it fails because the broker cannot create the request object.

I tried a similar approach but with an interface like this:

public class PermissionsRequestBroker {
    public IPermissionsRequest Test() {
        return new PermissionsRequest();
    }

    public interface IPermissionsRequest {

    }

    private class PermissionsRequest : IPermissionsRequest {
        public PermissionsRequest() {

        }
    }
}

But the request processors can implement IPermissionsRequest and cheat the system that way. Sure i could implement a runtime check so the object returned is still the broker's PermissionRequest object but that's still a runtime check and will throw an exception.

I'm all for exceptions but i feel there must be some way to enforce that contract at compile time without installing any IDE extension or NuGet package of any kind.

Upvotes: 0

Views: 81

Answers (2)

Sumit Maingi
Sumit Maingi

Reputation: 2263

I know this is already answered, but I'm curious... why wouldn't this work?

EDIT: Had a brain freeze moment, the below code will not work, see the edit after that.

public class PermissionsRequestBroker {
    public PermissionsRequest Test() {
        return new PermissionsRequest();
    }

    public sealed class PermissionsRequest {
        private PermissionsRequest() {

        }
    }
}

Basically making the inner class public and sealed but only its constructor private?

EDIT

If we invert this, it would be simpler to implement, thoughts? The staticness of the broker is optional of course.

public class PermissionsRequest
{
    private PermissionsRequest()
    { }

    public sealed class Broker
    {
        public static PermissionsRequest CreatePermissionsRequest()
        {
            return new PermissionsRequest();
        }

        public PermissionsRequest CreatePermissionsRequest_Instance()
        {
            return new PermissionsRequest();
        }
    }
}

public class UserClass
{
    public void Blah()
    {
        var permissionsRequest = PermissionsRequest.Broker.CreatePermissionsRequest();

        var broker = new PermissionsRequest.Broker();

        var permRequest = broker.CreatePermissionsRequest_Instance();
    }
}

Upvotes: 1

Steve
Steve

Reputation: 181

Place PermissionsRequestBroker and PermissionsRequest in a separate assembly together, and mark PermissionsRequest as internal instead of public. Then if you need consumers to be able to hold onto an instance of the PermissionsRequest object, wrap it in another class that is public.

Something like the following:

public class PermissionsRequestBroker {
   public PermissionsRequestWrapper Test() {
      return new PermissionsRequestWrapper( new PermissionsRequest() );
   }
}

internal class PermissionsRequest {
   internal PermissionsRequest() {

   }
}

// Use 'sealed' to prevent others from inheriting from this class
public sealed class PermissionsRequestWrapper {
   private PermissionsRequest _permissionsRequest;

   internal PermissionsRequestWrapper(PermissionsRequest permissionsRequest) {
      _permissionsRequest = permissionsRequest;
   }

   /* etc... */
}

Upvotes: 1

Related Questions