Chuyển đến nội dung chính

Primary constructor trong Dart 3.13: bớt boilerplate, một thay đổi phá vỡ thật sự

Dart 3.13 ra cùng Flutter 3.47, và tính năng tiêu điểm là thứ lập trình viên Dart đòi từ hồi ngôn ngữ có null safety: primary constructor đã stable. Khai báo field và constructor ngay trên header của class, và xoá đi ba dòng nghi thức mà mọi class model trong codebase của bạn đang lặp lại.

Trong bản phát hành cũng có một thay đổi phá vỡ ẩn mình, và nó không phải thứ phần lớn mọi người đoán.

Hình dạng cơ bản

Dạng truyền thống:

class Point {
  int x;
  int y;
  Point(this.x, this.y);
}

Dạng primary constructor:

class Point(var int x, var int y);

Đó là toàn bộ class. Chú ý thứ tự — modifier đứng trước, rồi tới kiểu, rồi tới tên: var int x, không phải int var x.

Declaring parameter mới là tính năng thật

Đây là phần đáng hiểu cho kỹ, vì thiết kế ở đây tinh tế hơn câu “cú pháp ngắn hơn”.

Một tham số có tiền tố var hoặc finaldeclaring parameter: nó sinh ra một instance field. Tham số không có modifier nào hành xử như một đối số constructor thông thường và không tạo field nào cả.

// Tạo field x và y
class Point(var int x, var int y);

// Không tạo field — `name` chỉ là đối số
class User(String name);

Sự phân biệt đó cho bạn miễn phí một constructor chỉ để validate, mà không cần mẹo tạo một field private chẳng bao giờ đọc.

Với model bất biến — tức là phần lớn model trong một codebase Flutter — hãy dùng final:

class ConstPoint(final int x, final int y);

Named parameter, và mẹo dấu gạch dưới

Named parameter hoạt động như bạn nghĩ, kèm một chi tiết hay: một named parameter private tự động phơi ra một tên public cho phía gọi.

class User({required var String _name});
// Gọi bằng: User(name: 'John Doe')

Field là _name, private trong thư viện. Nhãn đối số là name, public. Bạn có đóng gói mà không phải viết một constructor để ánh xạ cái này sang cái kia.

Primary constructor có tên và private

Thêm dấu chấm và một cái tên sau định danh class:

class Point.custom(var int x, var int y);

// Private — hạn chế khởi tạo trực tiếp từ ngoài thư viện
class Point._(var int x, var int y);

Dạng private chính là mẫu “gần như sealed” mà nhiều package đang tự cài đặt bằng tay hôm nay.

Primary constructor const

Đặt const trước danh sách tham số:

class ConstPoint const (final int x, final int y) {
  final int z;
  this : z = x + y;
}

Các ràng buộc đúng như bạn đoán từ ngữ nghĩa const:

  • không có khối thân
  • mọi field phải final và chắc chắn được khởi tạo
  • biểu thức khởi tạo phải có khả năng là hằng

Với Flutter điều này quan trọng hơn vẻ ngoài. Constructor widget const là một đòn bẩy hiệu năng thật, và bất cứ thứ gì làm chúng rẻ hơn để viết đều khiến chúng được viết nhiều hơn.

Thêm thân mà không phải bỏ header

Bạn không phải chọn giữa primary constructor và logic constructor. this : mở ra một initializer list, một thân, hoặc cả hai:

class Point(var int x, var int y) {
  this : assert(x >= 0 && y >= 0) {
    print('Point initialized at ($x, $y)');
  }
}

Super parameter chuyển tiếp gọn gàng:

class Person(final String name, final int age);

class Employee(super.name, super.age, final String role) extends Person;

Quy tắc scope sẽ làm bạn vấp

Có hai scope, và chúng phân giải cùng một định danh theo cách khác nhau:

ScopeỞ đâux trỏ tới
Primary initializer scopeKhởi tạo field, initializer listtham số
Primary parameter scopeThân constructorfield (với declaring parameter)

Cụ thể:

