Lập trình hướng chức năng đang thống trị mảng UI với Pure Views

Những năm gần đây đã chứng kiến sự cải thiện rất lớn trong cách UI được phát triển bằng cách sử dụng Purely Functional Views. Trong lập trình hướng chức năng “pure function” là một trong số function khi chạy, trả về một giá trị nhưng không thay đổi bất cứ điều gì (còn được gọi là side-effect free function). Điều này làm giảm đáng kể việc load cognitive và permutations trong các ứng dụng để có thể giảm được rất nhiều bug (xem trường hợp nghiên cứu), dễ hiểu, dễ tương tác và ổn định vô cùng.

David Nolen có một trong những cách giải thích tốt nhất cho kiểu phát triển UI này, ƒ (d) = V. Đó là một chức năng nhận dữ liệu làm đầu vào của nó và trả về dưới chế độ view. Không scoping, binding, subclassing, truy cập từ bên ngoài vào các biến hoặc bất kỳ tác dụng phụ. Điều này làm cho nó trở nên rất có thể đoán được chỉ cần nhìn qua Pure View được chạy. Trong thế giới JavaScript bạn có thể nói, Cho bất kỳ JSON nào cũng sẽ tạo ra các trình listener HTML, CSS và sự kiện tương tự.

Tại sao Pure Views khác với các kiểu UI khác?

Để hiểu kỹ về điều này, chúng ta cần phải hiểu những gì chúng ta đã ngầm làm trong nhiều năm trong code của chúng ta. Code được viết như một chuỗi các permutation phức tạp. Mỗi chức năng chúng ta call đều thay đổi một cái gì đó bên ngoài phạm vi private. Sau đó chúng ta call một hàm khác thay đổi cái gì đó khác. Nếu dữ liệu hoặc thứ tự của các chức năng này call deviate trong bất kỳ cách nào bug sẽ đến bất ngờ. Hãy suy nghĩ về những lần bạn fix bug được tạo ra bởi một cái gì đó dường như không liên quan trong class khác hoặc server một nơi nào đó. Đây là những vấn đề lớn về permutation.

Hãy xem bảng dưới đây. Đây là cách chúng tôi suy nghĩ về nội bộ của các ứng dụng.

Cách chúng tôi “nghĩ” cách ứng dụng của chúng tôi hoạt động.

Rất rõ ràng, phải không? Call “login ()”, rồi “getUser ()” rồi “getCart ()”. Trong thực tế luồng ứng dụng của chúng tôi bao gồm các đường dẫn lỗi, các giá trị bất ngờ, network và luồng. Nếu chúng trở lại với một thứ tự hơi khác hoặc dữ liệu hơi khác nhau hoặc dữ liệu bị biến đổi bởi nhiều class trong ứng dụng của chúng tôi, chúng sẽ tạo ra bug. Đây là mô hình chính xác hơn về các nhánh:

Cách ứng dụng của chúng tôi hoạt động thực sự.

Nếu bất cứ điều gì không đi theo hướng dự kiến vào thời gian dự kiến chúng tôi nhận được bug. Lớp OOP được cho là dữ liệu đóng gói, sự kiện và biến đổi bên trong ranh giới của nó để làm việc này mà không có bug. Rõ ràng chúng ta đều có thể gặp bug. Nhưng hệ thống rất khó bởi vì chúng phức tạp để tạo ra bug.

Đơn giản dễ dàng

Đối với bất cứ ai đã học về Gang of Four and Object-Oriented Patterns nhưng chưa xem bài nói chuyện của Rich Hickey “Simple Made Easy” hãy xem nó ngay bây giờ. Khái niệm cốt lõi mà Hickey nói đến là làm thế nào mà quá nhiều yếu tố bên ngoài là một lý do chính tại sao chúng ta không hiểu rõ code của chúng ta đang làm gì khi chúng ta đọc nó. Không dễ vì quá nhiều điều được thực hiện cùng một lúc. Ngôn ngữ hướng đối tượng có các pattern cố gắng giảm sự phức tạp đó bằng cách sử dụng encapsulation. Các ngôn ngữ chức năng, có khả năng cao composability là tính năng chính để giảm sự phức tạp. Một sự kết hợp tốt hơn của cả hai là cần thiết để có được kiểm soát tốt hơn UI của chúng tôi.

