我观看了WWDC15的面向协议的编程视频。看完这个视频后,我感到困惑。谁能给我一个相关的例子来说明这个想法?
更多的协议扩展是运营商重载的真正替代。
在动态类型语言(Ruby,Python,Javascript等)中,存在"鸭子类型"的概念,它本质上说,只要对象响应某些方法,对象的实际类型并不重要。而不是检查instance_of?
,您可以在使用检查来确定是否可以调用方法时检查哪个更相关responds_to?
。
协议只是鸭子类型的正式声明。既然你要求了一个示例(代码是 Ruby,它是动态类型的——如果你不熟悉 Ruby,只需将其视为伪代码,并想象所有键入为 id
和返回值都是void
):
想象一下,我们正在构建一个程序来模拟交通。我们可能有几种不同的交通方式:
class Bicycle
def goto(x, y)
# Implementation details
end
end
class Car
def goto(x, y)
# Implementation details
end
end
class Boat
def goto(x, y)
# Implementation details
end
end
class Jetpack
def goto(x, y)
# Implementation details
end
end
所有这些类都成为Vehicle
父类的子类可能没有意义,因为Jetpack
的属性和实现细节与Bicycle
非常不同。但请注意,所有这些类都响应 goto(x, y)
方法。创建另一个也响应此方法的类(例如Helicopter
)很容易。
现在假设我们正在使用以下类之一:
class Person
def travel(vehicle, destination)
vehicle.goto(destination.x, destination.y)
end
end
无论vehicle
是将来定义的Bicycle
、Boat
还是其他类,此代码都将起作用,因为此代码调用的是协议方法而不是类型方法。
A.基本问题
想想这种情况:
您有与其他实例交互的类A
实例。 A
必须知道其他实例所属类的 API。有两种方法可以实现此目的:
- 其他实例属于(可能是虚拟的)基类
B
,A
知道类B
(通过导入或其他方式)。在这种情况下,A
可以依靠B
的能力。 - 其他实例
B
实现协议,A
知道协议B
(通过导入或其他方式)。在这种情况下,A
也可以依靠B
的能力。
解决方案 1 的缺点是A
指示其他实例的(基)类。这可能会导致一个问题:首先,基类B
不适合。此外,也许B
应该充当许多类的"助手",而不仅仅是A
。这将导致多重继承。不,不,不,没有人想要这样。
多协议实现没有问题。
B. 子类化与委托+协议作为专业化的模式
在许多情况下,您会发现一个基类A
,出于某些原因(即数据访问和提供)应该专门化。在基于类的编程语言中,您将找到一个(虚拟)基类A
并对其进行子类化。除了A)下讨论的问题之外,您还会发现其他一些问题:
- 子类
- 是白盒:子类比其他类知道得更多。
- 基类包含许多方法。应该覆盖的方法集是什么?
和委托),您可以更改它:每当基类想要专用化时,它都会将方法放入协议中,并在需要专用信息时"调用"实现协议的实例(向该协议的实现者发送消息)。另一个类可以(可选)实现此协议并充当专用器。
这称为委托,使用协议实现并替换子类。
三.更抽象
你可以用更抽象的方式看到这一点:
- 协议声明一组方法,一个API。
- 类声明一组方法、一个 API,并实现它们。
因此,两者之间的区别在于类是实现器。
所以有一个经验法则:如果你关心实现某些东西,请使用子类,因为你可以从基类继承很多代码。如果您关心声明 API,请选择一个协议(并在类而不是子类中实现它)。