0
0
Lập trình
Hưng Nguyễn Xuân 1
Hưng Nguyễn Xuân 1xuanhungptithcm

Tích hợp các thực tiễn tốt nhất vào Java

Đăng vào 7 tháng trước

• 5 phút đọc

Câu chuyện tích hợp thực tiễn tốt nhất vào Java

Trong bài viết này, chúng ta sẽ khám phá quá trình tái cấu trúc một ứng dụng, tập trung vào việc sử dụng các công cụ Dependency Injection (DI), phân tích các phụ thuộc và vẽ sơ đồ. Đây là câu chuyện về cách mà nhóm phát triển của chúng tôi đã cải thiện mã nguồn của chúng tôi để đạt được tiêu chuẩn cao hơn và hiệu suất tốt hơn.

Bối cảnh

Bảy năm trước, khi Java 8 vừa mới ra mắt, đội ngũ phát triển Java đã trải qua nhiều thay đổi. Trong giai đoạn này, dự án gần như đứng yên, chỉ sửa lỗi và cải thiện các plugin. Kết quả là, tiêu chuẩn phát triển Java bị vi phạm, với mã nguồn bị rối ren như:

  • Một lượng lớn các phương thức static
  • Các phương thức có tên dài dòng như doThisAndThisBeforeThat
  • Trạng thái toàn cục
  • Nhiều Singleton
  • Và những chú thích dài hơn cả mã nguồn.

Phong cách mã cũng là sự kết hợp giữa C# và C++, điều này đã vi phạm các tiêu chuẩn phát triển. Một trong những nhiệm vụ đầu tiên là làm sạch mã nguồn, chúng tôi đã áp dụng Google Java Style với một số thay đổi tiêu chuẩn trong ngành, chẳng hạn như sử dụng bốn khoảng trắng thay vì hai.

Nhờ có nhiều công cụ định dạng tự động, việc này khá dễ dàng. Tuy nhiên, việc viết lại kiến trúc là một câu chuyện khác, dường như là điều không thể. Vì vậy, chúng tôi quyết định thực hiện các thay đổi kiến trúc một cách dần dần khi cần thiết.

Nhu cầu về thay đổi

Nhu cầu về kỹ thuật tục tăng lên khi chúng tôi phát triển một module taint, yêu cầu một cơ chế chú thích (annotations). Trong trường hợp của chúng tôi, các chú thích là tập hợp các đánh dấu cho phân tích tĩnh như @NotNull@Nullable. Nhưng chúng tôi cần thứ gì đó tiên tiến hơn, với một chút chức năng bổ sung.

Ví dụ về chú thích

Dưới đây là một ví dụ về chú thích trong phân tích tĩnh Java:

