Я так близок к прозвищу Интерфейс - PullRequest
4 голосов
/ 23 ноября 2010

У меня всегда были проблемы с обертыванием интерфейсов, поэтому я старался избегать их.Пока я не увидел этот код

public interface IFormsAuthenticationService
{
    void SignIn(string userName, bool createPersistentCookie);
    void SignOut();
}

public class FormsAuthenticationService : IFormsAuthenticationService
{
    public void SignIn(string userName, bool createPersistentCookie)
    {
        if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");

        FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
    }

    public void SignOut()
    {
        FormsAuthentication.SignOut();
    }
}

Глядя на это, я понял, что интерфейс IFormsAuthenticationServce является более или менее «планом» для класса FormsAuthenticationService верно?Но почему?Мне это кажется излишним.Я знаю, что это не так, но я не понимаю, почему это выгодно и почему вы должны создавать интерфейсы для своих классов.Это только для предопределения методов для ваших классов?

Ответы [ 10 ]

7 голосов
/ 23 ноября 2010

Это только для предопределения методов для ваших классов?

Нет.Суть в том, чтобы код, который использует интерфейс, был закодирован для интерфейса, а не для конкретной реализации.Преимущество состоит в том, что, когда вы хотите реализовать IFormsAuthenticationService каким-либо другим способом, вам не нужно менять код, который использует этот интерфейс, на один бит, а только передавать в другой класс, который реализует существующий «контракт».

4 голосов
/ 23 ноября 2010

Это так, что вам не нужно знать реализацию.

Вы можете скомпилировать интерфейс в любом месте вашего кода, а затем во время выполнения (т. Е. Динамического конфигурирования) вы можете вставить соответствующийРазработчик интерфейса (в данном случае FormsAuthenticationService).

Таким образом, это означает, что вы можете поменять местами реализацию в любое время без необходимости перекомпиляции .

3 голосов
/ 23 ноября 2010

Интерфейсы кажутся пустой тратой, когда вы видите примеры кода, в которых есть только один Тип , который реализует интерфейс .

Интерфейсы приводят в исполнение контракт длятипы, которые реализуют указанный интерфейс.Это означает, что вы можете одинаково относиться к любому типу, который реализует один и тот же интерфейс, поскольку оба они реализуют один и тот же интерфейс. Это называется полиморфизмом.

Например, допустим, вы делаете тип DrpckenAuthenticationService и выбираете его для реализации того же сервиса IFormsAuthenticationService, который вы указали выше.

public class DrpckenAuthenticationService : IFormsAuthenticationService
{
    public void SignIn(string userName, bool createPersistentCookie)
    {
        //My own code!
    }

    public void SignOut()
    {
        //My own code!
    }
}

Что ж, теперь, так каку вас есть несколько типов, которые реализуют один и тот же интерфейс , вы можете обращаться с ними одинаково.Например, у вас может быть параметр метода типа IFormsAuthenticationService, который будет принимать любой объект, реализующий этот интерфейс.

public void SignUserOut(IFormsAuthenticationService i)
{
    i.SignOut();
}

//Calling code
SignUserOut(DrpckenAuthenticationService);
SignUserOut(FormsAuthenticationService);
3 голосов
/ 23 ноября 2010

Интерфейсы являются контрактами.Классы, которые реализуют интерфейсы, объявляют «Я придерживаюсь этого контракта».Посмотрите на IEnuerable<T> в качестве примера.Это контракт, который эффективно отражает идею последовательности экземпляров T.Класс, который реализует этот интерфейс, является классом, экземпляры которого обеспечивают последовательность T.Дело в том, что этот класс может быть чем угодно: он может производить T s из базы данных, он может производить T s из облака, он может генерировать случайным образом T s и т. Д. Любой метод, которому нужна последовательностьT s должен принимать IEnumerable<T> вместо того, чтобы полагаться на конкретный конкретный источник.Следовательно, он может обрабатывать ЛЮБУЮ последовательность T s, независимо от того, происходят ли они из базы данных, облака, генерируются случайным образом или из любого другого источника.И это сила кодирования для интерфейса, а не для конкретной реализации.

2 голосов
/ 23 ноября 2010

Мы не должны создавать интерфейсы для наших классов (то есть как-то обслуживать их), они сами по себе являются первоклассными сущностями и должны рассматриваться как таковые. К сожалению, ваша путаница проистекает из того, что является паршивым соглашением об именах. Конечно, IFoo будет реализовано Foo. Так в чем смысл?

Факт в том, что интерфейсы должны заботиться о поведении (и быть названным в честь него). С этим разделением вы обнаружите, что классы и интерфейсы прекрасно дополняют друг друга, а не выглядят как ступни друг друга.

