Elixir 模块:defmodule 与 def
模块基础
在Elixir中,模块是组织和封装相关函数的主要结构。理解模块的概念对于编写清晰、可维护的代码至关重要。
创建模块使用defmodule关键字,后面跟随模块名称。模块名称通常采用PascalCase(首字母大写的驼峰命名法)。
defmodule MyModule do
# 模块内容
end
defmodule详解
基本语法
编写模块时,需要遵循Elixir的基本语法规则:
- 使用
defmodule关键字声明模块 - 确保模块名称以大写字母开头
- 放置模块内容在
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
总结
掌握defmodule和def是Elixir编程的基础。通过合理组织模块结构和函数定义,可以创建出结构清晰、易于维护的代码。
记住:
- 使用
defmodule创建模块封装相关功能 - 使用
def定义公共函数,defp定义私有函数 - 利用模块属性存储配置值
- 编写清晰的文档帮助理解代码
- 按功能组织模块,保持单一职责原则
遵循这些原则,可以构建出高质量的Elixir应用程序。

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