Интерфейс и его примеры

Рейтинг: 6Ответов: 1Опубликовано: 08.02.2015

Привет всем!

В продолжение темы Ссылка на класс или делегат, где уважаемый @Alex Krass пытался мне объяснить, какая польза от применения интерфейса.

Для меня все так же остается загадкой. Я все так же считаю, что это лишняя писанина кода. За это время я почитал немало статей про интерфейс и просмотрел видеокурс. Но так и не разобрался. Честно сказать, я не знаю, почему я зациклился на нем, ведь не знал бы о его существовании, давно бы закончил писать свой проект.

Вот, к примеру, Алекс объясняет, что в его примере он с легкостью может добавить еще 100 классов useConnect, но, на мой взгляд, ему также нужно 100 раз вызывать connect = new UseConnect(ссылка на класс());, причем в его конкретном случае ему придется плодить switch, чтобы вызвать все классы. Причем я так и не понял, как в таком случае можно добраться до метода из какого-либо класса, в том примере их можно вызвать только все кучей. Причем если в главном классе мне нужно будет еще раз использовать какой-нибудь метод, то мне опять придется переписывать connect = new UseConnect(ссылка на класс());. В моем же случае, где я просто пишу ссылку на класс и через переменную вызываю метод, так я эту переменную могу использовать где захочу и когда захочу, причем могу вызвать любой из методов. Да и вообще мне не придется писать класс UseConnect, я могу вызывать методы сразу, в данном примере из класса MySQL, к примеру.

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

Вот пример. У меня есть форма, в которой пользователь может сохранять записи в базе данных. В моем случае MySQL. Он может внести запись, после вызвать ее и сделать изменения. У меня есть пять классов. Главный, который вызывает форму, класс инсерт, класс упдата, класс селест и класс подключения к базе. Класс инсерт упдата и селект наследуют класс подключения к базе. Ну это я так решил сделать, чтобы не использовать интерфейс. Вот и сама задача: как мне эти четыре класса с помощью интефейса(ов) подключить к главному классу с формой.

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

Ответы

▲ 15

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

С помощью интерфейсов Вы:

  • не завязываете логику своего приложения с конкретными классами и можете легко вносить изменения в одни классы, не трогая другие, добавлять функциональность или заменить один класс абсолютно другим, не трогая все приложение. То есть реализуется абстракция от реализации;
  • можете проектировать логику приложений на интерфейсах, оставив реализацию на потом и применяя заглушки;
  • применять так называемое множественное наследование;
  • если будете начальником, сможете легко донести свои мысли до подчиненных (реализуй мне функциональность вот этого интерфейса).

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

Проектирование недозоопарка

Давайте запроектируем небольшую программу зоопарка, при этом зоопарк у нас бужет пошаговым.

1. В первую очередь его стоит населить животными, которые умеют ходить по нему, приступим.

Для описания жизненного цикла зоопарка я использую класс ZooLivecycle:

    class ZooLivecycle
{
    public void allPetWalk()
    {

    }
}

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

interface IWalk 
{ 
    void walk();
}
class Cat : IWalk
{
    public void walk()
    {
        Console.WriteLine("cat walk");
    }
}
class Dog : IWalk
{
    public void walk()
    {
        Console.WriteLine("dog walk");
    }
}
class Fish : IWalk
{
    public void walk()
    {
        Console.WriteLine("fish cant walk");
    }
}
class Bird : IWalk
{
    public void walk()
    {
        Console.WriteLine("bird can fly");
    }
}

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

class ZooLivecycle
{
    public void allPetWalk(IWalk[] arr)
    {
        for (int i = 0; i < arr.Count(); i++)
            arr[i].walk();
    }
}

Итак, теперь можно вызывать нашу реализацию.

class Program
{
    static void Main(string[] args)
    {
        ZooLivecycle zoo = new ZooLivecycle();

        IWalk[] walks = new IWalk[]{new Cat(), new Dog(), new Fish(), new Bird()};

        Console.WriteLine("\n*Pet Walk*");
        zoo.allPetWalk(walks);

        Console.Read();
    }

}

А теперь попробуйте реализовать это без интерфейсов, главным образом функцию allPetWalk, хотя есть способ - это общий класс родитель. Можно также попробовать передавать ссылку как public void allPetWalk(Object[] arr) и потом приводить типы, но это является очень плохой практикой, так как вы теряете контроль над типами и вынуждены добавлять проверки, что не является лучшим решением.

2. Идем дальше, мы решили добавить систему наблюдения, а именно видеокамеры, которая сможет передавать информацию.

interface IObservation
{
    void analyze();
}
class Videocamera : IObservation
{
    public void analyze()
    {
        Console.WriteLine("In zoo all ok");
    }
}

Реализуем получение информации в классе зоопарка:

class ZooLivecycle
{
    public void allPetWalk(IWalk[] arr)
    {
        for (int i = 0; i < arr.Count(); i++)
            arr[i].walk();
    }
    public void zooAnalyze(IObservation[] arr)
    {
        for (int i = 0; i < arr.Count(); i++)
            arr[i].analyze();
    }
}

