Elixir의 OOP 소개

이것은 누구를 위한 것입니까?



다른 많은 사람들과 마찬가지로 저는 Phoenix 프레임워크를 통해 Elixir 언어를 알게 되었고 Phoenix 외부에서 Elixir를 작성하는 데 관심을 갖게 되었습니다. 그러나 불변성과 클래스 메서드의 부족으로 인해 가장 큰 도전은 무엇이든 수행하는 방법을 알아내는 것이었습니다.

이 게시물은 OOP 프로그래밍 지식을 생산적인 Elixir 코드로 전환하는 방법에 대해 배운 몇 가지 중요한 교훈을 살펴보기 위한 것입니다. 이러한 요점의 대부분은 매우 간단하며 생각에서 "클릭"만 하면 됩니다.

기능과 데이터는 별개



대부분의 OOP 언어에서는 프로그램 작업을 수행하기 위해 클래스(객체)의 인스턴스를 사용합니다. 각 인스턴스에는 자체 포함된 상태와 해당 상태를 참조하여 작업을 수행할 수 있는 함수가 있습니다. 예를 들어 간단한 Ruby 클래스는 다음과 같습니다.

class Person
   def initialize(name)
      @cust_name = name
   end

   def name_backwards
      @cust_name.reverse
   end
end

person_1 = Person.new('Ted')
person_1.name_backwards
# outputs "deT"

Elixir 함수는 this , @ 또는 다른 인스턴스 변수에 대한 참조를 가질 수 없습니다. 따라서 Elixir에서 유사한 작업을 수행하는 방법은 함수가 데이터(인스턴스)를 첫 번째 매개변수로 사용하도록 하는 것입니다. 다음은 Elixir로 재작성한 위의 Ruby 코드입니다.

person = %{name: "Ted"}

defmodule Person.Functions do
  def name_backwards(person) do
    String.reverse(person.name)
  end
end

Person.Functions.name_backwards(person)

위의 코드에서 개인 인스턴스의 데이터가 Person.Functions 모듈 외부에 완전히 보관되어 있음을 알 수 있습니다. person 인스턴스에서 함수를 사용하려면 person 인스턴스를 첫 번째 매개 변수로 사용하는 함수에 전달합니다.

실제로 모듈은 이름이 지정되지 않습니다Person.Functions. 모듈은 정의에 따라 개인 인스턴스에 연결되어 있지 않기 때문입니다. name_backwards 함수는 이름 필드가 있는 모든 개체에 사용할 수 있습니다.

교체로 업데이트



OOP에서 가장 일반적인 작업 중 하나는 개체의 인스턴스를 사용하여 일부 상태를 추적하는 것입니다. 예를 들어 루비에서

class Person
   def initialize(age)
      @person_age = age
   end

   def add_year
      @person_age = @person_age + 1
   end

   def get_age
      @person_age
   end
end

person_1 = Person.new(30)
person_1.add_year
person_1.add_year
person_1.get_age
# outputs 32

Elixir에서 모든 객체는 변경할 수 없습니다. 그렇다면 도대체 어떻게 이 사람 인스턴스의 필드를 업데이트할 수 있을까요? 새 사람 인스턴스를 만들고 이전 인스턴스를 교체하여 수행할 수 있습니다.

person = %{age: 30}

defmodule Person.Functions do
  def add_year(person) do
    new_age = person.age + 1
    %{age: new_age}
  end

  def get_age(person) do
    person.age
  end
end

person = Person.Functions.add_year(person)
person = Person.Functions.add_year(person)
Person.Functions.get_age(person)

따라서 인스턴스 내의 필드 값을 변경하는 대신 %{age: new_age} 줄에 새 인스턴스를 반환합니다.

전역 개체는 프로세스에 저장됩니다.



전역 상태의 사용을 옹호하는 것은 아니지만 이 아이디어가 Elixir의 기초를 배우는 데 근본적으로 중요하다고 생각합니다. 또한 이것은 인위적인 예일 수 있지만 OOP를 Elixir로 번역하는 도전을 보여줄 것이라고 생각합니다.

티켓을 분배하는 티켓 클래스를 추가했습니다. 각 사람이 고유한 티켓을 얻을 수 있도록 각 티켓이 나가는 대로 추적합니다. 정적 필드@@ticket_count를 사용하여 티켓을 추적할 수 있으므로 Ruby에서는 매우 간단합니다.

class Tickets
  @@ticket_count = 1
  def self.get_ticket
    next_ticket = @@ticket_count
    @@ticket_count = @@ticket_count + 1
    next_ticket
  end
end

class Person
   def initialize()
      @ticket_num = -1
   end

   def get_ticket
    @ticket_num = Tickets.get_ticket
    @ticket_num
   end
end

person_1 = Person.new()
puts person_1.get_ticket
#outputs 1
person_2 = Person.new()
puts person_2.get_ticket
#outputs 2

그러면 Elixir에서 티켓 수를 어떻게 추적할 수 있을까요? GenServer라는 것을 사용합니다.

defmodule Tickets do
  use GenServer

  def start_link(start_ticket_num) do
    GenServer.start_link(Tickets, start_ticket_num, name: Tickets)
  end

  def get_ticket() do
    GenServer.call Tickets, :get_ticket
  end

  def handle_call(:get_ticket, _from, curr_ticket_num) do
    {:reply, curr_ticket_num, curr_ticket_num + 1}
  end
end

defmodule Person.Functions do
  def get_ticket(person) do
    next_ticket = Tickets.get_ticket()
    %{ticket_num: next_ticket}
  end
end

Tickets.start_link(1)
person_1 = %{ticket_num: -1}
IO.inspect Person.Functions.get_ticket(person_1)
#output %{ticket_num: 1}
person_2 = %{ticket_num: -1}
IO.inspect Person.Functions.get_ticket(person_2)
# %{ticket_num: 2}

이제 이것은 점점 더 발전하고 있지만 Elixir의 진정한 아름다움입니다. start_link(1) 함수에서 초기 값이 1인 Tickets라는 이름의 GenServer 인스턴스로 다른 프로세스를 시작했습니다.

GenServer는 많은 일을 할 수 있지만 여기서는 현재 티켓 번호를 저장하는 데 사용합니다. get_ticket를 호출할 때마다 코드는 기본적으로 {:reply, curr_ticket_num, curr_ticket_num + 1} 형식의 GenServer에 대한 주문인 응답{command_to_genserver, response_value, new_state_value}으로 흐릅니다. 이제 모든 Elixir 모듈은 Tickets.get_ticket 함수를 호출하기만 하면 티켓을 얻을 수 있습니다.

대부분의 OOP 언어에서 다른 프로세스를 시작하는 것은 매우 높은 수준의 작업입니다. 실제로 개발자는 몇 년 동안 기본 프로세스에서만 작업할 수 있습니다. 그러나 Elixir에서는 다른 프로세스와 함께 작업하는 것이 기본 기술입니다.

GenServers에 대해 자세히 알고 싶다면 직접 작성하는 것이 좋습니다! 여기는 tutorial

마무리



이것이 Elixir 학습의 어려움에 대해 돌이켜 생각했을 때 처음으로 떠오른 몇 가지 사항이었습니다. 그들 중 하나가 당신에게 도움이 되었기를 바랍니다.

Elixir의 코딩은 재미있습니다!

좋은 웹페이지 즐겨찾기