文章目录

Elixir 模块:defmodule 与 def

发布于 2026-04-14 14:12:49 · 浏览 29 次 · 评论 0 条

Elixir 模块:defmodule 与 def

模块基础

在Elixir中,模块是组织和封装相关函数的主要结构。理解模块的概念对于编写清晰、可维护的代码至关重要。

创建模块使用defmodule关键字,后面跟随模块名称。模块名称通常采用PascalCase(首字母大写的驼峰命名法)。

defmodule MyModule do
  # 模块内容
end

defmodule详解

基本语法

编写模块时,需要遵循Elixir的基本语法规则:

  1. 使用defmodule关键字声明模块
  2. 确保模块名称以大写字母开头
  3. 放置模块内容在do...end块中
defmodule Math do
  # 模块函数将在这里定义
end

模块属性

定义模块属性使用@符号:

defmodule Config do
  @app_name "MyApp"
  @version "1.0.0"
end

访问模块属性使用@符号:

defmodule Config do
  @app_name "MyApp"

  def get_name do
    @app_name
  end
end

模块文档

添加模块文档有助于其他开发者理解模块的用途:

defmodule Math do
  """
  提供常用数学计算的模块

  ## 示例
      Math.add(2, 3) #=> 5
  """

  # 函数定义
end

函数定义(def)

声明函数使用def关键字。Elixir中的函数分为两种:公共函数和私有函数。

公共函数

定义公共函数使用def关键字,这些函数可以从模块外部调用:

defmodule Math do
  def add(a, b) do
    a + b
  end
end

调用公共函数:

Math.add(2, 3) #=> 5

私有函数

定义私有函数使用defp关键字,这些函数只能在模块内部调用:

defmodule Math do
  def add(a, b) do
    sum(a, b)
  end

  defp sum(a, b) do
    a + b
  end
end

注意:尝试从模块外部调用私有函数将导致编译错误。

函数参数

定义带参数的函数:

defmodule Greeter do
  def hello(name) do
    "Hello, #{name}!"
  end
end

使用默认参数值:

defmodule Greeter do
  def hello(name \\ "World") do
    "Hello, #{name}!"
  end
end

函数头部与守护表达式

添加守护表达式(guard)来限制函数匹配:

defmodule Math do
  def double(number) when is_integer(number) do
    number * 2
  end

  def double(number) when is_float(number) do
    number * 2.0
  end
end

函数文档

编写函数文档:

defmodule Math do
  """
  提供常用数学计算的模块
  """

  @doc """
  计算两个数的和

  ## 参数
    - a: 第一个数
    - b: 第二个数

  ## 返回值
    两数之和

  ## 示例
      iex> Math.add(2, 3)
      5
  """
  def add(a, b) do
    a + b
  end
end

模块与函数的最佳实践

1. 按功能组织模块

设计模块时,确保每个模块专注于单一功能:

# 不好的做法
defmodule Everything do
  def calculate_tax(amount) do
    # 计算逻辑
  end

  def send_email(to, subject, body) do
    # 邮件发送逻辑
  end

  def format_date(date) do
    # 格式化日期逻辑
  end
end

# 好的做法
defmodule TaxCalculator do
  def calculate_tax(amount) do
    # 计算逻辑
  end
end

defmodule EmailSender do
  def send_email(to, subject, body) do
    # 邮件发送逻辑
  end
end

defmodule DateFormatter do
  def format_date(date) do
    # 格式化日期逻辑
  end
end

2. 使用模块属性配置

使用模块属性存储配置值:

defmodule MyApp.Config do
  @api_url "https://api.example.com"
  @timeout 5000

  def get_api_url do
    @api_url
  end

  def get_timeout do
    @timeout
  end
end

3. 适当的函数可见性

使用私有函数隐藏内部实现细节:

