Ruby's attr_accessor, attr_reader and attr_writer


Một đối tượng Ruby có các phương thức mặc định public nếu không được khai báo phạm vi truy cập , nhưng dữ liệu của nó thì private . Vì vậy, nếu chúng ta cần truy cập dữ liệu, để đọc hoặc viết, chúng ta cần phải public nó bằng cách nào đó.
Và đó là khi attr_reader, attr_writer, và attr_accessor lên tiếng thể hiện mình.

What is attr_reader?

Hãy cùng xem một ví dụ đơn giản về định nghĩa lớp Ruby.

# person.rb
class Person
  def initialize(name)
    @name = name
  end
end

john = Person.new("John")
puts john.name # => undefined method `name'

(À quên chúng ta chưa nói đến Rails và ORM nên mọi người đừng thắc mặc tại sao trong dự án của mình vẫn chạy ngon nhé 😉 )
Chúng ta không thể lấy dữ liệu từ trường name của đối tượng Person vì nó là private. Vậy nếu chúng ta vẫn cứ muốn lấy thì chúng ta sẽ cần 1 phương thức. Như mình đã nói ở đầu nếu không được khái báo phạm vi truy cập , thì các phương thức sẽ mặc định public

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

  def name
    @name
  end
end

john = Person.new("John")
puts john.name # => John

Bằng cách định nghĩa phương thức name giừ chúng ta có thể đọc được dữ liệu, loại phương thức này được gọi là getter method
Nhưng không , Person thì phải có nhiều hơn 1 thuộc tính name chẳng hạn age, email, sex, ... Nếu cứ mỗi thuộc tính đó chúng ta lại phải định nghĩa 1 phương thức thì sẽ rất mệt . Nhưng rất may là Ruby cung cấp cho chúng ta 1 cách khác ngắn gọn hơn rất nhiều mà hiệu quả thì không khác gì đó là attr_reader

class Person
  attr_reader :name

  def initialize(name)
    @name = name
  end
end

john = Person.new("John")
puts john.name

Vậy nếu chúng ta cần lấy thêm thuộc tính chúng ta chỉ cần viết tiếp sau thuộc tính :name là có thể gọi ra bình thường

 attr_reader :fname, :lname, :age, :sex, :email

What is attr_writer?

Tiếp đến , chúng ta không muốn đọc nữa mà lại muốn thay đổi hoặc thêm mới dữ liệu. Thì như mình đã nói dữ liệu private và phương thức public . Vậy chúng ta cần các setter method

class Person
  attr_reader :name

  def initialize(name)
    @name = name
  end

  def name=(name)
    @name = name
  end
end

john = Person.new("John")
john.name = "Jim"
puts john.name # => Jim

Cúng giống vấn đề với attr_reader , nếu có nhiều thuộc tính thì rất phiền, và Ruby đã giải quyết vấn đề đó với attr_writer

class Person
  attr_reader :name
  attr_writer :name

  def initialize(name)
    @name = name
  end
end

john = Person.new("John")
john.name = "Jim"
puts john.name # => Jim

Nghe vẻ gọn rồi nhỉ, mà khoan nếu vậy code sẽ phải gõ 2 dòng code giông giống nhau như sau

class Person
  attr_reader :name, :age, :sex, :email
  attr_writer :name, :age, :sex, :email

  def initialize(name)
    @name = name
  end
end

Nhà phát triển ngôn ngữ lập trình Ruby nghe vẻ không ưng lắm về cách viết này nên attr_accessor ra đời

What is attr_accessor?

Cơ bản là khi bạn muốn cả attr_readerattr_writer của các thuộc tính chúng ta chỉ cần sử dụng attr_accessor

class Person
  attr_accessor :name, :age, :sex, :email

  def initialize(name)
    @name = name
  end
end

Define method

Và còn 1 cách khác để có thể tạo các setter , getter method tương tự như attr_accessor nhưng chỉ biết đển tham khảo thôi nhé . Đó là dùng define_method

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

  def self.define_attr(attr)
    define_method(attr) do
      instance_variable_get("@#{attr}")
    end

    define_method("#{attr}=") do |val|
      instance_variable_set("@#{attr}", val)
    end
  end
end

john = Person.new("John")
Person.define_attr(:name)
john.name = "Jim"
puts john.name # => Jim

Tài liệu tham khảo

https://mixandgo.com/learn/ruby_attr_accessor_attr_reader_attr_writer