多态性:Elixir与Java

Introduction

这篇文章试图通过将Elixir协议与Java中的类层次结构进行比较来增强对Elixir协议的理解。 尽管两种语言有很大不同,但是它们具有一个共同的强大功能:多态。

当您第一次开始学习Elixir时,人们首先要做的就是将您的头转向功能编程(FP)。 这可能意味着要摆脱一些旧习惯,这些习惯是由使用传统的面向对象的语言(例如Java和Python)形成的,并且在编写代码和设计软件时思路有所不同。

尽管每个Elixir开发人员都会告诉您FP有多么出色以及他们为什么喜欢它,但是对于初学者Elixir开发人员而言鲜为人知的功能是它通过协议对多态性的支持。

多态是一个概念,它是面向对象编程的核心,使其成为与Elixir语言的核心相反的功能。 我们将了解它为何有用,并将它们与Java中熟悉的基础功能进行比较。

Who is this article for?

This article introduces the use of protocols in Elixir to achieve polymorphic functionality. If you've never heard of Elixir before, be sure to check out the Elixir website for more details on this exciting young language.

如果您完全熟悉Java中的面向对象编程(OOP),那么这篇文章将有助于您得出Java类层次结构与Elixir协议之间的相似之处。

What is polymorphism?

多态是指程序根据给定数据的类型或类调度到函数或方法。

例如,在Java中,我们可以定义仅包含方法的接口定义概述了一些功能。 然后,我们提供实作 for an interface's method 定义 when we define a class which 实施该界面。

如果方法的签名接受接口的实例并在该实例上调用方法,则在运行时,Java会确定哪个方法定义根据基础派发实作类。

这使我们可以通过不将一个模块的实现与另一个模块的实现联系起来,从而使代码解耦。

Java class hierarchies

让我们在这里使用一个简单的例子。 美国人以英寸为单位测量身高,而世界其他地方则以厘米为单位。 我确定世界上的某些地区会使用其他度量单位,但为简单起见,我们将坚持这一假设。

让我们定义一个界面,以厘米或英寸为单位来测量美国人或世界其他地方的人:

public interface MeasuredPerson {
  double measureInInches();
  double measureInCentimetres();
}

现在,我们可以在代表美国和世界其他地区的人们的几个类中实现此接口:

public class American implements MeasuredPerson {
  private String name;
  private double heightInInches;

  public American(String name, double heightInInches) {
    this.name = name;
    this.heightInInches = heightInInches;
  }

  public double measureInInches() { return heightInInches; }
  public double measureInCentimetres() { return heightInInches * 2.54; }
}

public class RestOfTheWorld implements MeasuredPerson {
  private String name;
  private double heightInCentimetres;

  public American(String name, double heightInCentimetres) {
    this.name = name;
    this.heightInCentimetres = heightInCentimetres;
  }

  public double measureInInches() { return heightInCentimetres * 0.39; }
  public double measureInCentimetres() { return heightInCentimetres; }
}

现在我们可以使用几个类,我们可以编写一个接受被测人并以英寸为单位打印出它们的高度。

public class Main {
  public static void main(string[] args) {
    American american = new American("John", 72);
    RestOfTheWorld restOfTheWorld = new RestOfTheWorld("Jean", 178);

    printInches(american);
    printInches(restOfTheWorld);
  }

  private void printInches(MeasuredPerson measuredPerson) {
    System.out.println("The person's height is: " + measuredPerson.measureInInches());
  }
}

该代码将打印出美国人和其他人的身高(英寸)。 方法printInches将呼叫分派给measureInInches在运行时基于给定对象的基础类型!

Elixir protocols

Elixir中的协议允许我们在程序中定义类似的关系。 首先,我们定义一个协议,它是一组功能定义概述了一些功能。 然后,我们提供实作 for a protocol's function 定义 when we define an implementation module for an Elixir type.

lix剂类型包括清单,地图,and 关键词,but they can also include custom structs that we define. The following is an example of two Person structs,similar to our Java classes which we defined above:

defmodule Person.American do
  defstruct [:name, :height_in_inches]
end

