在编程中,接口定义了对象将具有的行为,但实际上并没有指定行为。这是一个契约,它将保证某个类可以做某事。
在这里考虑这段 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() 方法,那么一切都很好。如果没有:卡普特!
这个错误不会在编译时出现,而是在运行时出现,因此可能在您的程序已经部署和运行时出现。 C# 会在您接近之前通知您。
天真地说,之所以使用这种机制,是因为在 OO 语言中,函数自然不是一等公民。如您所见,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)
lambda 只是一个函数,它被声明为“随用随用”。
委托在 C# 中是一样的:
class Game
{
public Game(Func<int> generate)
{
Console.WriteLine(generate())
}
}
new Game(() => 5);
new Game(() => new Random().Next(0, 10));
旁注:后面的例子在 Java 7 之前是不可能的。在那里,接口是你指定这种行为的唯一方法。但是,Java 8 引入了 lambda 表达式,因此 C# 示例可以很容易地转换为 Java(Func<int> 变为 java.util.function.IntSupplier 和 => 变为 ->)。