12/08/2018, 15:02

Ruby! những điều tạo nên sự khác biệt

Đó là một câu nói ngắn gọn mà mình có thể nói về Ruby. Một ngôn ngữ rất rất tự nhiên, nó khiến người lập trình viên cảm thấy thoải mái khi sử dụng nó. Mặc giù chỉ mới tiếp cận Ruby nhưng mình thấy rất có hứng thú với nó. Và ở bài viết này mình muốn giới thiệu với các bạn về những điều mình thấy ...

Đó là một câu nói ngắn gọn mà mình có thể nói về Ruby. Một ngôn ngữ rất rất tự nhiên, nó khiến người lập trình viên cảm thấy thoải mái khi sử dụng nó. Mặc giù chỉ mới tiếp cận Ruby nhưng mình thấy rất có hứng thú với nó. Và ở bài viết này mình muốn giới thiệu với các bạn về những điều mình thấy hay, thấy thú vị ở Ruby, những đoạn code tạo nên sự khác biệt đối với những ngôn ngữ khác.

1.1 Map

Sử dụng map trong Ruby sẽ dơn giản hóa đoạn code của bạn, và có thể làm được nhiều điều mà bạn muốn. Map "trả về một mảng mới với kết qủa là việc chạy kết quả mỗi phần tử trong enum". Cấu trúc như sau

    an_array.map { |element| element * element }

Thật là đơn giản phải không! Những khi bạn mới bắt đầu với Ruby, sẽ dễ dàng hơn nếu bạn sử dụng each. Cấu trúc sử dụng each như sau:

user_ids = []
users.each { |user| user_ids << user.id }

đoạn code trên sẽ đơn giản nếu sử dụng map:

user_ids = users.map { |user| user.id }

Và còn tốt hơn nữa (nhanh hơn nữa):

user_ids = users.map(&:id)

1.2 Select

Khi bạn sử dụng code với map, có lúc nào đó code bạn có thể trông như thế này:

even_numbers = [1, 2, 3, 4, 5].map { |element| element if element.even? } # [ni, 2, nil, 4, nil]
even_numbers = even_numbers.compact # [2, 4]

Sử dụng map thì mảng sẽ lấy chứa các số chẵn còn lại sẽ trả về nil. Tuy nhiên khi bạn sử dụng phương thức compact bạn có thể xóa tất cả phần tử nil ra khỏi mảng. Như vậy bạn có thể lấy ra được tất cả các số chẵn. Và giờ chúng ta có thể làm tốt hơn bằng việc sử dụng select

[1, 2, 3, 4, 5].select { |element| element.even? }

Chỉ bằng một dòng code mà có thể giải quyết được bài toán, rất ngắn gọn và dễ hiểu phải không?

Bonus:

[1, 2, 3, 4, 5].select(&:even?)

1.3 Sample

Hãy tưởng tượng, khi bạn muốn lấy ra một giá trị random trong mảng và bạn chỉ mới bắt đầu học Ruby thì hãy luôn nghĩ đến phương thức random, điều đó có nghĩa là:

[1, 2, 3][rand(3)]

đoạn code trên khá là ngắn gọn và dễ hiểu, tuy nhiên mình không nghĩ đây là cách tốt nhất để lấy ra giá trị random. Và mình sẽ hướng dẫn bạn bằng việc sử dụng phương thức shuffle

[1, 2, 3].shuffle.first

Mình thực sự hay dùng shuffle để thay thế cho rand. Tuy nhiên trong thời gian làm dự án vừa rồi, mình còn phát hiện ra phương thức hay hơn nữa là sử dụng sample.

[1, 2, 3].sample

Tại sao lại là sample, vì mình nghĩ khá là tự nhiên và trực quan. Lý do đơn giản vậy thôi!!!

Như mình đã đề cập từ trước, mình thấy thích và thoải mái với những dòng code của Ruby. Ở phần này mình sẽ chỉ cho các bạn thấy cú pháp đẹp đẽ mà Ruby mang lại.

2.1 Return? or Not?

Như các bạn đã biết khi dùng return thì sẽ trả về một giá trị nào đó và trong Ruby cũng vậy. Ví dụ dễ thấy nhất là phương thức getter, chúng ta sẽ khởi tạo nó và trả về cho nó một giá trị nào đó.

def get_user_ids(users)
  return users.map(&:id)
end

Tuy nhiên trong Ruby, khi gọi 1 phương thức thì nó luôn luôn trả về một giá trị, vậy tại sao lại phải cần "return" nữa nhỉ, xóa nó đi thôi.

def get_user_ids(users)
  users.map(&:id)
end

Và sau một số dự án trải qua, mình thấy không sử dụng return có vẻ như làm code trong sáng hơn và cũng giúp mình thoải mái hơn trong việc code

2.2 Multiple Assignments

Ruby cũng có thể cho phép gán giá trị cùng một thời điểm. Khi mình cũng như các bạn mới bắt đầu với Ruby, thì code có vẻ như thế này:

def values
  [1, 2, 3]
end

one   = values[0]
two   = values[1]
three = values[2]

Tuy nhiên mình sẽ có cách viết hay hơn, ngắn gọn hơn.

def values
  [1, 2, 3]
end

one, two, three = values

2.3 Phương thức đưa ra câu hỏi

Đây chính là một trong những phương thức gây ấn tượng nhất với mình. Mình có thể sử dụng dấu ? trong các phương thức. Khi vừa từ Java chuyển qua Ruby mình thực sự bất ngờ với nó. Bạn có 1 đoạn code như sau:

Framgia.awesome # => true

Nó khá là ổn đúng không? nhưng hãy sử dụng dấu hỏi cho phương thức đó

Framgia.awesome?  # => true

Bằng cách này sẽ giúp người đọc dễ hình dung được phương thức mà bạn muốn gửi gắm đến. Nó kiểu như là một dạng câu hỏi true/false

một phương thức khác mà mình thường sử dụng là phương thức any?. Nó sẽ kiểm tra xem giá trị đó có tồn tại hay không. Ví dụ:

[].any? # => false
[1, 2, 3].any? # => true

2.4 IF statement

Về cơ bản thì cách dùng if/else khá giống với các ngôn ngữ khác, tuy nhiên ngoài cách viết cũ, thì mình khá là thích với cách viết như sau:

def hey_ho?
  true
end

puts "let’s go" if hey_ho?

Nó khiến code trông ngắn gọn hơn, người đọc sẽ cảm giác như đang đọc một đoạn văn hơn là một đoạn code.

2.4 Try

Trong Ruby, try() giúp bạn gọi các method của 1 object mà không cần lo lắng về việc object đó có phải là nil hay không và việc gây ra các exception không mong muốn. Chúng ta rất hay quên việc kiểm tra trường hợp nil nên phương thức try() này rất hữu ích trong việc giải quyết vấn đề này. Ví dụ sử dụng if/unless:

user.id unless user.nil?

Sử dụng phương thức try:

user.try(:id)

2.5 Double Pipe Equals (||=) / Memoization

Đây là một đặc tính rất cool của Ruby. nó giống như là lưu trữ giá trị trong bộ nhớ đệm

some_variable ||= 10
puts some_variable # => 10

some_variable ||= 99
puts some_variable # => 10

Bạn sẽ không cần phải sử dụng if, chỉ cần sử dụng ||=. Rất đơn giản và dễ sử dụng.

2.6 Class Static Method

Một trong những điều khiến Ruby thú vị đó chính là "static method"

GetSearchResult.call(params)

Rất đơn giản, đẹp và trực quan. Vậy để có được nó thì mình phải cài đặt như thế nào?

class GetSearchResult
  def self.call(params)
    new(params).call
  end

  def initialize(params)
    @params = params
  end

  def call
    # ... code gì thì code đi ...
  end
end

phương thức "self.call" sẽ khởi tạo một thể hiện của đối tượng đó, và đối tượng đó sẽ chỉ cần gọi đến phương thức "call". Đây chính là một design pattern, hãy sử dụng nó vì nó thực sự cần thiết cho việc code và maintain

2.7 Getters & Setters

Cũng giống như class GetSearchResult, nếu bạn muốn sử dụng params, bạn có thể dùng @params

class GetSearchResult
  def self.call(params)
    new(params).call
  end

  def initialize(params)
    @params = params
  end

  def call
    # ... code gì thì code đi ...
    @params # do something with @params
  end
end

Chúng ta sẽ định nghĩa setter/ getter cho biến đó

class GetSearchResult
  def self.call(params)
    new(params).call
  end

  def initialize(params)
    @params = params
  end

  def call
    # ... code gì thì code đi ...
    params # do something 
  end

  private

  def params
    @params
  end

  def params=(parameters)
    @params = parameters
  end
end

Hoặc chúng ta cũng có thể định nghĩa các attr_reader, attr_writer or attr_accessor

class GetSearchResult
  attr_reader :param

  def self.call(params)
    new(params).call
  end

  def initialize(params)
    @params = params
  end

  def call
    # ...code gì thì code đi ...
    params # do something 
  end
end

Mình đã chỉ một số điều cơ bản, một số điều thú vị mà Ruby đem lại:

  • Làm thế nào để sử dụng mảng trong Ruby
  • Làm thế nào để sử dụng cú pháp hợp lí trong Ruby
  • Làm thế nào để biến những dòng code trở nên trong sáng, đẹp đẽ Mình hi vọng bài viết này sẽ giúp những người sẽ học, đang học có một cách nhìn tổng quan nhất về những điều tốt đẹp mà Ruby mang lại. Tuy nhiên không phải có gì là quá cầu toàn, có lợi ắt sinh ra bất lợi. Giù sao ngôn ngữ cũng chỉ là công cụ, điều quan trọng khi code hãy suy nghĩ thật trong sáng, thật có tâm để vừa giúp leader review code dễ hơn, vừa giúp mình nâng lên một tầm cao mới.
0