Có phải chúng ta đang lạm dụng các phương thức tĩnh?


12

Một vài tháng trước, tôi đã bắt đầu làm việc trong một dự án mới và khi duyệt mã, nó sẽ cho tôi biết số lượng phương thức tĩnh được sử dụng. Không chỉ các phương thức tiện ích như collectionToCsvString(Collection<E> elements), mà còn rất nhiều logic kinh doanh được giữ trong đó.

Khi tôi hỏi anh chàng chịu trách nhiệm cho lý do đằng sau chuyện này, anh ta nói rằng đó là một cách để thoát khỏi sự chuyên chế của Spring . Nó đi một cái gì đó xung quanh quá trình suy nghĩ này: để thực hiện một phương pháp tạo biên nhận khách hàng, chúng tôi có thể có một dịch vụ

@Service
public class CustomerReceiptCreationService {

    public CustomerReceipt createReceipt(Object... args) {
        CustomerReceipt receipt = new CustomerReceipt();
        // creation logic
        return receipt;
    }
}

Bây giờ, anh chàng nói rằng anh ta không thích có các lớp được quản lý một cách không cần thiết bởi Spring, về cơ bản vì nó áp đặt các hạn chế rằng các lớp khách phải là chính Spring bean. Chúng tôi cuối cùng có tất cả mọi thứ được quản lý bởi Spring, điều này khiến chúng tôi phải làm việc với các đối tượng không quốc tịch theo cách thủ tục. Nhiều hơn hoặc ít hơn những gì được nêu ở đây https://www.javacodegeek.com/2011/02/domain-driven-design-spring-aspectj.html

Vì vậy, thay vì các mã trên, anh ta có

public class CustomerReceiptCreator {

    public static CustomerReceipt createReceipt(Object... args) {
        CustomerReceipt receipt = new CustomerReceipt();
        // creation logic
        return receipt;
    }
}

Tôi có thể tranh luận đến mức tránh Spring quản lý các lớp học của chúng tôi khi có thể, nhưng điều tôi không thấy là lợi ích của việc có mọi thứ tĩnh. Các phương thức tĩnh này cũng không trạng thái, do đó cũng không OO lắm. Tôi sẽ cảm thấy thoải mái hơn với một cái gì đó như

new CustomerReceiptCreator().createReceipt()

Ông tuyên bố rằng các phương pháp tĩnh có một số lợi ích bổ sung. Cụ thể là:

  • Dễ đọc hơn. Nhập phương thức tĩnh và chúng ta chỉ cần quan tâm đến hành động, không có lớp nào đang thực hiện.
  • Rõ ràng là một phương thức không có các cuộc gọi DB, vì vậy hiệu năng rất rẻ; và đó là một điều tốt để làm cho nó rõ ràng, để khách hàng tiềm năng cần phải đi vào mã và kiểm tra điều đó.
  • Dễ dàng hơn để viết bài kiểm tra.

Nhưng tôi chỉ cảm thấy có điều gì đó không hoàn toàn đúng với điều này, vì vậy tôi muốn nghe một số suy nghĩ của các nhà phát triển dày dạn hơn về điều này.

Vì vậy, câu hỏi của tôi là, những cạm bẫy tiềm năng của cách lập trình này là gì?




4
Các staticphương pháp mà bạn đang minh họa ở trên chỉ là một phương pháp nhà máy bình thường. Làm cho các phương thức nhà máy tĩnh là quy ước được chấp nhận chung, vì một số lý do thuyết phục. Liệu phương pháp nhà máy có phù hợp ở đây là một vấn đề khác.
Robert Harvey

Câu trả lời:


22

Sự khác biệt giữa new CustomerReceiptCreator().createReceipt()và là CustomerReceiptCreator.createReceipt()gì? Khá nhiều không có. Sự khác biệt đáng kể duy nhất là trường hợp đầu tiên có cú pháp khó xử hơn đáng kể. Nếu bạn làm theo điều đầu tiên với niềm tin rằng bằng cách nào đó tránh các phương thức tĩnh làm cho mã của bạn tốt hơn OO, bạn đã bị nhầm lẫn nghiêm trọng. Tạo một đối tượng chỉ để gọi một phương thức duy nhất trên đó là một phương thức tĩnh bằng cú pháp obtuse.