class ScopingDemo(var String x, String suffix) {
  final String field = x;  // 'x' là tham số

  this : {
    x = x.toUpperCase();   // 'x' giờ trỏ tới field
    print('$x$suffix');    // 'suffix' vẫn là tham số
  }
}

Hãy đọc hai lần trước khi refactor một class có initializer list không tầm thường. Nó nhất quán, nhưng không hiển nhiên.

Ràng buộc

Các ràng buộc lúc biên dịch đáng biết trước khi bạn bắt đầu chuyển đổi class:

  • declaring parameter không được dùng late hay external
  • tên tham số không được trùng method hoặc field đã có
  • tham số là chỉ đọc bên trong primary initializer scope
  • không được khởi tạo một field ở cả chỗ khai báo lẫn trong primary constructor
  • class mixin chỉ được khai báo primary constructor tầm thường — không tham số, không initializer list, không thân
  • covariant chỉ hoạt động với declaring parameter khả biến (var)

Thay đổi phá vỡ

Đây là phần cần lên kế hoạch. finalvar trên tham số hàm thông thường giờ dành riêng cho declaring parameter của primary constructor. Code hiện có viết void f(final int x) trở thành không hợp lệ.

Câu chuyện lint đổi theo: parameter_assignments (Dart 3.13+) thay cho avoid_final_parametersvar_with_no_type_annotation cũ từ 3.12 trở về trước.

Còn một cạm bẫy thứ hai, âm thầm hơn: một method tên factory không có kiểu trả về tường minh giờ có thể bị phân tích nhầm thành factory constructor. Nếu bạn có, hãy thêm chú thích kiểu trả về.

Còn gì nữa trong 3.13

Ngoài thay đổi ngôn ngữ:

  • dart2wasm deferred loading (preview)dart compile wasm -O2 --enable-deferred-loading, cải thiện đáng kể thời gian tải trang lần đầu so với dart2js cho app lớn
  • @RecordUse() trong package:meta, cho phép tree-shake thư viện native song song với code Dart
  • tinh chỉnh formatter — sửa lỗi định dạng sai quanh các collection literal lớn, cải thiện heuristic ngắt chuỗi method, và thêm dòng trống giữa các nhóm import theo Effective Dart
  • sửa tính đúng đắn của type promotion cho hàm lồng nhau
  • dartdoc render nhanh hơn trên pub.dev nhờ chỉ mục băm hai tầng

Áp dụng mà không gây xáo trộn

  1. Nâng cấp và build. Sửa mọi tham số kiểu void f(final int x) mà analyzer giờ từ chối.
  2. Thêm kiểu trả về tường minh cho mọi method tên factory.
  3. Bật parameter_assignments và gỡ avoid_final_parameters / var_with_no_type_annotation khỏi analysis_options.yaml.
  4. Bắt đầu với class dữ liệu thuần — DTO, value object, model kiểu freezed. Giảm boilerplate nhiều nhất, rủi ro thấp nhất.
  5. Dùng bốn refactoring trong IDE để chuyển qua lại giữa primary constructor và constructor trong thân, thay vì sửa tay.
  6. Chuyển constructor widget const tiếp theo, khi bạn đã quen với ràng buộc của dạng const.
  7. Để dành class có initializer list phức tạp sau cùng, và đọc lại bảng scope trước khi động vào.

Kết luận

Primary constructor là loại tính năng ngôn ngữ hiếm hoi mang tính thuần trừ đi: cùng ngữ nghĩa, gõ ít hơn, không thêm khái niệm mới một khi bạn hiểu declaring parameter. Ràng buộc var/final trên tham số hàm là thay đổi phá vỡ thật, nhưng mang tính cơ học và analyzer sẽ tìm ra hết trong một lần build. Hãy chuyển các class dữ liệu ngay tuần này; để dành mấy class rắc rối cho tới khi bạn đã thuộc quy tắc hai scope.


Bài viết gốc đăng tại FlutterCook. Bản trên đó là bản được cập nhật mới nhất.

