Ayende @ Rahien

It's a girl

Public vs. Published

imageOne of the thing that you see some people agonizing about is what they should be exposed to the outside world. On the one hand, people have heard all about encapsulation, and how it is going to save the world. On the other hand, we want to expose functionality to the outside world, be it for testing or for advance usages of your software.

Something that I wish that C# had is the published alias to the public keyword, that would have expressed intent in a much easier fashion than just public.

That said, I found a very useful approach to handling this myself. Separating interface and implementation. Let us look at an example:

public interface ICommand
{
     void Init(string[] args);
     void Execute();
}

And an implementation of that:

public class MyCommand : ICommand
{
       public string Key { get; set; }
       public long ByteCount { get; set; }
 

       public void Init(string [] args)
       {
		// parse args to key & byte count
       }
}

Now, if I want to test that I am getting the right thing, I can, easily. At the same time, the published interface of this is ICommand, not whatever is public in MyCommand.

In situations where I have one to one mapping between interfaces and implementations (IUserRepository and UserRepository), this is even more pronounced.

Comments

Andrey Shchekin
06/25/2008 07:24 PM by
Andrey Shchekin

I think it's exactly what 'internal' is for.

I always make things like UserRepository internal.

Nick
06/25/2008 07:44 PM by
Nick

I use this all the time for my repository and service classes. It allows me to use property injection without publishing the public properties. I can still access those properties if I want to, but they aren't a part of the "published" interface to those classes.

chris
06/25/2008 08:04 PM by
chris

congrats, you've discovered OOP! ;)

Adam Vandenberg
06/25/2008 08:06 PM by
Adam Vandenberg

I've used the "Interface as published" thing before on my projects. I had class with a HUGE set of public methods being passed around, and I finally went around partitioning them into separate iterfaces, and rewriting the client code to accept only the narrowed interface.

This worked wonders.

I was able to componentize the kitchen-sink class and finally refactor the responsibilities into wherever they needed to go, rather than in the kitchen-sink.

It helps intellisense within the client code too, as I only see those methods which I "ought" be using, instead of everything the class has.

Ayende Rahien
06/25/2008 08:19 PM by
Ayende Rahien

andrey,

Internal is a bad solution, it means that you are only exposing things to the current assembly.

Testing, advance scenarios needs that access as well

Petar Repac
06/25/2008 09:16 PM by
Petar Repac

Ayende,

you can test internal members if you make test assembly a friend assembly (with InternalsVisibleToAttribute)

Ayende Rahien
06/25/2008 09:19 PM by
Ayende Rahien

Petar, I am aware of that.

I strongly dislike this method

Ed
06/25/2008 11:19 PM by
Ed

Adding InternalsVisibleTo a project's AssemblyInfo.cs will allow other explicitly defined assemblies access to internal members. This is really helpful for unit testing.

Example:


[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("UnitTests, PublicKeyToken=123456789abcdef0")]

http://www.sturmnet.org/blog/archives/2005/05/10/internalsvisibleto-sn/

James
06/26/2008 02:07 AM by
James

Why wouldn't you mark the set parts of the properties to private since the way they should be initialized is via the Init method?

Andrey Shchekin
06/26/2008 06:10 AM by
Andrey Shchekin

Testing works extremely well with InternalsVisibleTo. Also it seems the right thing to do -- why do something as big as making everything public just for testing purposes?

I always try to write my code to be test ignorant (making code modular or using interfaces are common sense good design decisions, not test-forced ones). Doing something 'just for tests' feels as bad as doing something 'just for persistence' or 'just for specific IoC container'.

But if you have an advanced scenario when you need these classes to be public, probably it means they are 'published' as well.

Ayende Rahien
06/26/2008 06:14 AM by
Ayende Rahien

You seem to be ignoring the part of testing not the only thing required.

And published is not the same as public. For published, I want to support this.

For public, it means that you can do things with it, but it is your responsability

Petar Repac
06/26/2008 06:24 AM by
Petar Repac

Ed,

I really don't see how "other explicitly defined assemblies" could access internal members. It would defeat the whole purpose of friend assemblies.

Ayende,

could you say why you dislike this method ? Because I see it very much like Andrey.

The other way to test internal stuff could be to implement test classes inside the tested assembly itself and manage builds so that test classes are not included in release version. With partial classes maybe we could test private stuff to. Haven't tried this scenario myself..

Ayende Rahien
06/26/2008 06:44 AM by
Ayende Rahien

Because testability is a secondary concern. I often need to be able to support additional functionality, and I found out that creating this distinction means that I can get pretty good results over a long period of time.

here is an example:

http://www.ayende.com/Blog/archive/2008/01/24/Interception-as-an-extensibility-mechanism.aspx

Andrey Shchekin
06/26/2008 01:12 PM by
Andrey Shchekin

I do not see any be-public requirements here: http://www.ayende.com/Blog/archive/2008/01/24/Interception-as-an-extensibility-mechanism.aspx, what am I missing?

Andrey Shchekin
06/26/2008 01:12 PM by
Andrey Shchekin

I do not see any be-public requirements here: http://www.ayende.com/Blog/archive/2008/01/24/Interception-as-an-extensibility-mechanism.aspx, what am I missing?

Jon Skeet
06/27/2008 06:06 AM by
Jon Skeet

Out of interest, has anyone here ever seen a good use for InternalsVisibleTo other than testing? That's the only use I've had for it so far, and I'd be slightly worried at other uses, I suspect. If anyone's found one, I'd be interested to hear about it.

Paul Batum
06/27/2008 08:06 AM by
Paul Batum

Jon, when working with NHibernate, I use InternalsVisibleTo to give Castle.DynamicProxy access to the internals of my domain model.This allows me to use the lazy loading that Castle.DynamicProxy provides while keeping stricter controls on the visibility of constructors.

George Spofford
06/27/2008 05:22 PM by
George Spofford

The first textbook I ever came across on computer security discussed role-based security access to functions prior to applying it to things like file and object access. I agree that public/private/protected/internal are too gross in their scopes- very convenient for lots of cases, but not adequate. I for one would like to be able to unit-test "private" methods and classes as well. Of course, role-based or named-entity-based access authorization for code introduces a set of questions, but I'm quite confident that a broader set of cases could be handled with the answers that are easier to arrive at.

Colin Jack
06/30/2008 12:36 PM by
Colin Jack

Noticed you have Execute on the interface but not on the class...

@Andrey

"Doing something 'just for tests' feels as bad as doing something 'just for persistence' or 'just for specific IoC container'."

I think a lot of the time this is true, changes put in for testing don't necessarily prove that useful. That is not an argument against TDDD though, just not sure always designing for testability is always a good approach.

@Jon Skeet

Good, maybe not but I've seen it used where you have a seperation between layers but actually there is a tight relationship. For example only an AccountService (in one project) should access a member on Account (in another project), in those cases you can make the member public and only use it from AccountService or just use InternalsVisibleTo in order to give the project containing AccountService access.

Comments have been closed on this topic.