Tên của mẫu (chống) sau đây là gì? ưu điểm và nhược điểm của nó là gì?


28

Trong vài tháng qua, tôi đã vấp ngã một vài lần về kỹ thuật / mô hình sau đây. Tuy nhiên, tôi dường như không thể tìm thấy một tên cụ thể, tôi cũng không chắc chắn 100% về tất cả các ưu điểm và nhược điểm của nó.

Mô hình đi như sau:

Trong giao diện Java, một tập hợp các phương thức phổ biến được định nghĩa như bình thường. Tuy nhiên, bằng cách sử dụng một lớp bên trong, một thể hiện mặc định bị rò rỉ thông qua giao diện.

public interface Vehicle {
    public void accelerate();
    public void decelerate();

    public static class Default {
         public static Vehicle getInstance() {
             return new Car(); // or use Spring to retrieve an instance
         }
    }
 }

Đối với tôi, dường như lợi thế lớn nhất nằm ở chỗ nhà phát triển chỉ cần biết về giao diện chứ không phải triển khai, ví dụ trong trường hợp anh ta muốn nhanh chóng tạo một thể hiện.

 Vehicle someVehicle = Vehicle.Default.getInstance();
 someVehicle.accelerate();

Hơn nữa, tôi đã thấy kỹ thuật này được sử dụng cùng với Spring để tự động cung cấp các phiên bản tùy thuộc vào cấu hình. Về vấn đề này, có vẻ như điều này có thể giúp với việc mô đun hóa.

Tuy nhiên, tôi không thể lay chuyển được cảm giác rằng đây là sự lạm dụng giao diện vì nó kết hợp giao diện với một trong những triển khai của nó. (Nguyên tắc đảo ngược phụ thuộc, v.v.) Ai có thể vui lòng giải thích cho tôi cách kỹ thuật này được gọi, cũng như ưu điểm & nhược điểm của nó không?

Cập nhật:

Sau một thời gian để xem xét, tôi đã kiểm tra lại và nhận thấy rằng phiên bản đơn lẻ sau đây của mẫu được sử dụng thường xuyên hơn. Trong phiên bản này, một phiên bản tĩnh công khai được hiển thị thông qua giao diện chỉ được khởi tạo một lần (do trường là cuối cùng). Ngoài ra, cá thể hầu như luôn được truy xuất bằng Spring hoặc một nhà máy chung để tách giao diện khỏi quá trình thực hiện.

public interface Vehicle {
      public void accelerate();
      public void decelerate();

      public static class Default {
           public static final Vehicle INSTANCE = getInstance();

           private static Vehicle getInstance() {
                return new Car(); // or use Spring/factory here
           }
      }
 }

 // Which allows to retrieve a singleton instance using...
 Vehicle someVehicle = Vehicle.Default.INSTANCE;

Tóm lại: có vẻ như đây là mẫu singleton / nhà máy tùy chỉnh, về cơ bản cho phép phơi bày một thể hiện hoặc một singleton thông qua giao diện của nó. Liên quan đến những bất lợi, một số ít đã được nêu tên trong các câu trả lời và ý kiến ​​dưới đây. Cho đến nay, lợi thế dường như nằm ở sự tiện lợi của nó.


một số loại mô hình singleton?
Bryan Chen

Tôi nghĩ rằng bạn đúng rằng giao diện không nên "biết" về việc triển khai của nó. Có lẽ Vehicle.Defaultnên được chuyển lên không gian tên gói như một lớp nhà máy, vd VehicleFactory.
sáu

7
Nhân tiện,Vehicle.Default.getInstance() != Vehicle.Default.getInstance()
Konrad Morawski 16/12/13

20
Các antipotype ở đây là sử dụng các antipotype tương tự xe hơi, liên quan chặt chẽ với các antipotype tương tự động vật. Hầu hết các phần mềm không có bánh xe hoặc chân.
Pieter B

@PieterB :-)))) Bạn đã tạo ra một ngày của tôi!
Doc Brown

Câu trả lời:


24

Default.getInstanceIMHO chỉ là một hình thức rất cụ thể của một phương thức xuất xưởng , trộn lẫn với một quy ước đặt tên được lấy từ mẫu đơn (nhưng không phải là một cách thực hiện sau). Trong biểu mẫu hiện tại, đây là vi phạm " nguyên tắc trách nhiệm duy nhất ", bởi vì giao diện (đã phục vụ mục đích khai báo API lớp) có trách nhiệm bổ sung là cung cấp một thể hiện mặc định, sẽ tốt hơn nhiều khi được đặt riêng biệt VehicleFactorylớp học.

Vấn đề chính gây ra bởi cấu trúc này là nó gây ra sự phụ thuộc theo chu kỳ giữa VehicleCar. Ví dụ, ở dạng hiện tại, không thể đặt Vehicletrong một thư viện và Cartrong một thư viện khác. Sử dụng một lớp nhà máy riêng biệt và đặt Default.getInstancephương thức ở đó sẽ giải quyết vấn đề đó. Nó cũng có thể là một ý tưởng tốt để đặt cho phương thức đó một tên khác, để ngăn chặn bất kỳ sự nhầm lẫn nào liên quan đến singletons. Vì vậy, kết quả có thể là một cái gì đó như

VehicleFactory.createDefaultInstance()


Cảm ơn câu trả lời của bạn! Tôi đồng ý rằng ví dụ cụ thể này là vi phạm SRP. Tuy nhiên, tôi không chắc liệu nó có còn vi phạm hay không trong trường hợp triển khai được lấy ra một cách linh hoạt. Ví dụ: bằng cách sử dụng Spring (hoặc khung tương tự) để truy xuất một thể hiện cụ thể được xác định bởi ví dụ: tệp cấu hình.
Jérôme

1
@ Jérôme: IMHO một lớp không lồng nhau có tên VehicleFactorythông thường hơn cấu trúc lồng nhau Vehicle.Default, đặc biệt là với phương thức "getInstance" sai lệch. Mặc dù có thể tránh được sự phụ thuộc theo chu kỳ bằng cách sử dụng Spring, cá nhân tôi thích biến thể đầu tiên chỉ vì lẽ thường. Và, nó vẫn cho bạn tùy chọn chuyển nhà máy sang một thành phần khác, thay đổi việc triển khai sau đó để hoạt động mà không có Mùa xuân, v.v.
Doc Brown

Thông thường, lý do để sử dụng một giao diện chứ không phải là một lớp là để cho phép các lớp có mục đích khác có thể sử dụng được bằng mã cần triển khai giao diện. Nếu một lớp triển khai mặc định có thể đáp ứng tất cả các nhu cầu của mã mà chỉ cần một cái gì đó thực hiện giao diện theo kiểu "bình thường", thì việc chỉ định một phương thức tạo một cá thể có vẻ là một điều hữu ích cho giao diện.
supercat

Nếu Xe là một phương thức triển khai tiêu chuẩn của Xe, thì đây không phải là vi phạm SRP. Xe vẫn chỉ có một lý do duy nhất để thay đổi, ví dụ như khi Google thêm một autodrive()phương thức. Chiếc xe rất chung chung của bạn sau đó phải thay đổi để thực hiện phương pháp đó, ví dụ như bằng cách ném NotImcellenceedException, nhưng điều đó không tạo thêm lý do để thay đổi trên Xe.
dùng949300

@ user949300: SRP không phải là một khái niệm "toán học có thể chứng minh". Và quyết định thiết kế phần mềm không phải lúc nào cũng là "trắng đen". Mã ban đầu không tệ đến mức nó không thể được sử dụng trong mã sản xuất, và có thể có trường hợp sự tiện lợi vượt xa sự phụ thuộc theo chu kỳ, như OP đã tự mình lưu ý trong "cập nhật" của mình. Vì vậy, vui lòng đọc câu trả lời của tôi là "xem xét nếu sử dụng một lớp nhà máy riêng biệt không phục vụ bạn tốt hơn". Cũng lưu ý rằng tác giả đã thay đổi câu hỏi ban đầu của mình một chút sau khi tôi đưa ra câu trả lời của mình.
Doc Brown

3

Điều này trông giống như mô hình Null Object đối với tôi.

Nhưng điều đó phụ thuộc rất nhiều vào cách Carlớp được thực hiện. Nếu nó được thực hiện theo cách "trung lập", thì đó chắc chắn là một đối tượng Null. Điều đó có nghĩa là, nếu bạn có thể loại bỏ tất cả các kiểm tra null trên giao diện và đặt thể hiện của lớp này thay vì mỗi lần xuất hiện null, thì mã vẫn phải hoạt động chính xác.

Ngoài ra nếu đó là mẫu này, thì không có vấn đề gì trong việc tạo khớp nối chặt chẽ giữa chính giao diện và việc thực hiện đối tượng null. Bởi vì đối tượng null có thể ở mọi nơi mà giao diện đã nói được sử dụng.


2
Xin chào và cảm ơn câu trả lời. Mặc dù tôi khá chắc chắn rằng trong trường hợp của mình, điều này không được sử dụng để triển khai mẫu "Đối tượng Null", đó là một lời giải thích thú vị.
Jérôme
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.