Я пытаюсь понять Интерфейсы так, чтобы я мог реализовать их в своих программах, но я не могу вообразить, как я должен использовать их. Также дайте мне некоторых, например, использования их со множественным наследованием в C#
Хорошим примером интерфейса является шаблон репозитория. Ваш интерфейс будет определять такие методы, как Get, GetAll, Update, Delete и т. Д. Без реализации, только сигнатуры функций.
Затем вы можете написать «конкретную» реализацию этого класса для работы, скажем, с MySQL. Однако ваш пользовательский интерфейс должен относиться только к интерфейсу.
Позже, если вы решите перейти на Microsoft SQL, вы напишете другую конкретную реализацию, но ваш код пользовательского интерфейса не должен (сильно) меняться.
Множественное наследование не существует в C # в том смысле, что вы можете наследовать только от одного «конкретного» класса; хотя вы можете унаследовать (или «реализовать») столько интерфейсов, сколько захотите.
Интерфейсы просто определяют контракт общедоступных элементов (например, свойств, методов, событий) для вашего объекта, а не поведения.
interface IDog
{
void WagTail(); //notice no implementation
ISound Speak(); //notice no implementation
}
class Spaniel : IDog
{
public void WagTail()
{
Console.WriteLine("Shook my long, hairy tail");
}
public ISound Speak()
{
return new BarkSound("yip");
}
}
class Terrier : IDog
{
public void WagTail()
{
Console.WriteLine("Shook my short tail");
}
public ISound Speak()
{
return new BarkSound("woof");
}
}
ОБНОВЛЕНИЕ
В "реальном" примеры "Я использую интерфейсы с: - Модульное тестирование - ОБЩИЕ (например, репозиторий, шлюз, настройки)
interface Repository<T>{
T Find(Predicate<T>);
List<T> ListAll();
}
interface Gateway<T>{
T GetFrom(IQuery query);
void AddToDatabase(IEntity entityItem);
}
interface Settings<T>{
string Name { get; set; }
T Value { get; set; }
T Default { get; }
}
Вот один (на Java, но это не важно, поскольку они похожи): В своем проекте я создал простой интерфейс:
public interface Identifiable<T> {
public T getId();
}
Это простая замена некоторым видам аннотаций. Следующий шаг: я заставил все классы сущностей реализовать этот интерфейс.
Третий шаг - написать несколько синтаксически-похожих на сахар методов:
public <T> List<T> ids(List<? extends Identifiable<T> entities) { ... }
Это был всего лишь пример.
Более сложный пример - это кое-что например, правила проверки: у вас есть механизм проверки (вероятно, написанный вами) и простой интерфейс для правила:
public interface ValidationRule {
public boolean isValid(...);
}
Итак, этот механизм требует, чтобы правила были реализованы вами. И, конечно же, будет множественное наследование, так как вы определенно захотите большего, чем одно правило.
Иногда слишком абстрактное мышление просто мешает, а ссылка на детали реализации действительно проясняет ситуацию. Поэтому я' Я предоставлю близкое к металлическому объяснение интерфейсов, которое заставило меня окончательно их разобрать.
Интерфейс - это просто способ объявить, что класс реализует некоторые виртуальные функции, и как эти виртуальные функции должны быть размещены в классе vtable . Когда вы объявляете интерфейс, вы по сути даете компилятору высокоуровневое описание таблицы виртуальных функций. Реализуя интерфейс, вы сообщаете компилятору, что хотите включить vtable, на которую ссылается этот интерфейс, в свой класс.
Назначение интерфейсов состоит в том, что вы можете неявно привести класс, реализующий интерфейс I, к экземпляру интерфейса I:
interface I {
void doStuff();
}
class Foo : I {
void doStuff() {}
void useAnI(I i) {}
}
var foo = new Foo();
I i = foo; // i is now a reference to the vtable pointer for I in foo.
foo.useAnI(i); // Works. You've passed useAnI a Foo, which can be used as an I.
Взгляните на то, с чем вы знакомы - например, на коллекцию списков в C #. Списки определяют интерфейс IList, а общие списки определяют интерфейс IList. IList предоставляет такие функции, как Добавить, Удалить, а список реализует эти функции. Существуют также списки привязок, которые реализуют IList несколько иначе.
Я также рекомендовал бы Шаблоны проектирования Head First . Примеры кода написаны на Java, но легко переводятся на C #, плюс они познакомят вас с реальной мощью интерфейсов и шаблонов проектирования.
Я пишу видеоигру. В этой видеоигре я применяю разные силы к объектам в игре. Сила тяги, силы удара, силы тяжести. Хотя они рассчитываются по-разному, все они имеют одинаковые основные элементы. Мне нужно вызвать функцию обновления, которая оценит силу и добавит силу к объекту, к которому она прикреплена.
Итак, я создал интерфейс IForce, который имеет функцию обновления для своей подписи. Все мои силы реализуют этот интерфейс:
public interface IForce
{
void Update(Particle particle, GameTime gameTime);
}
Вот пример реализации.
public class Spring : IForce
{
private Particle ThisParticle;
private Particle ThatParticle;
private float K;
public Spring(Particle thisParticle, Particle thatParticle, float k)
{
ThisParticle = thisParticle;
ThatParticle = thatParticle;
}
public void Update(Particle particle, GameTime gameTime)
{
float X = Vector3.Length(ThisParticle - ThatParticle);
ThisParticle.Forces.Add(K * X);
}
}
Функция обновления имеет упрощенное обновление силы пружины, чтобы его было легче понять.
Это помогает несколькими способами.
I может полностью изменить способ вычисления силы, не влияя на другие части моего кода. Я делаю это все время. В том же духе мне до смешного легко добавить новые силы. Я знаю, что поскольку он реализует интерфейс IForce, он будет хорошо взаимодействовать с моим существующим кодом.
Еще один способ, которым он помогает, - это обработка большого количества сил. У меня есть реестр сил, в котором есть список IForce. Поскольку все силы реализуют этот интерфейс и имеют функцию обновления, очень легко обновить все силы в моей игре. Когда я создаю силу, я добавляю ее в список. Затем я просматриваю список и вызываю функцию обновления каждого элемента, не беспокоясь о том, какой это тип силы, и все мои силы обновляются.
Я использую интерфейсы каждый день во множестве различных ситуаций. Они фантастические!
Поскольку все силы реализуют этот интерфейс и имеют функцию обновления, очень легко обновить все силы в моей игре. Когда я создаю силу, я добавляю ее в список. Затем я просматриваю список и вызываю функцию обновления каждого элемента, не беспокоясь о том, какой это тип силы, и все мои силы обновляются.Я использую интерфейсы каждый день во множестве различных ситуаций. Они фантастические!
Поскольку все силы реализуют этот интерфейс и имеют функцию обновления, очень легко обновить все силы в моей игре. Когда я создаю силу, я добавляю ее в список. Затем я просматриваю список и вызываю функцию обновления каждого элемента, не беспокоясь о том, какой это тип силы, и все мои силы обновляются.Я использую интерфейсы каждый день во множестве различных ситуаций. Они фантастические!
Множественное наследование означает, что класс можно использовать. в нескольких ситуациях: [псевдокод]
interface Shape {
// shape methods like draw, move, getboundingrect, whatever.
}
interface Serializable {
// methods like read and write
}
class Circle : public Shape, public Serializable {
// TODO: implement Shape methods
// TODO: implement Serializable methods
}
// somewhere later
{
Circle circle;
// ...
deserializer.deserialize(circle);
// ...
graphicsurface.draw(circle);
// ...
serializer.serialize(circle);
}
Идея состоит в том, что ваш класс Circle реализует два разных интерфейса, которые используются в очень разных ситуациях.
В качестве примечания, вы можете отключить функцию автоматического изменения размера Firefox, отправив (несколько плохо задокументированную) команду enableObjectResizing
в документ:
document.execCommand("enableObjectResizing", false, false);
AFAIK, это можно безопасно сделать только после загрузки документа, и я не знаю способа отключить граббер, который является отдельной функцией.
Мы создали программу, которая позволяет пользователю создавать 4 различных вида карт. Для этого я создал 4 разных типа классов-генераторов. Однако все они реализуют интерфейс 'IGenerator':public interface IGenerator {
public void generateNow(int period);
}
Который говорит им определить по крайней мере функцию «public generateNow (int period)».
Какой бы генератор у меня ни был изначально, после того, как я передал его в «IGenerator» I может вызвать на нем "generateNow (4)". Мне не нужно будет точно знать, какой тип генератора я вернул, что по сути означает, что больше никаких «экземпляров переменной класса 1», «экземпляра переменной класса 2» и т. Д. В гигантском операторе if.