Сейчас Вам, скорее всего, это не надо, Вы столкнетесь с этим когда-нибудь позже, когда будете готовы. Не заморачивайтесь и идите дальше, очень часто программисты вынуждены пропускать темы и возвращаться к ним. Главное, что Вы знаете, что есть такая вещь, как интерфейс, и она позволяет скрывать реальные реализации классов.
С помощью интерфейсов Вы:
- не завязываете логику своего приложения с конкретными классами и можете легко вносить изменения в одни классы, не трогая другие, добавлять функциональность или заменить один класс абсолютно другим, не трогая все приложение. То есть реализуется абстракция от реализации;
- можете проектировать логику приложений на интерфейсах, оставив реализацию на потом и применяя заглушки;
- применять так называемое множественное наследование;
- если будете начальником, сможете легко донести свои мысли до подчиненных (реализуй мне функциональность вот этого интерфейса).
Я дам несколько примеров, в которых применяется интерфейс, но сразу оговорюсь, они не являются лучшими представителями. Ведь я тоже учусь. )
Проектирование недозоопарка
Давайте запроектируем небольшую программу зоопарка, при этом зоопарк у нас бужет пошаговым.
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.
Ну и в конце концов вы можете ставить заглушки и передавать тестовые данные.
Если реализация классов достаточно сложна, например, один из них получает данные из базы, а другой обрабатывает эти данные, то вы можете написать ненастоящий класс. То есть вместо реальных данных Вы передаете класс, которые не использует подключение к БД, и потом его можно будет переписать. Причем можно работать нормально как с одним, так и с другим классом попеременно.