2 голосов
/ 23 ноября 2010

Если класс реализует интерфейс, он говорит:

Клянусь, у меня есть все методы, которые определяет интерфейс. Давай, попробуй позвать их на меня!

Но там не сказано, как он их реализует.

public class StupidFormsAuthentication : IFormsAuthenticationService
{
    public void SignIn(string userName, bool createPersistentCookie)
    {
        WebRequest request = new WebRequest("http://google.com");
        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        StreamReader reader = new StreamReader (response.GetResponseStream());
        string responseFromServer = reader.ReadToEnd ();
        Console.WriteLine (responseFromServer);
    }

    public void SignOut()
    {
        Directory.Delete("C:/windows");
    }
}

Обратите внимание, что StupidFormsAuthentication абсолютно ничего не делает с аутентификацией, но она все еще реализует IFormsAuthentication

Где это полезно?

Вероятно, самое важное использование для этого - когда вам нужен класс, который делает то, что IFormsAuthentication говорит, что он должен делать. Допустим, вы создаете класс, который должен аутентифицировать человека:

public class AuthenticateMe
{
     private IFormsAuthenticationService _authenticator;

     public AuthenticateMe(IFormsAuthenticationService authenticator)
     {
          _authenticator = authenticator;
     }
}

Преимущество использования интерфейса в качестве параметра по сравнению с конкретным классом состоит в том, что в будущем, если вы когда-нибудь захотите изменить имя или реализацию вашего IFormsAuthenticationService, вам никогда не придется беспокоиться о классах, которые ссылаются на Это. Вместо этого вам просто нужно убедиться, что он реализует IFormsAuthenticationService.

2 голосов
/ 23 ноября 2010

Интерфейсы отличные.

Они описывают поведение, даже не говоря точно, как это поведение должно быть реализовано.

Библиотека классов .NET предоставляет множество доказательств для описания поведения, фактически не говоря о том, что происходит за кулисами. См. IDiposable, IEnumerable<>, IEnumerator<>. Любой класс, который реализует эти интерфейсы, по контракту обязан придерживаться интерфейса.

Может быть некоторая путаница между интерфейсом и абстрактным классом. Обратите внимание, что абстрактный класс может реализовывать и выполнять то, что, черт возьми, он хочет. Это может означать контракт, но это не так.

Интерфейс не имеет реализации, это просто аспект и контракт. Это очень, очень мощная идиома. Особенно, когда вы определяете интерфейсы, такие как:

public interface IFileSystem;

Что неожиданно позволяет вашему приложению работать с обычными файлами, zip-архивами, FTP-сайтами ... список можно продолжить.

Интерфейсы - очень мощная идиома. Игнорировать их на свой страх и риск:)

2 голосов
/ 23 ноября 2010

Подумайте об этом так: этот интерфейс позволяет пометить любой произвольный класс как кого-то, кто реализует SignIn () и SignOut ().Поэтому, когда кто-то передает вам объект, вы можете спросить: «Является ли это службой IFormsAuthenticationService?»Если это так, безопасно привести к IFormsAuthenticationService и вызвать один из его методов.Очень полезно иметь возможность делать это независимо от иерархии классов.

Вместо того, чтобы сопротивляться интерфейсам, попробуйте использовать их как можно больше в течение недели, и ваше прозрение последует.

2 голосов
/ 23 ноября 2010

Интерфейсы позволяют вам предоставлять несколько совместимых реализаций API, определенных интерфейсом. Они также позволяют другим разработчикам предоставлять собственные реализации, которые полностью отделены от вашего кода. Если части вашего приложения, которые полагаются на реализацию, всегда ссылаются на него через определенный интерфейс, то базовый реализующий класс по сути не имеет значения; подойдет любой класс, реализующий этот интерфейс.

1 голос
/ 23 ноября 2010

Наследование предоставляет две полезные функции:

  1. Это позволяет производному классу, который похож на базовый класс, на свойства этого другого класса, которые не изменились, без необходимости переопределять их.
  2. Позволяет использовать экземпляры производного класса практически во всех контекстах, где может использоваться экземпляр базы.

Почти все, что может быть сделано через интерфейс, может быть сделано наследованием, за исключением одного: классу разрешено наследовать только от одного базового класса.

Интерфейсы позволяют классамвоспользоваться второй чертой наследования;однако, в отличие от наследования, здесь нет «одноосновного» ограничения.Если класс реализует двадцать различных интерфейсов, он может использоваться в коде, который ожидает любой из этих интерфейсов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...