I have a project that supports plugins. As to make plugins unit-testable, they only interact with the main application through interfaces.
The main application provides an implementation to these interfaces. The different components of this application also have dependencies between each others.
I want to limit the amount of interface members by only having the ones needed for plugin development. Problem is, sometimes an implementation needs to call a method that I do not want to expose to the plugins. In a "class" world, I would use the internal
keyword for these methods.
Here's a rudimentary example that might be clearer:
interface IUserManager
{
IReadOnlyCollection<IUser> Users { get; }
}
interface IUser
{
string Name { get; }
}
class UserImpl : IUser
{
public string Name { get; }
public void Delete()
{
// ...
}
}
class UserManager : IUserManager
{
IReadOnlyCollection<IUser> Users { get; }
public void DeleteAllUsers()
{
foreach(var user in Users)
{
if(user is UserImpl impl)
{
impl.Delete();
}
}
}
}
class Plugin
{
public Plugin(IUserManager userManager)
{
// I want the plugin to be able to access the user's name, but not its Delete() method
Console.WriteLine(userManager.Users.First().Name);
}
}
class NetworkController
{
private readonly IUserManager _userManager;
public ReceiveDeleteMessage(string name)
{
var user = _userManager.Users.Single(x => x.Name == name);
user.Delete(); // not possible, needs a cast
}
}
But this feels wrong to me... Not only we now have a cast that could fail, we have no way to mock the Delete() function for implementation unit tests. The best thing I could come with is adding an "internal" interface, but I am still stuck with the cast.
interface IInternalUser : IUser
{
void Delete();
}
class UserImpl : IInternalUser
{
public string Name { get; }
public void Delete()
{
}
}
// in UserManager ...
if(user is IInternalUser internalUser)
{
internalUser.Delete();
}
I could settle with this and I think it will be fine, but this little detail makes me feel like I am not taking the best approach. I am looking for better ideas on how to do this.
Aucun commentaire:
Enregistrer un commentaire