Pure Views mang lại một số lợi ích của ngôn ngữ chức năng cho thế giới OO mà hầu hết các lập trình viên sử dụng. Trong ngôn ngữ chức năng, pure function chỉ làm một điều và không có tác dụng phụ (không tác dụng phụ có nghĩa là nó không ảnh hưởng đến bất cứ điều gì, chỉ trả về một giá trị mới). Nhiều chức năng đơn giản có thể được tạo ra theo nhiều cách khác nhau để tạo ra nhiều loại dữ liệu đầu ra. Để giải thích Rich Hickey, đơn giản có nghĩa là làm một việc. Dễ dàng có nghĩa là chúng ta có thể hiểu được tổng quan. Trong trường hợp của Pure View, nhìn vào chức năng render chúng ta có thể hiểu được tất cả các đầu ra tiềm năng. Nó làm giảm yêu cầu hiểu toàn bộ cơ sở code để biết làm thế nào nó có thể có tác dụng đến một chức năng duy nhất.

Đây là cách chế độ Pure View sẽ hoạt động:

Pure Views là những biến đổi đơn giản mà không có tác dụng phụ.

Đây là một khái niệm vô cùng mạnh mẽ. Thay vì phải truyền thông qua chức năng, sự kiện và các network stack và thay đổi ở mọi nơi, chỉ cần chụp nhanh dữ liệu và hiển thị với chế độ view. Chế độ view sẽ có tất cả các bố cục, trình xử lý sự kiện và các điểm truy cập để người dùng tương tác với các dữ liệu cơ bản. Điều này có nghĩa là để có đến được bất kỳ vị trí trong ứng dụng, chỉ phụ thuộc là một mô hình dữ liệu tĩnh. Nói chung các Pure View này được kết hợp với các phương pháp vòng đời để hiểu rõ hơn khi nào, tại sao và dữ liệu thay đổi như thế nào. Ví dụ: hãy xem một số code thực sự của Pure View:

