В программировании интерфейс определяет поведение объекта, но фактически не определяет его поведение. Это контракт, который гарантирует, что определенный класс может что-то сделать.
Рассмотрим этот фрагмент кода C # здесь:
using System;
public interface IGenerate
{
int Generate();
}
// Dependencies
public class KnownNumber : IGenerate
{
public int Generate()
{
return 5;
}
}
public class SecretNumber : IGenerate
{
public int Generate()
{
return new Random().Next(0, 10);
}
}
// What you care about
class Game
{
public Game(IGenerate generator)
{
Console.WriteLine(generator.Generate())
}
}
new Game(new SecretNumber());
new Game(new KnownNumber());
Класс Game требует секретного номера. Ради тестирования вы хотели бы ввести то, что будет использоваться в качестве секретного числа (этот принцип называется инверсией контроля).
Класс игры хочет быть «открытым» относительно того, что на самом деле создаст случайное число, поэтому он запросит в конструкторе «все, что имеет метод Generate».
Во-первых, интерфейс указывает, какие операции будет выполнять объект. Это просто содержит то, на что это похоже, но никакой фактической реализации не дано. Это просто подпись метода. Традиционно, в интерфейсах C # с префиксом I.
Классы теперь реализуют интерфейс IGenerate. Это означает, что компилятор удостоверится, что у них обоих есть метод, который возвращает int и называется Generate
.
Игру сейчас называют двумя разными объектами, каждый из которых реализует правильный интерфейс. Другие классы выдают ошибку при сборке кода.
Здесь я заметил аналогию с планом:
Класс обычно рассматривается как план объекта. Интерфейс определяет что-то, что должен будет сделать класс, поэтому можно утверждать, что это действительно просто план для класса, но, поскольку классу не обязательно нужен интерфейс, я бы сказал, что эта метафора ломается. Думайте об интерфейсе как о контракте. Класс, который «подписывает его», будет юридически обязателен (принудительно установлен полицией компилятора), чтобы соответствовать условиям договора. Это значит, что для этого придется делать то, что указано в интерфейсе.
Это все из-за статически типизированной природы некоторых языков OO, как в случае с Java или C #. В Python, с другой стороны, используется другой механизм:
import random
# Dependencies
class KnownNumber(object):
def generate(self):
return 5
class SecretNumber(object):
def generate(self):
return random.randint(0,10)
# What you care about
class SecretGame(object):
def __init__(self, number_generator):
number = number_generator.generate()
print number
Здесь ни один из классов не реализует интерфейс. Python не заботится об этом, потому что класс SecretGame
будет просто пытаться вызвать любой объект, в который передан. Если объект имеет метод generate (), все в порядке. Если это не так: KAPUTT!
Эта ошибка будет видна не во время компиляции, а во время выполнения, поэтому, возможно, когда ваша программа уже развернута и работает. C # уведомит вас, прежде чем вы приблизитесь к этому.
Причина, по которой этот механизм используется, наивно изложена, потому что в ОО-языках естественным образом функции не являются гражданами первого сорта. Как видите, KnownNumber
и SecretNumber
содержат ПРОСТО функции для генерации числа. Один действительно не нуждается в классах вообще. Следовательно, в Python их можно просто выбросить и выбрать функции самостоятельно:
# OO Approach
SecretGame(SecretNumber())
SecretGame(KnownNumber())
# Functional Approach
# Dependencies
class SecretGame(object):
def __init__(self, generate):
number = generate()
print number
SecretGame(lambda: random.randint(0,10))
SecretGame(lambda: 5)
Лямбда - это просто функция, которая была объявлена "в очереди, как вы идете".
В C # точно такой же делегат:
class Game
{
public Game(Func<int> generate)
{
Console.WriteLine(generate())
}
}
new Game(() => 5);
new Game(() => new Random().Next(0, 10));
Последние примеры не возможны, как это в Java, поскольку Java не может перемешиваться с функциями, как это могут другие два (Python и C #). Там интерфейсы - ваш единственный способ описать поведение таким образом в av.