Object-Oriented Programming in Elixir

From Elixir Wiki
Jump to navigation Jump to search

Object-Oriented Programming in Elixir[edit]

File:Elixir logo.png
Elixir Programming Language Logo

Object-Oriented Programming (OOP) is a popular programming paradigm used in many languages, allowing developers to model their code around objects that encapsulate data and behavior. While Elixir is primarily a functional programming language, it provides certain features and concepts that can be used to implement object-oriented programming principles.

Module-Based Design[edit]

One of the key aspects of object-oriented programming is encapsulation, where data and behavior are grouped together in objects. In Elixir, modules serve a similar purpose, allowing you to define functions and data structures together in a single unit. Modules can be used to create objects and instances by defining functions as methods and using module attributes to store the data.

For example, consider the following module definition:

```elixir defmodule Person do

 @name "John"
 
 def greet(), do: IO.puts("Hello, #{@name}!")

end ```

In this case, the `Person` module can be seen as an object and the `greet/0` function as a method. The module attribute `@name` serves as the encapsulated data. You can create an instance of `Person` and call methods on it as follows:

```elixir person = Person person.greet() ```

Protocols for Polymorphism[edit]

Polymorphism is a key principle of object-oriented programming that allows objects of different classes to be treated as if they were the same type. In Elixir, polymorphism can be achieved using protocols, a feature that allows you to define a set of functions for a data type.

For example, let's define a protocol called `Eatable` that specifies a single function `eat/1`:

```elixir defprotocol Eatable do

 def eat(food)

end ```

Now, we can implement the `Eatable` protocol for different data types, such as fruits and vegetables:

```elixir defimpl Eatable, for: String do

 def eat(food) do
   IO.puts("Eating #{food} as a string.")
 end

end

defimpl Eatable, for: List do

 def eat(food) do
   IO.puts("Eating #{inspect(food)} as a list.")
 end

end ```

You can then invoke the `eat/1` function on different data types, and the appropriate implementation will be used:

```elixir "Elixir" |> Eatable.eat() [1, 2, 3] |> Eatable.eat() ```

Inheritance with Protocols and Behaviours[edit]

Inheritance allows classes to inherit and extend the behavior of other classes. In Elixir, you can achieve a similar effect using protocols and behaviours. A behavior is a predefined set of functions that a module must implement.

For example, let's define a behaviour called `Shape` that requires modules implementing it to define two functions, `area/1` and `perimeter/1`:

```elixir defmodule Shape do

 @callback area(shape)
 @callback perimeter(shape)

end ```

Now, we can define a `Rectangle` module that implements the `Shape` behavior:

```elixir defmodule Rectangle do

 @behaviour Shape
 def area({length, width}), do: length * width
 def perimeter({length, width}), do: 2 * (length + width)

end ```

The `Rectangle` module can be seen as inheriting the behavior of `Shape`. Other modules can also implement the `Shape` behaviour and provide their own logic for calculating area and perimeter, effectively allowing for polymorphism with inheritance.

Conclusion[edit]

While Elixir is primarily a functional programming language, you can still apply object-oriented programming principles using its features. By leveraging modules, protocols, and behaviours, you can encapsulate data and behavior, achieve polymorphism, and create reusable code. Understanding and using these concepts in Elixir can help you write clean, maintainable, and modular code.