I was refactoring some of my utility code modules used in my test projects, and wanted to apply my knowledge of clean OOP design patterns and SOLID principles to make these modules more useful and easier to use.
I have an interface called IFtpFileCleaner
:
/// <summary>
/// Responsible for deleting a single file located in a remote server
/// </summary>
public interface IFtpFileCleaner
{
/// <summary>
/// Deletes a remote file
/// </summary>
/// <param name="remotePath">
/// The full remote path.
/// </param>
void Clean(string remotePath);
}
I have an abstract class called RemoteFileCleaner
, which implements IFtpFileCleaner
:
/// <summary>
/// The file cleaner base class provides common code for all the implementations to reduce code duplication
/// </summary>
public abstract class RemoteFileCleaner : IFtpFileCleaner
{
/// <summary>
/// The FTP _client.
/// </summary>
private readonly IFtpSecureClient _client;
/// <summary>
/// Initializes a new instance of the <see cref="RemoteFileCleaner"/> class.
/// </summary>
/// <param name="client">
/// The FTP client.
/// </param>
protected RemoteFileCleaner(IFtpSecureClient client)
{
_client = client;
}
/// <summary>
/// Deletes a single file located on a remote server
/// </summary>
/// <param name="remotePath">
/// The full path of the remote file.
/// </param>
public void Clean(string remotePath)
{
try
{
_client.Init();
_client.DeleteFile(remotePath);
}
catch (Exception e)
{
Console.WriteLine($"Failed to clean file {remotePath}.{Environment.NewLine}{e}");
throw;
}
finally
{
_client.Disconnect();
}
Console.WriteLine($"Remote File {remotePath} was deleted.");
}
}
Now, I can have concrete implementations of my file cleaners. Like SFTPFileCleaner
, FTPFileCleaner
, and FTPSFileCleaner
. They all look very similar. For instance, SFTPFileCleaner
looks like this:
/// <summary>
/// The SFTP implementation of <see cref="RemoteFileCleaner"/>.
/// </summary>
public class SFTPFileCleaner : RemoteFileCleaner
{
/// <summary>
/// Initializes a new instance of the <see cref="SFTPFileCleaner"/> class.
/// </summary>
public SFTPFileCleaner()
: base(new FtpSecureClientTestFactory().CreateSFTPClient())
{
}
}
As you can see, I am performing "Bastard Injection" here in the concrete implementation because I would like to easily instantiate these cleaners in my test methods. Here is an example:
[TearDown]
public void Cleanup()
{
_ftpCleaner = new SFTPFileCleaner();
_ftpCleaner.Clean(_expectedRemotePath);
}
- What do you guys think about this design? Is it clean and easy to use?
- Can it be improved any further?
- Is there a name for this design pattern? To me is seems to be the mixture of Facade Pattern (Bastard Injection) and Strategy Pattern. Am I correct?
Console.WriteLine
) elements and therefore violating both Dependency Inversion and Single Responsibility Principle. Inject a logger (such as Log4Net'sILog
) or at worse aTextWriter
in the constructor for status logging operations. \$\endgroup\$ILogger
interface in my production code with different implementations such as Log4Net and LocalModular (My own version). Can I do another Bastard Injection from the inherited classes and inject aNativeModularLogger(new ConsoleLogger())
into theRemoteFileCleaner
? \$\endgroup\$