Как мы помним из лекций, интерфейсы позволяют уйти от сильного связывания кода. Т.е. вместо использования переменной конкретного класса (если вспомнить трансформеров, то вместо класса "винтовка" мы используем интерфейс "оружие") мы используем переменную типа интерфейс, которой может быть присвоен любой объект, который реализует этот интерфейс.
// объявляем интерфейс - это просто список методов, который должен реализовать класс
interface IWeapon
{
void Fire();
}
// класс, реализующий интерфейс
class Berdanka : IWeapon
{
public override void Fire()
{
Console.WriteLine("Ба-бах");
}
}
// еще один класс, реализующий интерфейс
class Палка : IWeapon
{
public override void Fire()
{
if(прошёл_год)
Console.WriteLine("Раз в год и палка стреляет!");
else
Console.WriteLine("Я просто палка");
}
}
class Transformer
{
// у трансформера есть свойство СуперОружие типа IWeapon (т.е. может стрелять)
IWeapon SuperWeapon;
public Transformer()
{
// на складе выдали берданку
SuperWeapon = new Berdanka();
}
public void Attack()
{
SuperWeapon.Fire();
}
}
Т.е. трансформеру пофиг что ему всучили на складе, главное что это реализует интерфейс IWeapon (умеет стрелять).
Интерфейсы часто используются при интеграционном тестировании в качестве заглушек. Например, в приложении есть класс API, который реализует авторизацию на сервере (login, logout...). Хорошей практикой является вынос этих методов в интерфейс и реализация при тестировании класса заглушки:
class Mock: IAPI
{
public bool Login(string name, string password)
{
return true;
}
public bool Logout()
{
return true;
}
}
Т.е. при тестировании приложения мы не зависим от наличия сети и работоспособности сервера, и проверяем только работоспособность приложения.
Задания