defmodule StringUtils do
  def capitalize_words(text) do
    words = split_into_words(text)
    capitalize_all(words)
    join_words(words)
  end

  # 私有函数
  defp split_into_words(text) do
    String.split(text)
  end

  defp capitalize_all(words) do
    Enum.map(words, &String.capitalize/1)
  end

  defp join_words(words) do
    Enum.join(words, " ")
  end
end

4. 函数参数模式匹配

利用Elixir的模式匹配特性:

defmodule ListProcessor do
  def process([]) do
    "空列表"
  end

  def process([item]) do
    "单个项目: #{item}"
  end

  def process([head | tail]) do
    "头部: #{head}, 其余部分: #{length(tail)}个项目"
  end
end

实际应用示例

示例1: 用户模块

创建一个用户模块,处理用户相关操作:

defmodule User do
  @moduledoc """
  处理用户数据的模块
  """

  defstruct name: "", age: 0, email: ""

  @doc """
  创建新用户

  ## 参数
    - name: 用户名
    - age: 年龄
    - email: 电子邮件地址

  ## 返回值
    包含用户信息的映射
  """
  def new(name, age, email) do
    %User{name: name, age: age, email: email}
  end

  @doc """
  验证用户是否成年
  """
  def adult?(%User{age: age}) do
    age >= 18
  end

  defp validate_email(email) do
    String.contains?(email, "@")
  end
end

示例2: 数学工具模块

构建一个数学工具模块,提供多种数学计算功能:

defmodule MathTools do
  @moduledoc """
  提供各种数学计算的工具函数
  """

  @pi 3.14159

  @doc """
  计算圆的面积

  ## 参数
    - radius: 圆的半径

  ## 返回值
    圆的面积
  """
  def circle_area(radius) do
    @pi * :math.pow(radius, 2)
  end

  @doc """
  计算矩形的周长

  ## 参数
    - width: 宽度
    - height: 高度

  ## 返回值
    矩形的周长
  """
  def rectangle_perimeter(width, height) do
    2 * (width + height)
  end

  @doc """
  计算一组数的平均值

  ## 参数
    - numbers: 数字列表

  ## 返回值
    平均值
  """
  def average(numbers) do
    sum(Enum.map(numbers, &Float.to_float(&1))) / length(numbers)
  end

  # 私有函数:计算列表的和
  defp sum(numbers) do
    Enum.sum(numbers)
  end
end

模块行为与回调

定义行为

创建行为模块来定义接口:

defmodule Parser do
  @callback parse(String.t()) :: map()
  @callback validate(map()) :: boolean()
end

实现行为

使用defimpl实现行为:

defmodule JSONParser do
  @behaviour Parser

  def parse(json_string) do
    # 解析JSON字符串的实现
  end

  def validate(data) do
    # 验证数据格式的实现
  end
end

使用回调

利用回调函数实现模块间的交互:

defmodule DataProcessor do
  def process(parser_module, data) do
    parsed_data = parser_module.parse(data)
    if parser_module.validate(parsed_data) do
      # 处理解析后的数据
    else
      # 处理无效数据
    end
  end
end

模块编译与加载

编译模块

使用elixir命令行工具编译模块:

elixir my_module.ex

在iex中加载模块

启动iex并加载模块:

iex my_module.ex

模块依赖

管理模块间的依赖关系:

defmodule WebServer do
  @moduledoc """
  依赖于Logger模块的Web服务器实现
  """

  def start(port) do
    Logger.info("启动Web服务器,端口: #{port}")
    # 服务器启动逻辑
  end
end

总结

掌握defmoduledef是Elixir编程的基础。通过合理组织模块结构和函数定义,可以创建出结构清晰、易于维护的代码。

记住

  • 使用defmodule创建模块封装相关功能
  • 使用def定义公共函数,defp定义私有函数
  • 利用模块属性存储配置值
  • 编写清晰的文档帮助理解代码
  • 按功能组织模块,保持单一职责原则

遵循这些原则,可以构建出高质量的Elixir应用程序。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文