+2

Những điều yêu thích của tôi về ngôn ngữ lập trình ruby

1. Biến trong ruby

  • Trong Ruby một Object không thể access trực tiếp tới một biến trong class.
  • Bắt buộc phải viết Setter và Getter cho một biến nếu muốn access biến trong Ruby Class.
class Test
 @x

 def get_x
   @x
 end

 def set_x value
   @x = value
 end
end
  • Như ví dụ trên ta không thể accsess tới biến @x bằng cách Test.new.x mà chỉ có một cách duy nhất là gọi qua method get_x và gán giá trị bằng method set_x (thử code đi ạ).
  • Method attr_accessor.
class Test
  attr_accessor :x
end
  • Khi sử dụng attr_accessor ta có thể access biến x bằng cách Test.new.x, lúc này các bạn sẽ nghĩ điều này mâu thuẫn với 2 cái gạch đầu dòng bên trên vì như đã nói Object không thể trực tiếp access biến x như vậy, mà phải thông qua Getter và Setter:

Trả lời : Khi sử dụng method attr_accessor :x Ruby đã ngầm định tạo ra hai method Setter và Getter như bên dưới, vì thế khi ta gọi Test.new.x thực chất là ta gọi đến method x() để access biến x, tương tự khi gán giá trị Test.new.x = value ta sẽ gọi đến method x=()

   #Getter
     def x
       return @x
     end

    #Setter
    def x= value
      @x = value
    end

2. Đối tượng

  • Khi dùng một Object của một class để truy xuất, Ruby luôn hiểu bạn đang truy xuất vào một method
  • Trong một số ngôn ngữ khác như Java, bạn không thể có hai thành phần trùng tên trong một class (ví dụ khi đã có biến x thì bạn không thể đặt tên một method tên là "x") vì Java sẽ không biết bạn truy xuất thành phần nào khi bạn gọi tới x.
     package test
     public class A{
       public string a;
       public void a{}
     }
  • Nhưng trong Ruby mặc định là bạn đang gọi tới một Method, vì vậy có thể thoải mái đặt tên biến trùng với tên hàm mà không sợ nhầm lẫn là đang gọi cái gì trong class.
	class Test
      @@x
      def self.x
      endope
    end

Khi gọi Test.x thì Ruby sẽ hiểu ta đang access vào một method có tên là "x". Đây cũng là một lý đo để chứng minh thêm cho việc ta bắt buộc phải viết Getter và Setter cho Variable trong Ruby (Vì object chỉ có thể gọi tới method mà không thể gọi trực tiếp tới biến).

3. Scope

  • Class Scope: (Tạo ra một vùng nhớ và đặt vào vùng nhớ đó những thành phần mà bản thân con trỏ của chính class đó có thể access)
  • Object Scope: (Tạo ra một vùng nhớ và đặt vào đó những thành phần mà object của class đó có thể access)
    class test
    attr_accessor :x
    @@y = 1
    def seft.get_y
      @@y
    end

    def gety
      @@y
    end
  • Khi compile một Class như trên Ruby sẽ tạo ra 2 Scope
    • Một Class Scope mà chính con trỏ Test trỏ tới.
    • Một Object Scope mà con trỏ Test.new (object của Test) trỏ tới.
  • Trong ví dụ trên, những thành phần lằm trong Class scope bao gồm:
 # @@ is the same static variable in Java
 @@y=1
 #self define Class scope
 def self.get_y
    @@y
  end
  • Những thành phần lằm trong Object scope bao gồm:
    attr_accessor :x
    def get_y
      @@y
    end
  • Chú ý : Vùng nhớ cấp cho Class scope là một vùng nhớ chia sẻ cho tất cả các Object của nó. Vì thế khi tạo nhiều Object của class Test thì tất cả các object này đều dùng chung biến @@y.

  • @@y cũng là biến tĩnh trong java 9f1b32895f423ffd32f3d85b7e8ef85c908b497e.png

  • t1, t2, t3 đều dùng chung biến @@y vì @@y nằm trong Class Scope

Chứng minh:

    class Test
      @@y = 2

      def get_y
       @@y
      end
    end
    t1 = Test.new
    t2 = Test.new
    t3 = Test.new
    p "@@y in t1: #{t1.get_y}"
    p "@@y in t2: #{t2.get_y}"
    p "@@y in t3: #{t3.get_y}"

Kết quả:

$ @@y in t1: 2
$ @@y in t2: 2
$ @@y in t3: 2

4. Meta-programming (Writing code dynamically)

  • Chắc hẳn hầu hết chúng ta đều đã nghe nói tới khái niệm Metaprogramming. Hiểu một cách đơn giản thì
    • Metaprogramming là một kĩ thuật cho phép một ta code ra một ứng dụng mà chính ứng dụng đó quản lý chính bản thân nó.
    • Metaprogramming là một kĩ thuật cho phép viết code để quản lý code.
    • Metaprogramming viết code để sinh ra những code khác, viết code để điều khiển những code khác. Ví dụ
    class A
      METHOD_NAMES =
      METHOD_NAMES.each do |name|
        define_method(name) do |arg|
          "Called method #{name} with argument: #{arg}"
        end
      end
    end
obj = A.new
obj.abc(42)        # => "Called method abc with argument: 42"
obj.mno('arg')     # => "Called method mno with argument: arg"

Tuy ta không định nghĩa ra các hàm như 'abc', 'def', 'ghi', 'jkl', 'mno', nhưng với Metaprogramming cụ thể là việc sử dụng method define_method ta sẽ dùng code để tạo ra những hàm đó.

    METHOD_NAMES.each do |name|
      define_method(name) do |arg|
        "Called method #{name} with argument: #{arg}"
      end
    end

