Skip to main content

Follow Law of Demeter!

Việc code bẩn code rác là khá phổ biến đối với lập trình viên, mà để thay đổi nó chúng ta cần phải có thời gian luyện tập, mình sẽ thông qua series các bài viết chia sẻ kinh nghiệm việc mình luyện tập và apply việc refactor code làm cho code đẹp hơn. (Trong series này mình sẽ sử dụng ngôn ngữ Ruby để làm ví dụ)
Ở phần 1 này mình sẽ đi vào nguyên lý đầu tiên Follow Law of Demeter.
Để hiểu xem cụ thể Low of Demeter (LoD) là gì chúng ta sẽ đi vào một ví dụ cụ thể. Giả sử chúng ta có 3 class là Address, Customer và Order
class Address < ActiveRecord::Base
    belongs_to: customer
end

class Customer < ActiveRecord::Base
    has_one: address
    has_many: orders
end

class Order < ActiveRecord::Base
    belongs_to: customer
end
Vì Ruby cho phép chúng ta thông qua quan hệ của các đối tượng để truy cập đến các thuộc tính và phương thức của những đối tượng liên quan. Để hiển thị địa chỉ của khách hàng cho 1 đơn hàng, chúng ta thông thường sẽ xử lý như sau
<%= @order.customer.name %>
<%= @order.customer.address.street %>
<%= @order.customer.address.city %>
<%= @order.customer.address.state %>
<%= @order.customer.address.zipcode %>
Tuy nhiên có một số vấn đề với đoạn code ở trên, ví dụ trong tương lai thay vì một khách hàng chỉ có 1 địa chỉ chung, chúng ta muốn phân tách rõ ràng billing address và shipping address. Khi đó chúng ta phải thay đổi lại tất cả các đoạn code liên quan đến địa chỉ cho phù hợp với model mới.
Để tránh vấn đề này chúng ta cần phải tuân theo Law of Demeter (LoD)
Each unit should have only limited knowledge about other units: only units “closely” related to the current unit. Or: Each unit should only talk to its friends; Don’t talk to strangers.
Trong Ruby hoặc Java chúng ta có thể hiểu nôm na tuân theo LoD có nghĩa là chúng ta chỉ sử dụng “one dot” Ví dụ @order.customer.name ko tuân theo LoD nhưng @order.customer_name tuân theo LoD.
Chúng ta refactor lại model theo để tuân theo LoD
class Address < ActiveRecord::Base
    belongs_to: customer
end

class Customer < ActiveRecord::Base
    has_one: address
    has_many: orders

    def street
        address.street
    end

    def city
        address.city
    end

    def state
        address.state
    end

    def zipcode
        address.zipcode
    end
end

class Order < ActiveRecord::Base
    belongs_to: customer

    def customer_name
        customer.name
    end

    def customer_street
        customer.street
    end

    def customer_city
        customer.city
    end

    def customer_state
        customer.state
    end 
    
    def customer_zipcode
        customer.zipcode
    end
end
Đoạn code hiển thị địa chỉ của đơn hàng được refactor lại như sau
<%= @order.customer_name %>
<%= @order.customer_street %>
<%= @order.customer_city %>
<%= @order.customer_state %>
<%= @order.customer_zipcode %>
Cách tiếp cận này làm cho model tràn ngập các wrapper method. Khi có yêu cầu thay đổi chúng ta phải maintain tất cả các wrapper đó. Mặc dù với hướng tiếp cận này chúng ta ít phải refactor code hơn so với việc update tất cả các views thông qua @order.customer.address.street tuy nhiên chúng ta nên tránh cách tiếp cận này.
Trong Ruby chúng ta có thể giải quyết vấn đề này thông qua :delegate
class Address < ActiveRecord::Base
    belongs_to :customer
end

class Customer < ActiveRecord::Base
    has_one :address
    has_many: orders

    delegate :street, :city, :state, :zipcode, :to => :address
end

class Order < ActiveRecord::Base
    belongs_to :customer

    delegate :name, :street, :city, :state, :zipcode, :to => :customer, :prefix => true
end
Đoạn code ở view của chúng ta vẫn ko thay đổi tuy nhiên model của chúng ta gọn hơn rất nhiều
<%= @order.customer_name %>
<%= @order.customer_street %>
<%= @order.customer_city %>
<%= @order.customer_state %>
<%= @order.customer_zipcode %>

Comments

Popular posts from this blog

Auto Code Review (Danger)

Code review là một quy trình bắt buộc trong qui trình phát triển phần mềm chuyên nghiệp. Mục tiêu quan trọng nhất của code review là nâng cao code quality của dự án. Tuy nhiên đối với những team làm outsource hoặc khi tính chất của dự án “quick win” với deadline oriented. Thông thường cách tiếp cận của team trong code review chỉ là apply một số rule về coding convention và coding style để cho dự án chạy. Lý do thường thấy nhất đó là reviewer thường phàn nàn không có đủ thời gian để làm review một cách chuẩn chỉnh và qui củ, hơn nữa việc bỏ sót lỗi trong quá trình review là khó tránh khỏi (human mistake). Hoàn toàn hợp lý tuy nhiên chúng ta có thể có một cách tiếp cận tốt hơn bằng cách delegate việc review code style, code convention, và kết quả static analysis tool cho code review bot (Danger). Bằng cách này chúng ta giải quyết được 3 vấn đề chính: Reviewer chỉ cần focus vào review business logic, cái này giá trị hơn rất nhiều so với code style và code convention cũng như s

Testing like a boss

Manual mobile app testing là một công việc không hề dễ dàng, nó đòi hỏi QA/Tester phải giành nhiều thời gian và công sức để có thể verify/qualify được hết tất cả các test case. Đặc biệt khi những yêu cầu như pixel perfect hay khi dữ liệu được combine từ nhiều nguồn khác nhau (API/Cache/Local Database/Sharepreferences), thì thời gian mà QA/Tester bỏ ra để có thể đánh giá được chính xác "development progress" là ko hề nhỏ. Tôi có thể kể ra một số câu hỏi thường gặp khi manual testing mobile app  1. Liệu 2 màu ( ▲ )( ▲ )   (implementation/specs) có thực sự giống nhau? Hãy chỉ ra mã màu của chúng? 2. Textsize là bao nhiêu? Typeface là gì? TextColor, HintColor giá trị như thế nào? Làm sao để trả lời đã tuân theo design specs hay chưa? 3. Khoảng cách giữa 2 view là bao nhiêu pixel? Có đúng specs ko? 4. Làm sao có thể biết được trong một màn hình, dữ liệu lấy từ đâu? (API/Cache/Local Database/Sharepreferences) 5. Làm sao có thể xoá local storage khi cần thiết? 6. Kiể