Самый простой пример, который приходит на ум: представьте, что вы хотите получать оповещения об ошибках на сайте по email/смс/чему-нибудь ещё.
С интерфейсами ваше решение будет выглядеть как-то так:
// Собственно, сам интерфейс оповещений
public interface INotification
{
void Notify(string text);
}
public class EmailNotification : INotification
{
public void Notify(string text)
{
// код по отправке почты
}
}
public class SmsNotification : INotification
{
public void Notify(string text)
{
// код по отправке смс
}
}
// ... Еще какие-нибудь классы оповещений
// В каком-нибудь классе, где может появиться ошибка
public class MaybeErrorClass
{
private INotification _notification;
public MaybeErrorClass(INotification notification)
{
// Класс не должен знать, по какому каналу оповещать об ошибках.
// Он работает с абстракцией
this._notification = notification;
}
// Очень простой пример метода, в котором ожидаем генерацию ошибки
public void DoSomething()
{
try {
// какой-то блок, в котором можем получить ошибку
}
catch (Exception e)
{
this._notification.Notify("А у нас тут ошибка!");
}
}
}
Теперь можно создавать экземпляры этого класса, передавая ему желаемый тип оповещения:
var maybeErrorEmail = new MaybeErrorClass(new EmailNotification());
var maybeErrorSms = new MaybeErrorClass(new SmsNotification());
Теперь вопрос на засыпку: как бы вы решили подобную задачу без интерфейсов?
На ум приходят только абстрактные классы (кстати, интерфейс - это тоже абстрактный класс), но их лучше использовать только в случае, если у производных классов есть какая-то общая логика, которую не хотелось бы дублировать.