Please sử dụng define_method hay Metaprogramming với sự cẩn trọng, nhìn về mặt tốt thì bạn có thể thấy nó khá thú vị khi viết ít line hơn mà lại được nhiều code hơn, nhưng sẽ rất không ổn, phức tạp và khó khăn khi bạn thực hiện debug.

5. Modules

  • Có một người nói với em là "Ruby’s modules are perhaps one of the language’s most powerful features", em hiểu là : "Modules trong Ruby có lẽ chính là một trong những điểm mạnh nhất của nó"
  • Là một trường hợp đặc biệt của class (nó là một class dặc biệt)
  • Một Module là nơi chứa các method và các constants.
  • Method trong Module có thể là instance methods hoặc module methods
  • Sự khác biệt giữa Module với Class
    • Class là object
    • Module là function
  • Mục đích của module là gì
    • Module bao gồm những Method mang tính chất dùng chung (share) mà nhiều class đều cần sử dụng. Khi nghĩ tới Module có thể hiểu nó như là một nơi để share methods.
    • Đóng vai trò như một namespace dùng để gói các class liên quan lại với nhau.
    • Xử lý việc Dupplicate code, ví dụ bạn có 2 model chứa 2 scope tương tự như: scope :newest, ->{order("created_at DESC")}
    • lúc này cách xử lý là tạo ra một module chung (trong Rails ActiveSupport::Concern) và include module trong 2 model để sự dụng chung scope "newest"
  • Không thể tạo Instance của module , vì vậy có thể coi Module giống static class trong Java mọi thứ trong module đều được access qua chính tên Module

anh modulm.png Module vs Class Khác biệt giữa include và extend trong Ruby?

    module M
      def mod_method
        "Module method invoked"
      end
    end
    class A
      extend M
    end

    class B
      include M
    end

    A.mod_method        # => "Module method invoked"
    B.new.mod_method    # => "Module method invoked"
  • extend : mixes in specified module methods as class methods in the target class
    • extend : mang những method của module về làm class method trong class
  • include : mixes in specified module methods as instance methods in the target class
    • include : mang những method của module về làm instance method trong class

Tip: Chúng ta có thể include or extend một module bằng cách thứ 2

    module M
      def mod_method
        "Module method invoked"
      end
    end

    class A
    end

    class B
    end

    A.extend M
    B.include M

    A.mod_method        # => "Module method invoked"
    B.new.mod_method    # => "Module method invoked"

6. Hook Methods

What is a Hook Method?

  • Hook methods là những phương thức đặc biệt của Ruby cung cấp một cách để mở rộng hoạt động của chương trình tại thời điểm runtime.
  • Hook method giống như việc đăng kí (registry) một sự kiện và sự kiện đó sẽ được callback (fire) bởi một chỗ khác.
  • Câu hỏi:
    • Khi bạn kế thừa một class cha từ một class con vậy bên trong việc kế thừa đó chuyện gì sẽ xảy, có hàm nào được thực thi không? hay đơn giản chỉ là từ class con có thể nhìn thấy mọi thứ của class cha.
  • Trả lời:
    • Khi trình biên dịch đọc đến dòng code mà thấy có sự kết thừa thì nó sẽ callback đến một hàm của class cha, hàm đó chính là một hook method
  • Chúng ta sẽ tìm hiểu về những hook methods bên dưới:
    • inherited
    • included
    • extended
    • prepended
    • method_missing

Inherited

  • Inheritance là một khái niệm rất cơ bản và quan trọng trong object-oriented programming.
  • Cú pháp để inherite trong Ruby là kí tự < (class A < B end)
  • Chúng ta sẽ tạo ra 2 class tên là Person và User, User sẽ kế thừa class Person vậy ngoài việc instance của class User sẽ nhìn thấy các method trong class Person, có còn điều gì xảy ra không ?
  • Khi trình biên dịch đọc đến kí tự inherite "<" nó sẽ tự thực hiện việc callback tới phương thức "self.inherited" của class cha
    class Person
      def self.inherited child_class
        puts "#{child_class} inherits #{self}"
      end

      def name
        "My name is Person"
      end
    end

    class User < Person
    end
# => User inherits Person

included

  • include mang tất cả những method của module về làm 'instance method' của class.
  • Tương tự inherite từ một class ta include một module lúc này khi trình biên dịch đọc đến kí tự include thì nó sẽ callback tới method self.included của module cha

extended

  • extende mang tất cả những method của module về làm 'class method' của class.
  • Tương tự inherite từ một class ta extende một module lúc này khi trình biên dịch đọc đến kí tự extended thì nó sẽ callback tới method self.extended của module cha.
    module Person
      def self.extende base
        puts "#{base} extended #{self}"
      end
    end

    class User
      extende Person
    end

    # => User extende Person

method_missing

  • Là một hook được sử dụng rộng rãi nhất trong Ruby hook method_missing được callback khi một object gọi tới một method không tòn tại.
  • method_missing nhận đầu vào là 2 tham số :
    • tham số thứ nhất : tên của method được gọi
    • tham số thứ hai : các tham số được truyền cho method được gọi.
    class Person
      def method_missing(sym, *args)
        "#{sym} not defined on #{self}"
      end

      def name
        "My name is Person"
      end
    end
    p = Person.new
    puts p.name     # => My name is Person
    puts p.address  # => address not defined on #<Person:0x007fb2bb022fe0>

7. Super basic memoization

||=

  • a ||= b tương dương a = a || b
    • x || y => return x
    • x && y => return y

class < < self

  • Is another way of defining class methods in Ruby
    class << self
      def class_method
      end
    end

    # tương đương với
    class
      def self.class_method
      end
    end

All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí