Since interfaces cannot contain implementation, that seems to me to lead to code duplication in the classes that inherit from the interface. In the example below, pretend that, let's say, the first 10 or so lines that setup reading from a Stream are duplicated. Try not to focus on the wording here, but, instead focus on the concept of how easy it is to create duplicate code between each class.
For example:
public interface IDatabaseProcessor
{
void ProcessData(Stream stream);
}
public class SqlServerProcessor : IDatabaseProcessor
{
void ProcessData(Stream stream)
{
// setting up logic to read the stream is duplicated code
}
}
public class DB2Processor : IDatabaseProcessor
{
void ProcessData(Stream stream)
{
// setting up logic to read the stream is duplicated code
}
}
I realize that using an abstract base class for ProcessData and adding non-abstract members is one solution. However, what if I really, really want to use an interface instead?
How to Apply the Guideline. To avoid the problem of duplicated bugs, never reuse code by copying and pasting existing code fragments. Instead, put it in a method if it is not already in one, so that you can call it the second time that you need it.
The conventional approach to reduce this kind of code duplication is to move the common code to a member function, which can be called from all the constructors. Usually, that member function is called init.
Duplicated code is considered one of the worse code smells. Beyond blatant copy paste, there are subtle duplications like parallel inheritance hierarchies and repetitive code structures.
It's safe to say that duplicate code makes your code awfully hard to maintain. It makes your codebase unnecessary large and adds extra technical debt. On top of that, writing duplicate code is a waste of time that could have been better spent.
This is a case where you would want to use both an interface and an abstract base class.
The only reason you would have both is because another class would not share the abstract base code but would honor the interface. Consider:
public interface IDatabaseProcessor {
void ProcessData(Stream stream);
}
public abstract class AbstractDatabaseProcessor : IDatabaseProcessor {
public void ProcessData(Stream stream) {
// setting up logic to read the stream is not duplicated
}
}
public class SqlServerProcessor : AbstractDatabaseProcessor {
//SqlServerProcessor specific methods go here
}
public class DB2Processor : AbstractDatabaseProcessor {
// DB2Processor specific methods go here
}
public class NonSharedDbProcessor : IDatabaseProcessor {
void ProcessData(Stream stream) {
// set up logic that is different than that of AbstractDatabaseProcessor
}
}
Syntax might be a little off, I am not a regular C# user. I came here through OOP tag.
The best way to share the code across interfaces is through stateless extension methods. You can build these extensions once, and use it in all classes implementing the interface, regardless of their inheritance chain. This is what .NET did with IEnumerable<T>
in LINQ, for rather impressive results. This solution is not always possible, but you should prefer it whenever you can.
Another way to share logic is by creating an internal "helper" class. This looks like the right choice in your case: implementations can call the internally shared code as helper's methods, without the need to duplicate any code. For example:
internal static class SqlProcessorHelper {
public void StreamSetup(Stream toSetUp) {
// Shared code to prepare the stream
}
}
public class SqlServerProcessor : IDatabaseProcessor {
void ProcessData(Stream stream) {
SqlProcessorHelper.StreamSetup(stream);
}
}
public class DB2Processor : IDatabaseProcessor {
void ProcessData(Stream stream) {
SqlProcessorHelper.StreamSetup(stream);
}
}
The helper class does not need to be static: if your shared methods need state, you can make your helper a regular class, and put an instance of it in each implementation of your interface where you would like to share code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With