Nơi mà mọi thứ trở nên khác biệt là khi bạn tiêm CustomerReceiptCreatorthay vì newing nó. Hãy xem xét một ví dụ:

class OrderProcessor {
    @Inject CustomerReceiptCreator customerReceiptCreator;

    void processOrder(Order order) {
        ...
        CustomerReceipt receipt = customerReceiptCreator.createReceipt(order);
        ...
    }
}

Hãy so sánh đây là một phiên bản phương thức tĩnh:

void processOrder(Order order) {
    ...
    CustomerReceipt receipt = CustomerReceiptCreator.createReceipt(order);
    ...
}

Ưu điểm của phiên bản tĩnh là tôi có thể dễ dàng cho bạn biết cách nó tương tác với phần còn lại của hệ thống. Nó không. Nếu tôi không sử dụng các biến toàn cục, tôi biết rằng phần còn lại của hệ thống đã bị thay đổi. Tôi biết rằng không có phần nào khác của hệ thống có thể ảnh hưởng đến việc nhận ở đây Nếu tôi đã sử dụng các đối tượng không thay đổi, tôi biết rằng thứ tự đã không thay đổi và creatReceipt là một hàm thuần túy. Trong trường hợp đó, tôi có thể tự do di chuyển / gỡ bỏ / vv cuộc gọi này mà không phải lo lắng về các tác động không thể đoán trước ngẫu nhiên ở nơi khác.

Tôi không thể đảm bảo như vậy nếu tôi đã tiêm CustomerReceiptCreator . Nó có thể có trạng thái nội bộ được thay đổi bởi cuộc gọi, tôi có thể bị ảnh hưởng bởi hoặc thay đổi trạng thái khác. Có thể có những mối quan hệ không thể đoán trước giữa các câu lệnh trong chức năng của tôi để việc thay đổi thứ tự sẽ đưa ra các lỗi đáng ngạc nhiên.

Mặt khác, điều gì xảy ra nếu CustomerReceiptCreatorđột nhiên cần một sự phụ thuộc mới? Hãy nói rằng nó cần kiểm tra một cờ tính năng. Nếu chúng ta tiêm, chúng ta có thể làm một cái gì đó như:

public class CustomerReceiptCreator {
    @Injected FeatureFlags featureFlags;

    public CustomerReceipt createReceipt(Order order) {
        CustomerReceipt receipt = new CustomerReceipt();
        // creation logic
        if (featureFlags.isFlagSet(Flags::FOOBAR)) {
           ...
        }
        return receipt;
    }
}

Sau đó, chúng ta đã hoàn thành, bởi vì mã cuộc gọi sẽ được tiêm với một mã CustomerReceiptCreatorsẽ tự động được tiêm a FeatureFlags.

Điều gì xảy ra nếu chúng ta đang sử dụng một phương thức tĩnh?

public class CustomerReceiptCreator {
    public static CustomerReceipt createReceipt(Order order, FeatureFlags featureFlags) {
        CustomerReceipt receipt = new CustomerReceipt();
        // creation logic
        if (featureFlags.isFlagSet(Flags::FOOBAR)) {
           ...
        }
        return receipt;
    }
}

Nhưng chờ đã! mã cuộc gọi cũng cần được cập nhật:

void processOrder(Order order) {
    ...
    CustomerReceipt receipt = CustomerReceiptCreator.createReceipt(order, featureFlags);
    ...
}

Tất nhiên, điều này vẫn để lại câu hỏi processOrderlấy FeatureFlagstừ đâu. Nếu chúng ta may mắn, đường mòn kết thúc ở đây, nếu không, sự cần thiết phải đi qua FeatureFlags sẽ được đẩy lên cao hơn nữa.

Có một sự đánh đổi ở đây. Các phương thức tĩnh yêu cầu chuyển rõ ràng xung quanh các phụ thuộc dẫn đến công việc nhiều hơn. Phương thức được tiêm làm giảm công việc, nhưng làm cho các phụ thuộc ngầm ẩn và do đó ẩn làm cho mã khó hơn để lý do.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.