Sự khác nhau của phương thức public, private và protected trong Ruby

1. So sánh phương thức public, private và protected

public, protected và private là gì?

  • public, protectedprivate đề cập đến khả năng truy cập của các phương thức.
  • Mặc định, tất cả phương thức đều ở trạng thái public. Nếu không chỉ định khả năng truy cập của phương thức, nó sẽ là public.
  • Phương thức protected và private không thể truy cập một cách tự do, và do đó khi có một thể hiện của đối tượng, bạn sẽ không thể gọi được các phương thức đó.
  • Phương thức protected và private cũng có sự khác biết xung quanh cách bạn có thể sử dụng chúng trong ngữ cảnh của đối tượng.

Việc phân biệt ba phương thức trên khá quan trọng đối với một developer do:

  • Phần lớn của việc tạo ra các đối tượng đó là thiết kế giao diện. Khi nói về giao diện của một đối tượng, điều này nói đến các phương thức có khả năng truy cập public (có thể gọi ở mọi nơi).
  • Một phần của việc này là viết các phương thức với các tên phù hợp, có hành động rõ ràng. Nếu không biết điều gì xảy ra khi gọi phương thức, thì đó là dấu hiệu của việc thiết kế chưa được tốt.
  • Một phần khác của việc thiết kế giao diện đối tượng tốt là việc tách biệt các phương thức public, protected và private.
  • Các phương thức public thường phản ánh về trách nhiệm, hành vi và vai trò của nó trong ứng dụng.
  • Phương thức private và protected là quan trọng cho các hành động thực hiện nội bộ của đối tượng mà bên ngoài không nhìn thấy.

Qua việc so sánh trên, các bạn có lẽ đang thắc mắc rằng phương thức public, protected và private thực tế nó là cái gì và định nghĩa, cũng như sử dụng nó ra sao? Tiếp theo, tôi sẽ giúp các bạn hiểu về chúng một cách rõ ràng hơn.

2. Phương thức public

class Product
  attr_accessor :name
 
  def initialize(name)
    @name = name
  end
end

Một ví dụ đơn giản về lớp Product với biến name trong hàm khởi tạo. Sử dụng phương thức attr_accessor để tự động tạo các phương thức getter và setter cho thuộc tính name. Khi chúng ta gọi phương thức name trên đối tượng, chúng ta sẽ được kết quả trả về là tên của sản phẩm:

iphone = Product.new("Iphone")
=> #<Product:0x007fd7a182f070 @name="Iphone">
iphone.name
=> "Iphone"

Hãy nhớ rằng, khi chúng ta sử dụng attr_accessor, điều đó có nghĩa Ruby chỉ đơn giản là tự động tạo ra các phương thức sau đây:

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

Đây là một phương thức public và bạn có thể gọi nó từ bên ngoài phạm vi của đối tượng. Các phương thức public mô tả hành vi của đối tượng và nên cho phép các đối tượng khác gửi thông điệp đó.

Ví dụ tiếp theo, sản phẩm có thuộc tính số lượng. Để tăng số lượng của sản phẩm, có thể thêm phương thức increment:

class Product
  attr_accessor :name, :quantity
 
  def initialize(name)
    @name = name
    @quantity = 1
  end
 
  def increment
    @quantity += 1
  end
end

Chúng ta có thể gọi phương thức increment như sau:

iphone = Product.new("Iphone")
=> #<Product:0x007fd7a231e648 @name="Iphone", @quantity=1>
 
iphone.quantity
=> 1
 
iphone.increment
=> 2
 
iphone.quantity
=> 2

Như bạn thấy, tăng số lượng của sản phẩm là một hành vi quan trọng đối với đối tượng và do đó, nó phản ánh trực tiếp được khả năng truy cập của phương thức public.

3. Phương thức private

Để định nghĩa một phương thức private chúng ta sử dụng từ khóa private. Private không thực sự là một từ khóa, nó là một phương thức, nhưng đối với tất mục đích và ý muốn, thì việc nghĩ nó là một từ khóa sẽ đơn giản hơn. Hãy nhìn vào các phương thức trong lớp Product:

class Product
  attr_accessor :name, :quantity
 
  def initialize(name)
    @name = name
    @quantity = 1
  end
 
  def increment
    @quantity += 1
  end
 
  private
    def stock_count
      100
    end