createView({
  getDefaultProps(){
    return {
      selectedIds: [1, 10],
      banks: [/* bank data here */]
    };
  } 
  render(){ 
    let selectedBanks = this.props.banks.filter(function(bank){
      return this.props.selectedIds.indexOf(bank.id) > -1;
    });
    let bankItems = [];
    for (let i=0; i < selectedBanks.length; i++){
      bankItems.push(
        el("BankItem", {key:i, bank:selectedBanks[i]})
      );
    }
    return el("div", null,
             el("h1", null, "Selected Banks"),
             bankItems,
             el("a", {href:"/install", "Next")
           );
  }
})

View này encapsulate các thuộc tính được chuyển bởi parent của nó. Khi chức năng render được call, nó không yêu cầu bất cứ điều gì ngoài phạm vi chính của view. Tất cả mọi thứ có thể ảnh hưởng đến đầu ra tất cả đều trong một chức năng duy nhất. Cho đến khi chức năng render kết thúc, props và state cũng không thể thay đổi. Điều này làm cho nó dễ dàng hiểu lý do về những gì sẽ xảy ra trên mỗi lần chạy. Bất cứ khi nào parent gửi dữ liệu mới, nó sẽ kích hoạt chức năng render để chạy lại.

Mở rộng khả năng mà không cần phức tạp thêm

Bởi vì Pure View chỉ làm một việc và không ảnh hưởng gì đến những ngoài phạm vi của nó, nó làm cho nó trở nên cực kỳ linh hoạt mà không có sự phức tạp. Có một vài trường hợp cho thấy sức mạnh thực sự của Pure View.

Việc đầu tiên được gọi là Hot Module Replacement. Nếu bạn nghĩ rằng điều này cũng giống như Live Reloading trong trình duyệt, bạn sẽ không còn hứng thú nữa. Ngay khi bạn lưu file trong editor, chỉ có code mới cập nhật được gửi đến app của bạn, thay thế code cũ, chức năng hiển thị cho chế độ view được chạy và chỉ HTML khác thay đổi. Toàn bộ data layer của ứng dụng không bị ảnh hưởng. Điều này làm việc với layout, view hierarchy và xử lý sự kiện. Dữ liệu trong app không thay đổi vì vậy bạn đang được xem dữ liệu cập nhật theo thời gian thực. Có thư viện để hỗ trợ điều này trong trình duyệt, ứng dụng iOS, Android, Windows và Mac. Khi thời gian lặp giữa việc thực hiện thay đổi code và nhìn thấy đầu ra gần 0, nó thay đổi hoàn toàn quy trình code của bạn.

Tiếp theo là Time Travel Debugging. Nó thực sự tuyệt vời như cái tên. Nếu không có Pure View, lưu trữ tất cả các sự kiện propagation và tác động của nó đến view là impossible load cho hầu hết các hệ thống. Với Pure Views tất cả những gì cần thiết là có một mảng và đẩy một bản sao của dữ liệu mới mỗi khi nó thay đổi. Nếu bạn hiểu cấu trúc dữ liệu liên tục hoạt động, công việc thậm chí còn ít hơn để lưu mọi thay đổi trong app của bạn. Khi gỡ lỗi, chỉ cần scrub back và forth giống như một VCR. (Công nghệ UI những năm 80,  Yeah những năm 80 và hầu hết các UI framework không có debug toolkit như ngày hôm nay). Application state thậm chí có thể được xếp theo thứ tự và lưu trên máy chủ trong quá trình sản xuất để có được các bước repo đầy đủ về mọi lỗi trong ứng dụng của bạn.

Khả năng chuyên dụng cuối cùng là Resiliency Over Rigidity. Dữ liệu không được gắn với UI. Rõ ràng tách biệt là điều cần quan tâm. Data layer ứng dụng của bạn có thể tập trung về chức năng, chuyển đổi dữ liệu và cache mà không cần phải suy nghĩ về tất cả những mối quan tâm mà người dùng có thể có. Về phần lớn các dự án business logic hầu như không thay đổi; Code cho một giỏ mua hàng comm khá nhiều có các yêu cầu tương tự. Nhưng trên View layer, các nhà thiết kế luôn tìm ra cách mới để giải thích hoặc làm rõ giao diện hoặc tăng doanh thu. Các bài kiểm tra đơn vị và tích hợp có thể tập trung vào việc kiểm tra lưu lượng ứng dụng trong dữ liệu thuần túy, đơn giản. Vì View chỉ là một phép biến đổi đơn giản và phản ánh dữ liệu ở bất kỳ trạng thái nào, nó ít bị ảnh hưởng bởi complexity hoặc bug. Điều này cũng có nghĩa là khi người thiết kế hoặc giám đốc kinh doanh yêu cầu thay đổi chế độ view, nó có thể được thực hiện mà không ảnh hưởng đến chức năng của luồng dữ liệu.

Quay trở lại với thực tế

Pure View đang phát triển. Có rất ít, nếu có bất kỳ tiện ích truyền thống view layer trên Pure View. Framework mở rộng hơn như Backbone bạn có thể trao đổi ra các hệ thống templating string cho Pure View. Ngày càng có nhiều tác giả framework đang nhìn thấy những lợi thế và hướng đến Pure Views. Một vài framework đáng chú ý thực sự là Elm, Ember 2, Om Next, ReactJS.

Để tận dụng lợi thế của một số khả năng mở rộng Pure View cung cấp thì có một số công cụ out-of-the-box mà dễ dàng hơn để tích hợp. Đối với Hot Module Replacement, Webpack và React là lựa chọn tuyệt vời. Tôi duy trì một bộ starter kit được gọi là Megatome, cái này sẽ giúp bạn bắt đầu và chạy một dự án mới trong vòng 2 phút. Đối với Time Travel Debugging xem như Redux với data layer  được xây dựng để làm việc với React.

Lưu ý cuối cùng là dành cho tất cả những người xem xét kỹ lưỡng cú pháp (ví dụ như Lisps hoặc JSX): không đánh giá một cuốn sách qua trang bìa của nó. Nếu bạn không nhìn kỹ hơn, bạn hoàn toàn có thể bỏ qua các khả năng và sức mạnh có thể đến từ một cái gì đó khác nhau. Nhìn lại đoạn code ở trong Pure View ở trên. Đó là tất cả các code hợp lệ mà không cần phải transpilation, quản lý package hoặc bất kỳ bước build bổ sung nào. Điều duy nhất mà mất là:

let el = React.createElement;
let createView = React.createClass;

Techtalk via Freecodecamp