Nhận xét

Bài đăng phổ biến từ blog này

Thiết kế giao diện với DotNetBar (Phần 1)

Đây là phiên bản DotNetBar hỗ trợ C# và Visual Basic https://www.dropbox.com/s/wx80jpvgnlrmtux/DotNetBar.rar  , phiên bản này hỗ trợ giao diện Metro cực kỳ “dễ thương” Các bạn load về và cài đặt, khi cài đặt xong sẽ có source code mẫu của tất cả các control. Để sử dụng được các control của DotNetBar các bạn nhớ add item vào controls box. Thiết kế giao diện với DotNetBar, giao diện sẽ rất đẹp. Link các video hướng dẫn chi tiết cách sử dụng và coding: http://www.devcomponents.com/dotnetbar/movies.aspx Hiện tại DotNetBar có rất nhiều công cụ cực mạnh, trong đó có 3 công cụ dưới đây: DotNetBar for Windows Forms Requires with Visual Studio 2003, 2005, 2008, 2010 or 2012.   DotNetBar for WPF Requires with Visual Studio 2010 or 2012 and Windows Presentation Foundation.   DotNetBar for Silverlight Requires with Visual Studio 2010 or 2012 and Silverlight. Dưới đây là một số hình ảnh về các control trong DotnetBar.   Metro User Interface  controls with Metro Tiles, toolba...

5 concepts every Flutter dev should know

  Phụ lục: State management architecture Testing IDE Shortcuts Platform channel Maintaining a project Tôi đã làm việc với Flagship trong một thời gian dài, và đây là những điều mà tôi phát hiện ra là điều cần phải có đối với bất kỳ nhà phát triển Flagship nào, về tổng thể nó sẽ khiến bạn trở thành một nhà phát triển Flagship giỏi trong thời gian dài. 1. State management architecture Đây là một trong những chủ đề quan trọng nhất trong cộng đồng thiết bị rung, nó khá quan trọng nếu bạn muốn duy trì một dự án rung kích thước trung bình hoặc lớn. Nó sẽ giúp tạo một dự án suôn sẻ và thêm các tính năng mới một cách hoàn hảo.  2. Testing Đây là một chủ đề duy nhất mà tôi không hiểu tại sao nó lại quan trọng trước đó trong sự nghiệp của tôi, nhưng khi tôi tiến lên trong sự nghiệp của mình và có kinh nghiệm với nhiều dự án và vấn đề xảy ra trong môi trường sản xuất. Tôi đã nhận ra một cách khó khăn, tại sao điều này lại quan trọng như vậy. Nếu bạn vẫn muốn có thêm lý do để cân nhắc thử...

Announcing Flutter 2

  Phụ lục: Flutter on the web Flutter 2 on desktops, foldables, and embedded devices The growing Flutter ecosystem Dart: The secret sauce behind Flutter Flutter 2: Available now Hôm nay, chúng tôi sẽ công bố Flutter 2: một bản nâng cấp lớn cho Flutter cho phép các nhà phát triển tạo các ứng dụng đẹp, nhanh chóng và di động cho bất kỳ nền tảng nào. Với Flutter 2, bạn có thể sử dụng cùng một cơ sở mã để gửi các ứng dụng gốc cho năm hệ điều hành: IOS, Android, Windows, macOS và Linux; cũng như trải nghiệm web nhắm mục tiêu các trình duyệt như Chrome, Firefox, Safari hoặc Edge. Flutter thậm chí có thể được nhúng vào ô tô, TV và thiết bị gia dụng thông minh, mang đến trải nghiệm di động và lan tỏa nhất cho thế giới điện toán xung quanh. Mục tiêu của chúng tôi là thay đổi cơ bản cách các nhà phát triển nghĩ về việc xây dựng ứng dụng, bắt đầu không phải với nền tảng bạn đang nhắm mục tiêu mà là với trải nghiệm bạn muốn tạo. Flutter cho phép bạn tạo ra những trải nghiệm tuyệt đẹp trong đó ...