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, đó là 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 Error
ra 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:
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(); } }
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 } } }
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; } }
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();
Đừ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.