end

Để biểu diễn phương thức stock_count là private, chúng ta có thể đặt nó dưới tiêu đề private. Bây giờ chúng ta sẽ có một thể hiện của đối tượng Product, chúng ta không thể gọi phương thước stock_count vì nó là private:

milk = Product.new("Milk")
=> #<Product:0x007fd7a22b53f0 @name="Milk", @quantity=1>
 
milk.stock_count
NoMethodError: private method 'stock_count' called for #<Product:0x007fd7a22b53f0 @name="Milk", @quantity=1>

Để gọi được phương thức stock_count, chúng ta cần phải đặt nó nằm trong phạm vi của đối tượng:

class Product
  attr_accessor :name, :quantity
 
  def initialize(name)
    @name = name
    @quantity = 1
 
    puts "There are #{stock_count} in stock"
  end
 
  def increment
    @quantity += 1
  end
 
  private
    def stock_count
      100
    end
end

Bây giờ, chúng ta khởi tạo một thể hiện mới của đối tượng, chúng ta sẽ thấy stock count:

milk = Product.new("Milk")
There are 100 in stock
=> #<Product:0x007fd7a22909d8 @name="Milk", @quantity=1>

Vì vậy, cách duy nhất để gọi phương thức private là thực hiện điều đó trong thể hiện của đối tượng. Tuy nhiên, một điều thú vị cần lưu ý với phương thức private trong Ruby là thực tế, một phương thức private không thể được gọi với một bộ phận tường mình, ngay cả khi là chính nó. Cùng xem ví dụ:

class Product
  attr_accessor :name, :quantity
 
  def initialize(name)
    @name = name
    @quantity = 1
 
    puts "There are #{self.stock_count} in stock"
  end
 
  private
    def stock_count
      100
     end
end

Trong ví dụ này, tôi đã sửa đổi phương thức khởi tạo để sử dụng self.stock_count. Trong trường hợp này, self là đối tượng hiện tại. Tuy nhiên, khi cố gắng tạo ra một thể hiện mới của Product, điều nhận được là lỗi:

milk = Product.new("milk")
NoMethodError: private method `stock_count’ called for #<Product:0x007fd7a2279f08 @name="milk", @quantity=1>

Vì vậy, chúng ta chỉ có thể gọi các phương thức private từ ngữ cảnh hiện tại của đối tượng và không thể gọi chúng với người nhận, ngay cả khi người nhận đó là self.

4. Phương thức protected

Phương thức protected là phương thức được bảo vệ không thể truy cập từ bên ngoài ngữ cảnh của đối tượng, nhưng có thể truy cập từ bên trong ngữ cảnh của đối tượng cùng loại khác. Hãy cùng xem ví dụ:

class Product
  attr_accessor :name, :quantity
 
  def initialize(name)
    @name = name
    @quantity = 1
 
    puts "The SKU is #{sku}"
  end
 
  protected
    def sku
      name.crypt("yo")
    end
end

Trong ví dụ, chúng ta sẽ tạo ra một SKU từ tên của sản phẩm. Giống như ví dụ về phương thức private ở trên, phương thức này không thể truy cập bên ngoài ngữ cảnh của đối tượng:

milk = Product.new("Milk")
The SKU is yo.B6xygWtQ1w
=> #<Product:0x007fd7a2184058 @name="Milk", @quantity=1>
 
milk.sku
NoMethodError: protected method 'sku' called for #<Product:0x007fd7a2184058 @name="Milk", @quantity=1>

Tuy nhiên, nếu bạn gọi phương thức với self nó sẽ hoạt động:

class Product
  attr_accessor :name, :quantity
 
  def initialize(name)
    @name = name
    @quantity = 1
 
    puts "The SKU is #{self.sku}"
  end
 
  protected
    def sku
      name.crypt("yo")
    end
end

Kết quả như sau:

irb(main):016:0> phan = Product.new("Phan")
The SKU is yo0ob53Cq5rp.
=> #<Product:0x00000000ed65c0 @name="Phan", @quantity=1>

Trên đây mình đã trình bày cơ bản về khác biệt giữa ba phương thức là public, private và protected, mong rằng nó sẽ giúp cho các bạn hiểu được chúng để sử dụng một cách chính xác nhất.


All Rights Reserved