java Copy
Class("java.lang.Math")
  - Function("max", Type::Int32, Type::Int32)
      .Pure()
      .Set(FunctionClassification::NoDiscard)
      .Requires(NotEquals(Arg1, Arg2)
      .Returns(Arg1, Arg2, [](const Int &v1, const Int &v2)
      { return v1.Max(v2); })

Chú thích này mô tả Math.max như sau:

  • Không có tác dụng phụ
  • Giá trị trả về phải được sử dụng
  • Các đối số đầu tiên và thứ hai không được lặp lại.

Mặc dù không rõ ràng khi chỉ nhìn vào mã, nhưng cơ chế chú thích của trình phân tích Java được viết bằng C++ và các chú thích này liên quan chặt chẽ đến cơ chế luồng dữ liệu, mà mô-đun C++ cũng cung cấp.

Giới thiệu về Guice

Dependency Injection (DI)

Các container DI rất phổ biến trong Java, một phần nhờ vào việc sử dụng rộng rãi của Spring Framework. Tuy nhiên, với ứng dụng CLI mà chúng tôi phát triển, Spring là quá nặng nề và mang nhiều phụ thuộc không cần thiết. Chúng tôi cần một lựa chọn nhẹ hơn - Guice, một framework DI từ Google không có các yếu tố thừa.

Làm quen với Guice

Mọi thứ bắt đầu với lớp AbstractModule. Để tạo một mô-đun, bạn kế thừa lớp này và viết phương thức configure:

java Copy
public class AnnotationModule extends AbstractModule {
    @Override
    public void configure() {
    }
}

Để định nghĩa một phụ thuộc trong container, sử dụng phương thức bind với tham số là lớp phụ thuộc:

java Copy
public class AnnotationModule extends AbstractModule {
    @Override
    public void configure() {
        bind(AnnotationProcessor.class);
    }
}

Khởi tạo injector

Sau đó, gọi Guice.createInjector để nạp ứng dụng:

java Copy
var injector = Guice.createInjector(new AnnotationModule());
var processor = injector.getInstance(AnnotationProcessor.class);

Xử lý phụ thuộc

Nếu lớp AnnotationProcessor chứa một constructor không tham số, injector sẽ tạo đối tượng. Nếu có phụ thuộc, constructor này cần được đánh dấu với @Inject:

java Copy
public class AnnotationProcessor {
  private final Dependency dependency;

  @Inject
  public AnnotationProcessor(Dependency dependency) {
    this.dependency = dependency;
  }
}

Khi thực thi mã, nếu Dependency không có trong đồ thị phụ thuộc, chương trình sẽ gặp lỗi. Do đó, chúng tôi cần khai báo nó trong mô-đun:

java Copy
public class AnnotationModule extends AbstractModule {
  @Override
  public void configure() {
    bind(AnnotationProcessor.class);
    bind(Dependency.class);
  }
}

Bây giờ, gọi injector.getInstance(AnnotationProcessor.class) sẽ trả về một đối tượng với trường dependency đã được khởi tạo.

Kết luận

Cuối cùng, chúng tôi đã cải thiện mã nguồn và áp dụng các tiêu chuẩn mới cho phân tích tĩnh. Việc áp dụng Guice không chỉ giúp chúng tôi dễ dàng quản lý phụ thuộc mà còn giảm thiểu các vấn đề về trạng thái toàn cục. Với kiến trúc mới, chúng tôi có thể mở rộng tính năng và duy trì mã nguồn một cách dễ dàng hơn.

Thực hành tốt

  • Sử dụng DI để giảm độ phức tạp: Tránh sử dụng các Singleton và trạng thái toàn cục, thay vào đó, hãy sử dụng DI để quản lý các phụ thuộc.
  • Chú thích rõ ràng: Đảm bảo rằng các chú thích trong mã nguồn được mô tả rõ ràng và chính xác, giúp dễ dàng cho việc bảo trì và mở rộng sau này.
  • Kiểm tra và xử lý lỗi: Luôn kiểm tra các phụ thuộc và xử lý lỗi nếu có, điều này giúp mã nguồn ổn định hơn.

Các cạm bẫy thường gặp

  • Quá phụ thuộc vào DI: Đừng để DI trở thành nơi gây ra độ phức tạp không cần thiết. Hãy chắc chắn rằng mô-đun của bạn vẫn đơn giản và dễ hiểu.
  • Thiếu tài liệu: Hãy viết tài liệu cho các chú thích và mô-đun để các lập trình viên khác dễ dàng hiểu và sử dụng.

Câu hỏi thường gặp

  1. Guice có thể thay thế Spring không?
    Có, Guice là một lựa chọn tốt hơn cho các ứng dụng không cần nhiều tính năng của Spring.

  2. Làm thế nào để xử lý lỗi trong Guice?
    Sử dụng các phương thức tổ chức phụ thuộc và kiểm tra các đối tượng để đảm bảo không có lỗi xảy ra.

  3. Có cần thiết phải biết C++ để sử dụng Guice không?
    Không, Guice hoàn toàn viết bằng Java và không yêu cầu bạn phải biết C++. Tuy nhiên, sẽ giúp ích nếu bạn hiểu các nguyên lý của DI tổng quát.

Kết luận

Việc cải thiện mã nguồn và áp dụng các thực tiễn tốt nhất trong Java là rất quan trọng để đảm bảo tính ổn định và khả năng mở rộng của ứng dụng. Hãy thử nghiệm với các kỹ thuật này và xem sự khác biệt mà chúng mang lại cho dự án của bạn!

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào