Elixir: 多太(Polymorphism)

developerworks

概述

多态(Polymorphism)按字面的意思就是"多种状态". 在面向对象语言中, 接口的多种不同的实现方式即为多态. 多态性是允许你将父对象设置成为一个或更多的他的子对象相等的技术, 赋值之后, 父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作. 简单的说, 就是一句话: 允许将子类类型的指针赋值给父类类型的指针. 多态性在Object Pascal和C++中都是通过虚函数实现的.

多太是单一接口接受不同类型的技术. 根本上, 它允许不同的数据类型适用于相同的函数. 不同类型的相同函数形态完成相同的行为. Elixir 适用协议(Protocols)实现多太.

下面以一个温度的不同表示形式来说明如何在开发过程中使用多太.

# 开尔文
defmodule Kelvin do
  defstruct name: "Kelvin", symbol: "K", degree: 0 
end
# 华氏温度
defmodule Fahrenheit do
  defstruct name: "Fahrenheit", symbol: "°F", degree: 0
end
# 摄氏温度
defmodule Celsius do
  defstruct name: "Celsius", symbol: "°C", degree: 0
end
# 温度协议(接口, 只有函数定义, 没有函数实现)
defprotocol Temperature do
  @doc """
  Convert Kelvin and Fahrenheit to Celsius degree
  """
  def to_celsius(degree)
end

# 开尔文, 实现了Temperature 协议(接口)
defimpl Temperature, for: Kelvin do
  @doc """
  Deduct 273.15
  """
  def to_celsius(kelvin) do
    celsius_degree = kelvin.degree - 273.15
    %Celsius{degree: celsius_degree}
  end
end
# 华氏摄氏度实现
defimpl Temperature, for: Fahrenheit do
  @doc """
  Deduct 32, then multiply by 5, then divide by 9
  """
  def to_celsius(fahrenheit) do
    celsius_degree = (fahrenheit.degree - 32) * 5 / 9
    %Celsius{degree: celsius_degree}
  end
end

下面是实际的运行结果

iex> fahrenheit = %Fahrenheit{degree: 45}
%Fahrenheit{degree: 45, name: "Fahrenheit", symbol: "°F"}
iex> celsius = Temperature.to_celsius(fahrenheit)
%Celsius{degree: 7.22, name: "Celsius", symbol: "°C"}
iex> kelvin = %Kelvin{degree: 300}
%Kelvin{degree: 300, name: "Kelvin", symbol: "K"}
iex> celsius = Temperature.to_celsius(kelvin)
%Celsius{degree: 26.85, name: "Celsius", symbol: "°C"}

上面的代码, 并没有实现摄氏温度模块. 你可以试着自己实现.

iex> Temperature.to_celsius(%{degree: 12})
** (Protocol.UndefinedError) protocol Temperature not implemented for %{degree: 12}
    iex:11: Temperature.impl_for!/1
    iex:15: Temperature.to_celsius/1

如果想深入了解Elixir的多态, 可以参考Elixir内置模块 String.CharEnum 的实现.

通过阅读 Enum 模块的源码, 你可以了解到, Enum 模块是如何处理不同的数据类型的.

clipboard.png

结论

多态本质上是一种运行时动态技术, 可以想象一下变量的概念, 如果编程语言中没有变量(多态, 接口), 只有常量(具体实现类), 那编程会是一种什么样的体验?

阅读 1.8k

Erlang/Elixir/Java/Javascript实践
本专栏是一个主要研究Erlang/Elixir语言的专栏. 附带其他相关的, 和不相关的东西, 目的是记录自己的学习...
1.6k 声望
257 粉丝
0 条评论
你知道吗?

1.6k 声望
257 粉丝
宣传栏