Menu
CoddyTech

An toàn với giá trị null trong lớp

Thuộc phần Lập trình hướng đối tượng trong hành trình Dart của Coddy. Bài học 28 trên 110.

Khi thiết kế lớp, bạn cần quyết định thuộc tính nào là bắt buộc và thuộc tính nào là tùy chọn. Tính an toàn với giá trị null ảnh hưởng đến cách bạn khai báo biến của đối tượng và viết hàm khởi tạo.

Bạn phải khởi tạo các trường không thể nhận giá trị null trước khi phần thân hàm khởi tạo chạy. Bạn có thể làm việc này bằng danh sách khởi tạo, tham số của hàm khởi tạo hoặc giá trị mặc định:

class User {
  String name;           // Phải khởi tạo
  String? bio;           // Không bắt buộc, có thể là null
  int age = 0;           // Có giá trị mặc định
  
  User(this.name);       // name được khởi tạo qua tham số
}

Với các trường có thể nhận giá trị null, bạn có thể để chúng chưa được khởi tạo vì mặc định chúng có giá trị null. Điều này hữu ích với dữ liệu không bắt buộc:

class Product {
  String title;
  String? description;   // Mô tả sản phẩm không bắt buộc
  double? discount;      // Mặc định không có giảm giá
  
  Product(this.title);
}

var item = Product('Laptop');
print(item.description);  // null
print(item.discount);     // null

Khi truy cập các trường có thể nhận giá trị null, hãy dùng các toán tử nhận biết null mà bạn đã học:

class Order {
  String id;
  User? customer;
  
  Order(this.id);
  
  String getCustomerName() {
    return customer?.name ?? 'Guest';
  }
}

Từ khóa required trong các tham số có tên đảm bảo bên gọi cung cấp các giá trị thiết yếu:

class Account {
  String email;
  String? phone;
  
  Account({required this.email, this.phone});
}

var acc = Account(email: 'test@mail.com');  // phone là tùy chọn

Bằng cách cân nhắc kỹ khi chọn trường có thể nhận giá trị null và trường không thể nhận giá trị null, bạn tạo ra các lớp thể hiện rõ dữ liệu nào là thiết yếu và dữ liệu nào là tùy chọn.

challenge icon

Thử thách

Dễ

Hãy xây dựng hệ thống đặt phòng khách sạn để minh họa cách thiết kế các lớp với sự kết hợp hợp lý giữa những trường bắt buộc và tùy chọn bằng cách sử dụng tính an toàn null.

Bạn sẽ sắp xếp code thành hai tệp:

  • reservation.dart: Định nghĩa lớp Reservation đại diện cho một lượt đặt phòng khách sạn. Mỗi lượt đặt phòng cần một số thông tin thiết yếu, nhưng một số chi tiết là tùy chọn và khách có thể cung cấp hoặc không. Lớp của bạn cần có:
    • String guestName không nullable, mỗi lượt đặt phòng phải có tên khách
    • int roomNumber không nullable, mỗi lượt đặt phòng phải được chỉ định một phòng
    • int nights không nullable với giá trị mặc định là 1, thời gian lưu trú tối thiểu là một đêm
    • String? specialRequests nullable, khách có thể có các yêu cầu đặc biệt
    • DateTime? checkInTime nullable, có thể chưa biết chính xác thời gian khách đến
    • Một hàm khởi tạo dùng tham số có tên với required cho guestName và roomNumber, còn nights là tùy chọn với giá trị mặc định
    • Một phương thức getRequestsSummary() trả về các yêu cầu đặc biệt nếu có, hoặc 'No special requests' bằng toán tử ??
    • Một phương thức getArrivalInfo() trả về biểu diễn dạng chuỗi của thời gian nhận phòng bằng ?., hoặc trả về null nếu chưa được đặt
    • Một phương thức printConfirmation() hiển thị thông tin đặt phòng
  • main.dart: Nhập lớp đặt phòng và tạo các lượt đặt phòng thể hiện những cách kết hợp khác nhau giữa dữ liệu bắt buộc và tùy chọn:
    • Tạo lượt đặt phòng cho khách 'Sarah Johnson' ở phòng 101 trong 3 đêm, với yêu cầu đặc biệt 'Late checkout' và đặt thời gian nhận phòng thành DateTime(2024, 6, 15, 14, 30)
    • Tạo lượt đặt phòng cho khách 'Mike Chen' ở phòng 205 (dùng số đêm mặc định), chỉ đặt yêu cầu đặc biệt thành 'Extra pillows'
    • Tạo lượt đặt phòng cho khách 'Emma Davis' ở phòng 310 trong 2 đêm (để cả hai trường tùy chọn là null)
    • Lần lượt với từng lượt đặt phòng, gọi printConfirmation(), sau đó in Arrival: [result] bằng getArrivalInfo()

Phương thức printConfirmation() cần in theo định dạng sau:

=== Reservation Confirmed ===
Guest: [guestName]
Room: [roomNumber]
Nights: [nights]
Requests: [getRequestsSummary()]

Đầu ra dự kiến:

=== Reservation Confirmed ===
Guest: Sarah Johnson
Room: 101
Nights: 3
Requests: Late checkout
Arrival: 2024-06-15 14:30:00.000
=== Reservation Confirmed ===
Guest: Mike Chen
Room: 205
Nights: 1
Requests: Extra pillows
Arrival: null
=== Reservation Confirmed ===
Guest: Emma Davis
Room: 310
Nights: 2
Requests: No special requests
Arrival: null

Tự làm thử

import 'reservation.dart';

void main() {
  // TODO: Tạo đặt phòng cho 'Sarah Johnson'
  // Phòng 101, 3 đêm, yêu cầu đặc biệt: 'Late checkout'
  // Thời gian nhận phòng: DateTime(2024, 6, 15, 14, 30)
  
  // TODO: Tạo đặt phòng cho 'Mike Chen'
  // Phòng 205, dùng số đêm mặc định, yêu cầu đặc biệt: 'Extra pillows'
  // Không chỉ định thời gian nhận phòng
  
  // TODO: Tạo đặt phòng cho 'Emma Davis'
  // Phòng 310, 2 đêm, không có yêu cầu đặc biệt, không có thời gian nhận phòng
  
  // TODO: Với từng đặt phòng theo thứ tự:
  // 1. Gọi printConfirmation()
  // 2. In ra 'Arrival: [result]' bằng getArrivalInfo()
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch Dart online