Ну и вызов.

class Program
{
    static void Main(string[] args)
    {
        ZooLivecycle zoo = new ZooLivecycle();

        IWalk[] walks = new IWalk[]{new Cat(), new Dog(), new Fish(), new Bird()};
        IObservation[] analyze = new IObservation[] { new Videocamera() };

        Console.WriteLine("\n*Pet Walk*");
        zoo.allPetWalk(walks);
        Console.WriteLine("\n*Analyze zoo*");
        zoo.zooAnalyze(analyze);

        Console.Read();
    }

}

3. А теперь давайте подложим свинью, если кто-то все еще использовал класс родитель, вместо интерфейсов.

Реализуем помошника, который может как ходить по зоопарку, так и передавать информацию. В языке C# нет перекрестного наследования, но зато можно наследовать несколько интерфейсов вместо этого.

class HelperPet : IWalk, IObservation
{
    public void walk()
    {
        Console.WriteLine("helppet walk");
    }

    public void analyze()
    {
        Console.WriteLine("helppet analyze");
    }
}

Это все, что пришлось добавить. Можно дальше пользоваться нашим зоопарком, ничего не переписывая.

class Program
{
    static void Main(string[] args)
    {
        ZooLivecycle zoo = new ZooLivecycle();

        IWalk[] walks = new IWalk[]{new Cat(), new Dog(), new Fish(), new Bird(), new HelperPet()};
        IObservation[] analyze = new IObservation[] { new Videocamera(), new HelperPet() };

        Console.WriteLine("\n*Pet Walk*");
        zoo.allPetWalk(walks);
        Console.WriteLine("\n*Analyze zoo*");
        zoo.zooAnalyze(analyze);

        Console.Read();
    }

}

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

Очень часто интерфейсы используются как описательные модели, наследование от которых дает возможность работать с тем, для чего они созданы. Тем самым вы не привязываетесь к конкретным классам. В самом языке очень C# даже есть интерфейсы, которые реализуют то или иное поведение. Вы очень часто будете с этим сталкиваться, если перейдете на WPF или ASP.NET MVC приложения.

Использование foreach с собственными классами

Давайте улучшим наш зоопарк и добавим класс для хранения животных. Несмотря на то, что он довольно объемный, я просто взял с MSDN пример и просто подставил свои значения.

    public interface IWalk 
{ 
    void walk();
}

    public class Animals : IEnumerable<IWalk>
{
    private IWalk[] _pets;
    public Animals(IWalk[] pArray)
    {
        _pets = new IWalk[pArray.Length];

        for (int i = 0; i < pArray.Length; i++)
        {
            _pets[i] = pArray[i];
        }
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return (IEnumerator)GetEnumerator();
    }

    public PetsEnum GetEnumerator()
    {
        return new PetsEnum(_pets);
    }

    IEnumerator<IWalk> IEnumerable<IWalk>.GetEnumerator()
    {
        return (IEnumerator<IWalk>)GetEnumerator();
    }
}
public class PetsEnum : IEnumerator
{
    public IWalk[] _pets;

    int position = -1;

    public PetsEnum(IWalk[] list)
    {
        _pets = list;
    }

    public bool MoveNext()
    {
        position++;
        return (position < _pets.Length);
    }

    public void Reset()
    {
        position = -1;
    }

    object IEnumerator.Current
    {
        get
        {
            return Current;
        }
    }

    public IWalk Current
    {
        get
        {
            try
            {
                return _pets[position];
            }
            catch (IndexOutOfRangeException)
            {
                throw new InvalidOperationException();
            }
        }
    }
}

Теперь я могу делать так:
class Program
{
    static void Main(string[] args)
    {
        ZooLivecycle zoo = new ZooLivecycle();

        Animals animals = new Animals(new IWalk[]{new Cat(), new Dog(), new Fish(), new Bird(), new HelperPet()});

        Console.WriteLine("\n*Pet Walk*");
        zoo.allPetWalk(animals);

        Console.Read();
    }

}

class ZooLivecycle
{
    public void allPetWalk(Animals animals)
    {
        foreach(IWalk animal in animals) 
            animal.walk();
    }
}

Не реализовав интерфейс, вы не сможете использовать с вашим классом foreach, хотя на самом деле это делается очень быстро. При этом метод foreach работает с любыми объектами, и не будь интерфейсов, достигнуть такого эффекта было бы сложнее. Теперь в класс Animals можно добавить такие методы, как Add, Remove, etc.

Ну и в конце концов вы можете ставить заглушки и передавать тестовые данные.

Если реализация классов достаточно сложна, например, один из них получает данные из базы, а другой обрабатывает эти данные, то вы можете написать ненастоящий класс. То есть вместо реальных данных Вы передаете класс, которые не использует подключение к БД, и потом его можно будет переписать. Причем можно работать нормально как с одним, так и с другим классом попеременно.