Trong Java, một số cách tốt để tách API khỏi việc triển khai * toàn bộ dự án * là gì?


8

Hãy tưởng tượng bạn có một mô-đun phần mềm, là một plugin cho một số chương trình (tương tự như Eclipse) và bạn muốn nó có một API mà các plugin khác có thể gọi. Plugin của bạn không phải là tự do có sẵn, vì vậy bạn muốn có một mô-đun API riêng biệt, đó tự do sẵn có và là điều duy nhất plugin khác cần phải trực tiếp liên kết đến - khách hàng API có thể biên dịch chỉ với mô-đun API, và không phải là mô-đun thực hiện, trên con đường xây dựng. Nếu API bị hạn chế phát triển theo các cách tương thích, thì các plugin khách hàng thậm chí có thể bao gồm mô-đun API trong các lọ riêng của chúng (để ngăn chặn mọi khả năng xảy Errorra do các lớp không tồn tại được truy cập).

Cấp phép không phải là lý do duy nhất để đưa API và triển khai vào các mô-đun riêng biệt. Nó có thể là mô-đun triển khai là phức tạp, với vô số phụ thuộc của chính nó. Các plugin Eclipse thường có các gói bên trong và không bên trong, trong đó các gói không bên trong tương tự như một mô-đun API (cả hai đều được bao gồm trong cùng một mô-đun, nhưng chúng có thể được tách ra).

Tôi đã thấy một vài lựa chọn thay thế khác cho việc này:

  1. API nằm trong một gói riêng (hoặc nhóm các gói) từ việc triển khai. Các lớp API gọi trực tiếp vào các lớp thực hiện. API không thể được biên dịch từ nguồn (điều này là mong muốn trong một số trường hợp không phổ biến) mà không cần thực hiện. Thật không dễ để dự đoán các tác động chính xác của việc gọi các phương thức API khi triển khai chưa được cài đặt - vì vậy khách hàng thường sẽ tránh làm điều này.

    package com.pluginx.api;
    import com.pluginx.internal.FooFactory;
    public class PluginXAPI {
        public static Foo getFoo() {
            return FooFactory.getFoo();
        }
    }
  2. API nằm trong một gói riêng và sử dụng sự phản chiếu để truy cập các lớp thực hiện. API có thể được biên dịch mà không cần thực hiện. Việc sử dụng sự phản chiếu có thể gây ra một cú đánh hiệu suất (nhưng các đối tượng phản chiếu có thể được lưu trong bộ nhớ cache nếu đó là một vấn đề. Thật dễ dàng để kiểm soát những gì xảy ra nếu việc triển khai không khả dụng.

    package com.pluginx.api;
    public class PluginXAPI {
        public static Foo getFoo() {
            try {
                return (Foo)Class.forName("com.pluginx.internal.FooFactory").getMethod("getFoo").invoke(null);
            } catch(ReflectiveOperationException e) {
                return null;
                // or throw a RuntimeException, or add logging, or raise a fatal error in some global error handling system, etc
            }
        }
    }
  3. API chỉ bao gồm các giao diện và các lớp trừu tượng, cộng với một cách để có được một thể hiện của một lớp.

    package com.pluginx.api;
    public abstract class PluginXAPI {
        public abstract Foo getFoo();
    
        private static PluginXAPI instance;
        public static PluginXAPI getInstance() {return instance;}
        public static void setInstance(PluginXAPI newInstance) {
            if(instance != null)
                throw new IllegalStateException("instance already set");
            else
                instance = newInstance;
        }
    }
  4. Tương tự như trên, nhưng mã máy khách cần lấy tham chiếu ban đầu từ một nơi khác:

    // API
    package com.pluginx.api;
    public interface PluginXAPI {
        Foo getFoo();
    }
    
    // Implementation
    package com.pluginx.internal;
    public class PluginX extends Plugin implements PluginXAPI {
        @Override
        public Foo getFoo() { ... }
    }
    
    // Client code uses it like this
    PluginXAPI xapi = (PluginXAPI)PluginManager.getPlugin("com.pluginx");
    Foo foo = xapi.getFoo();
  5. Đừng. Làm cho khách hàng liên kết trực tiếp đến plugin (nhưng vẫn ngăn họ gọi các phương thức không phải API). Điều này sẽ gây khó khăn cho nhiều plugin khác (và hầu hết các plugin nguồn mở) khi sử dụng API của plugin này mà không viết trình bao bọc riêng của chúng.

Câu trả lời:


3

Câu trả lời cho câu hỏi của bạn phụ thuộc vào định nghĩa "tốt" trong câu hỏi "một số cách tốt để tách API khỏi việc triển khai ..."

Nếu "tốt" có nghĩa là "thực dụng dễ thực hiện đối với bạn với tư cách là người sản xuất api" thì điều này có thể hữu ích:

vì bạn đang sử dụng java trong đó các thư viện jar được tải trong thời gian chạy, tôi sẽ đề xuất một cách khác hơi khác:

  • API chỉ bao gồm các giao diện cộng với việc triển khai các giao diện giả này không có chức năng thực sự.

khách hàng sử dụng api của bạn có thể biên dịch lại với hình nộm này và trong thời gian chạy, bạn có thể thay thế hình nộm bằng cái được cấp phép.

một cái gì đó tương tự được thực hiện với slf4j trong đó việc thực hiện ghi nhật ký thực tế được sử dụng với ứng dụng của bạn sẽ được chọn bằng cách thay thế tệp ghi nhật ký


Để mở rộng trên ví dụ slf4j : slf4j chứa các cài đặt giả trong các nguồn mà sau đó được gỡ bỏ sau khi biên dịch. Tác động của điều đó là một tài liệu tham khảo mạnh mẽ (dẫn đến ClassDefNotFound nếu không có triển khai nào được cung cấp trong ứng dụng của bạn)
dot_Sp0T

@ dot_Sp0T Tôi đồng ý rằng vấn đề "ClassDefNotFound" tồn tại cho Android vì trong Android tất cả các tệp được hợp nhất vào một tệp apk. Trong java thông thường (không sử dụng obfuscator), đây không phải là vấn đề. Nếu tôi sai lời cầu xin cho tôi biết trong trường hợp xảy ra.
k3b

4

Bạn đã xem qua Cơ chế dịch vụ tải trong Java chưa? Về cơ bản, bạn có thể chỉ định việc thực hiện một giao diện thông qua tệp kê khai trong một tệp jar. Oracle cũng cung cấp thêm một số thông tin về các plugin trong các chương trình Java.


1

Từ những gì tôi hiểu mọi người thường sử dụng mô hình nhà máy cho việc này.

Họ đặt các giao diện API vào mô-đun riêng biệt (giả sử là tệp jar) và sau đó khi khách hàng muốn sử dụng API và có quyền truy cập vào việc triển khai API, việc triển khai API sẽ có điểm vào nhà máy cho khách hàng bắt đầu tạo các đối tượng cụ thể triển khai API đó.

Điều này có nghĩa là ý tưởng đầu tiên của bạn ở trên là sự tương đồng gần nhất.

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.