defmodule Person.RestOfTheWorld do
  defstruct [:name, :height_in_centimetres]
end

美国人以英寸为单位测量身高,世界其他地方以厘米为单位测量身高。 如果我们想测量一个人而又不必担心他们是来自美国还是世界其他地方,该怎么办?

The defprotocol keyword

与我们在Java中定义接口的方式类似,我们可以定义用于测量人的身高的协议。 该协议期望用户想要与给定人员一起测量的测量单位。

defprotocol Person.Height do
  @doc """
  Accepts the person to measure as the first argument, and accepts either
  `:inches` or `:centimetres` as the second argument to indicate 
  which unit of measurement to return.
  """
  def measure(person, unit_of_measurement)
end

请注意,我们没有提供做阻止我们措施/ 2功能。 下一部分将显示我们如何为新协议定义实现。

The defimpl keyword

以下模块使用定义为我们的自定义人员结构提供实现的关键字:

defimpl Person.Height, for: Person.American do
  def measure(%Person.American{height_in_inches: height_in_inches}, :inches) do
    height_in_inches
  end

  def measure(%Person.American{height_in_inches: height_in_inches}, :centimetres) do
    height_in_inches * 2.54
  end
end

defimpl Person.Height, for: Person.RestOfTheWorld do
  def measure(%Person.RestOfTheWorld{height_in_centimetres: height_in_centimetres}, :inches) do
    height_in_centimetres * 0.39
  end

  def measure(%Person.RestOfTheWorld{height_in_centimetres: height_in_centimetres}, :centimetres) do
    height_in_centimetres
  end
end

定义采用协议模块(使用协议),以及映射到我们要为其提供实现的类型的模块。 我们的第一个论点措施/ 2因此,在我们的实现模块中定义的函数就是这种类型的结构。

与我们的Java实现有一个关键的区别:与其在美国人和世界的人模块,我们在单独的模块中实现行为。 由于结构本身不能具有行为(即您不能调用american.measure(:centtimetres)),则必须将结构传递给函数; 有关如何执行此操作的信息,请参见下文。

Tying it all together

现在,我们为我们定义了几个实现模块美国人和世界的人结构,我们可以称之为Person.Height.measure / 2功能以我们首选的度量单位来度量给定的人!

%Person.American{name: "John", height_in_inches: 72}
|> Person.Height.measure(:centimetres)
|> IO.inspect()
# 182.88

%Person.RestOfTheWorld{name: "Jean", height_in_centimetres: 178}
|> Person.Height.measure(:inches)
|> IO.inspect()
# 70.0787

The power of protocols

我们在这里使用了一个简单的示例来展示如何创建自己的协议。 但是有什么大不了的?

使用协议功能强大,因为我们可以定义行为明确的单个接口一旦,然后根据我们的自定义结构在其他地方实现该行为。

与Java类层次结构相比,Elixir协议具有的一大优势是我们可以为外部库中的结构提供我们自己的协议的实现。 在Java中,您必须编辑其他库的源代码才能实现您的自定义界面。 但是在Elixir中,我们所要做的就是为类型(结构)提供实现,而不管是什么库/框架提供了该类型!

A familiar example

In Elixir, we use the Enum module for traversing lists and maps. Anything we pass as the first argument to the functions in the Enum module must have an implementation for the Enumerable protocol! To read more about this check out the Enumerable docs.

Resources

我想提供一些其他资源来学习Elixir协议,因此您可以继续使用传统的功能语言来探索此OOP概念。

  • Elixir protocols: the introduction to Elixir protocols on the official Elixir lang website.
  • Elixir Protocol docs: if you're looking for a deeper dive into how protocols work, this is a good place to start. The Elixir docs are written so well that usually I don't look elsewhere.
  • Programming Elixir >= 1.6: this book is one of the best tools in my arsenal. I couldn't recommend a better book for beginners and experts alike. I use it as reference all of the time.

我为那些熟悉OOP功能的人写了这篇文章,并展示了Elixir的出色功能,对于熟悉Java OOP的人来说,这就像在家一样。 谢谢阅读!

from: https://dev.to//hugecoderguy/polymorphism-elixir-vs-java-14i0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值