I’m aware of the concept of abstraction in programming and the use of interfaces and abstract classes but am a little confused with how it’s actually achieved and where the abstraction actually occurs. Consider the following basic example with a C# interface:
interface MyInterface
{
int Add(int num1, int num2);
}
public class MyClass : MyInterface
{
public int Add (int num1, int num2)
{
return num1 num2;
}
}
public class Program
{
public static void Main (string[] arg)
{
MyClass MyObject = new MyClass();
Console.WriteLine(MyObject.Add(1,2));
}
}
Where is the abstraction occurring? For me, the programmer, there appears to be none. There appears to be no hiding of implementation details. I can simply instantiate a new MyClass object with a MyClass reference and interact with its methods directly – how does the interface abstract MyClass and its methods?
Perhaps if the “Add” function itself executes another method from another class, then that particular method and class would be hidden and abstracted away? But I don’t see why an interface or abstract class is needed to facilitate this.
I’m guessing the abstraction via an interface must occur when I’ve created my program, say as some kind of library, and another user wants to use my class library in their code?
Would be very grateful for some clarification!
CodePudding user response:
Disclaimer: this answer attempts to give a real life example and why it is useful and I may get terminologies wrong.
You may work with a DI (Dependency Injection) in the future:
// Registering service
services.AddScoped<IMyService, MyService>();
// Attempt to resolve
var myService = serviceProvider.GetRequiredService<IMyService>();
Here, you do not know exactly what class you will get, you only know there would be an implementation of IMyService
. You can use all methods/members of IMyService
without caring how it's implemented.
It's especially useful for testing. Say in the real project, you can have:
public class MyRealService : IMyService
{
public void DoSomething() { Console.WriteLine("Real code"); }
}
// Register it
services.AddScoped<IMyService, MyRealService>();
And for test project, they can have a completely different implementation:
public class MyTestService : IMyService
{
public void DoSomething() { Console.WriteLine("Test code"); }
}
// Register it
services.AddScoped<IMyService, MyTestService>();
Here you can see, no matter the implementation, your code that uses it is the same since they have the same signature.
CodePudding user response:
You are never using the interface in your code. Let's look at another example:
interface IMathematicalOperation
{
double PerformOperation(double n1, double n2);
}
class AddOperation : IMathematicalOperation
{
public double PerformOperation(double n1, double n2)
{
return n1 n2;
}
}
class SubtractOperation : IMathematicalOperation
{
public double PerformOperation(double n1, double n2)
{
return n1 - n2;
}
}
class MultiplyOperation : IMathematicalOperation
{
public double PerformOperation(double n1, double n2)
{
return n1 - n2;
}
}
class DivideOperation : IMathematicalOperation
{
public double PerformOperation(double n1, double n2)
{
return n1 / n2;
}
}
Here we have implementations for , -, * and /.
And now we need some code to use these:
public static void Main (string[] arg)
{
IMathematicalOperation operation;
Console.WriteLine("Please enter , -, *, or /");
switch (Console.ReadLine())
{
case " ":
operation = new AddOperation();
break;
case "-":
operation = new SubtractOperation();
break;
case "*":
operation = new MultiplyOperation();
break;
case "/":
operation = new DivideOperation();
break;
default:
Console.WriteLine("That was not a valid choice.");
return;
}
ReadNumbersAndPerformOperation(operation);
}
static void ReadNumbersAndPerformOperation(IMathematicalOperation operation)
{
Console.Write("Please input the first number: ");
double n1 = double.Parse(Console.ReadLine()); // in real code use double.TryParse
Console.Write("Please input the second number: ");
double n2 = double.Parse(Console.ReadLine()); // in real code use double.TryParse
double result = operation.PerformOperation(n1, n2);
Console.WriteLine("The result is {0}", result);
}
As you can see, in the Main method we are selecting an operation, and passing it to ReadNumbersAndPerformOperation
. Because we are using the abstract interface IMathematicalOperation
, the method has no need to understand what operation is actually being performed - it just knows that there is a method it can call and pass the parameters to. Everything else happens in the abstract black box determined elsewhere.
Like this, you can produce entire service classes that depends on an interface of abstract base class without needing to be aware of the actual implementation specifics. In my own work, we have an IStorageService
with implementations (classes) for managing files on Amazon S3, Azure Blob Storage, and Google Cloud Storage. When we use IStorageService
we don't care what the underlying implementation is.
CodePudding user response:
encapsulation is hiding the structure that what does it mean by hiding "data" but abstracting is when you don't need to change the code but you add new inherited classes instead