Clean implementation of FTP File Cleaners
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
0
down vote
favorite
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.NewLinee");
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?
c# object-oriented design-patterns integration-testing
add a comment |Â
up vote
0
down vote
favorite
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.NewLinee");
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?
c# object-oriented design-patterns integration-testing
Welcome to Code Review! I see you added one line to the code, which doesn't appear to invalidate the current answer, but if the changes were more drastic then those would be reversed. For more information on this, see what you may and may not do after receiving answers.
â Sam Onela
May 24 at 18:35
Hi @SamOnela, sorry about that. I just realized I was missing a re-throw statement in my catch block. I didn't know about the rules. Won't happen again. :)
â Shervin Shahrdar
May 24 at 18:46
1
1. Not terribly clean - you're mixing data processing (FTP file deletion) with user interface (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.
â Jesse C. Slicer
May 24 at 19:20
@JesseC.Slicer, Is it a common practice to use Log4Net in test projects? I simply want to write to a console, so that I can see the error message in the NUnit test runner and debug things faster. I do have anILogger
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
?
â Shervin Shahrdar
May 24 at 19:39
add a comment |Â
up vote
0
down vote
favorite
up vote
0
down vote
favorite
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.NewLinee");
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?
c# object-oriented design-patterns integration-testing
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.NewLinee");
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?
c# object-oriented design-patterns integration-testing
edited May 24 at 18:20
asked May 24 at 15:52
Shervin Shahrdar
1012
1012
Welcome to Code Review! I see you added one line to the code, which doesn't appear to invalidate the current answer, but if the changes were more drastic then those would be reversed. For more information on this, see what you may and may not do after receiving answers.
â Sam Onela
May 24 at 18:35
Hi @SamOnela, sorry about that. I just realized I was missing a re-throw statement in my catch block. I didn't know about the rules. Won't happen again. :)
â Shervin Shahrdar
May 24 at 18:46
1
1. Not terribly clean - you're mixing data processing (FTP file deletion) with user interface (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.
â Jesse C. Slicer
May 24 at 19:20
@JesseC.Slicer, Is it a common practice to use Log4Net in test projects? I simply want to write to a console, so that I can see the error message in the NUnit test runner and debug things faster. I do have anILogger
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
?
â Shervin Shahrdar
May 24 at 19:39
add a comment |Â
Welcome to Code Review! I see you added one line to the code, which doesn't appear to invalidate the current answer, but if the changes were more drastic then those would be reversed. For more information on this, see what you may and may not do after receiving answers.
â Sam Onela
May 24 at 18:35
Hi @SamOnela, sorry about that. I just realized I was missing a re-throw statement in my catch block. I didn't know about the rules. Won't happen again. :)
â Shervin Shahrdar
May 24 at 18:46
1
1. Not terribly clean - you're mixing data processing (FTP file deletion) with user interface (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.
â Jesse C. Slicer
May 24 at 19:20
@JesseC.Slicer, Is it a common practice to use Log4Net in test projects? I simply want to write to a console, so that I can see the error message in the NUnit test runner and debug things faster. I do have anILogger
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
?
â Shervin Shahrdar
May 24 at 19:39
Welcome to Code Review! I see you added one line to the code, which doesn't appear to invalidate the current answer, but if the changes were more drastic then those would be reversed. For more information on this, see what you may and may not do after receiving answers.
â Sam Onela
May 24 at 18:35
Welcome to Code Review! I see you added one line to the code, which doesn't appear to invalidate the current answer, but if the changes were more drastic then those would be reversed. For more information on this, see what you may and may not do after receiving answers.
â Sam Onela
May 24 at 18:35
Hi @SamOnela, sorry about that. I just realized I was missing a re-throw statement in my catch block. I didn't know about the rules. Won't happen again. :)
â Shervin Shahrdar
May 24 at 18:46
Hi @SamOnela, sorry about that. I just realized I was missing a re-throw statement in my catch block. I didn't know about the rules. Won't happen again. :)
â Shervin Shahrdar
May 24 at 18:46
1
1
1. Not terribly clean - you're mixing data processing (FTP file deletion) with user interface (
Console.WriteLine
) elements and therefore violating both Dependency Inversion and Single Responsibility Principle. Inject a logger (such as Log4Net's ILog
) or at worse a TextWriter
in the constructor for status logging operations.â Jesse C. Slicer
May 24 at 19:20
1. Not terribly clean - you're mixing data processing (FTP file deletion) with user interface (
Console.WriteLine
) elements and therefore violating both Dependency Inversion and Single Responsibility Principle. Inject a logger (such as Log4Net's ILog
) or at worse a TextWriter
in the constructor for status logging operations.â Jesse C. Slicer
May 24 at 19:20
@JesseC.Slicer, Is it a common practice to use Log4Net in test projects? I simply want to write to a console, so that I can see the error message in the NUnit test runner and debug things faster. I do have an
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 a NativeModularLogger(new ConsoleLogger())
into the RemoteFileCleaner
?â Shervin Shahrdar
May 24 at 19:39
@JesseC.Slicer, Is it a common practice to use Log4Net in test projects? I simply want to write to a console, so that I can see the error message in the NUnit test runner and debug things faster. I do have an
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 a NativeModularLogger(new ConsoleLogger())
into the RemoteFileCleaner
?â Shervin Shahrdar
May 24 at 19:39
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
1
down vote
Wouldn't it make more sense to have IRemoteFileCleaner
as the base class, and have FTPFileCleaner
(which inherits from IRemoteFileCleaner
as the class which all the others inherit from?
So, just a simple rename? Good idea.
â Shervin Shahrdar
May 24 at 16:10
@ShervinShahrdar Yeah, I guess so.
â Solomon Ucko
May 24 at 22:09
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
Wouldn't it make more sense to have IRemoteFileCleaner
as the base class, and have FTPFileCleaner
(which inherits from IRemoteFileCleaner
as the class which all the others inherit from?
So, just a simple rename? Good idea.
â Shervin Shahrdar
May 24 at 16:10
@ShervinShahrdar Yeah, I guess so.
â Solomon Ucko
May 24 at 22:09
add a comment |Â
up vote
1
down vote
Wouldn't it make more sense to have IRemoteFileCleaner
as the base class, and have FTPFileCleaner
(which inherits from IRemoteFileCleaner
as the class which all the others inherit from?
So, just a simple rename? Good idea.
â Shervin Shahrdar
May 24 at 16:10
@ShervinShahrdar Yeah, I guess so.
â Solomon Ucko
May 24 at 22:09
add a comment |Â
up vote
1
down vote
up vote
1
down vote
Wouldn't it make more sense to have IRemoteFileCleaner
as the base class, and have FTPFileCleaner
(which inherits from IRemoteFileCleaner
as the class which all the others inherit from?
Wouldn't it make more sense to have IRemoteFileCleaner
as the base class, and have FTPFileCleaner
(which inherits from IRemoteFileCleaner
as the class which all the others inherit from?
answered May 24 at 16:06
Solomon Ucko
822313
822313
So, just a simple rename? Good idea.
â Shervin Shahrdar
May 24 at 16:10
@ShervinShahrdar Yeah, I guess so.
â Solomon Ucko
May 24 at 22:09
add a comment |Â
So, just a simple rename? Good idea.
â Shervin Shahrdar
May 24 at 16:10
@ShervinShahrdar Yeah, I guess so.
â Solomon Ucko
May 24 at 22:09
So, just a simple rename? Good idea.
â Shervin Shahrdar
May 24 at 16:10
So, just a simple rename? Good idea.
â Shervin Shahrdar
May 24 at 16:10
@ShervinShahrdar Yeah, I guess so.
â Solomon Ucko
May 24 at 22:09
@ShervinShahrdar Yeah, I guess so.
â Solomon Ucko
May 24 at 22:09
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f195097%2fclean-implementation-of-ftp-file-cleaners%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Welcome to Code Review! I see you added one line to the code, which doesn't appear to invalidate the current answer, but if the changes were more drastic then those would be reversed. For more information on this, see what you may and may not do after receiving answers.
â Sam Onela
May 24 at 18:35
Hi @SamOnela, sorry about that. I just realized I was missing a re-throw statement in my catch block. I didn't know about the rules. Won't happen again. :)
â Shervin Shahrdar
May 24 at 18:46
1
1. Not terribly clean - you're mixing data processing (FTP file deletion) with user interface (
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.â Jesse C. Slicer
May 24 at 19:20
@JesseC.Slicer, Is it a common practice to use Log4Net in test projects? I simply want to write to a console, so that I can see the error message in the NUnit test runner and debug things faster. I do have an
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
?â Shervin Shahrdar
May 24 at 19:39