Triển khai nhiều Giao diện chung trong java


10

Tôi cần một giao diện đảm bảo cho tôi một phương thức nhất định, bao gồm cả chữ ký cụ thể, có sẵn. Cho đến nay anh ấy là những gì tôi có:

public interface Mappable<M> {
    M mapTo(M mappableEntity);
}

Vấn đề phát sinh khi một lớp nên có thể ánh xạ tới nhiều thực thể khác. Trường hợp lý tưởng sẽ là thế này (không phải java):

public class Something implements Mappable<A>, Mappable<B> {
    public A mapTo(A someObject) {...}
    public B mapTo(B someOtherObject) {...}
}

Điều gì sẽ là cách tốt nhất để đạt được điều này còn lại là "chung chung" nhất có thể?

Câu trả lời:


10

Tất nhiên, đây không phải là điều bạn có thể làm do Loại Xóa . Trong thời gian chạy, bạn có hai phương thức public Object mapTo(Object), rõ ràng không thể cùng tồn tại.

Thật không may, những gì bạn đang cố gắng làm chỉ đơn giản là ngoài hệ thống kiểu của Java.

Giả sử kiểu chung của bạn luôn là loại lớp đầu tiên và không phải là chung chung, bạn có thể đạt được hành vi hướng ngoại tương tự bằng cách có phương thức mapTo(Object, Class), cho phép bạn thực hiện kiểm tra thời gian chạy của lớp đã cho và quyết định sử dụng hành vi nào. Rõ ràng điều này khá không phù hợp - và sẽ yêu cầu đúc thủ công giá trị trả về - nhưng tôi nghĩ đó là cách tốt nhất bạn có thể làm. Nếu các loại chung của bạn là chung chung, thì các tham số chung của chúng cũng sẽ bị xóa và các Lớp của chúng sẽ bằng nhau, vì vậy phương thức này sẽ không hoạt động.

Tuy nhiên, tôi cũng sẽ hướng tới câu trả lời của @ Joachim, đây có thể là trường hợp bạn có thể phân chia hành vi thành các thành phần riêng biệt và vượt qua toàn bộ vấn đề.


3

Như bạn đã thấy, bạn không thể thực hiện cùng một giao diện hai lần với các tham số loại khác nhau (vì xóa: trong thời gian chạy, chúng là các giao diện giống nhau).

Ngoài ra, cách tiếp cận này vi phạm nguyên tắc trách nhiệm duy nhất: lớp của bạn nên tập trung vào việc trở thành một Something(bất kể điều đó có nghĩa là gì) và không nên thực hiện ánh xạ tới Ahoặc B ngoài nhiệm vụ đó.

Có vẻ như bạn thực sự nên có một Mapper<Something,A>và a Mapper<Something,B>. Theo cách này, mỗi lớp có một trách nhiệm được xác định rõ ràng bạn không gặp phải vấn đề thực hiện cùng một giao diện hai lần.


Chà, ý tưởng là để cho lớp chịu trách nhiệm "chuyển đổi" nội dung của nó sang các đối tượng khác. Sau đó, có một người điều phối xử lý chúng theo cách thức bất khả tri, do đó, yêu cầu chung chung. Tôi sẽ suy nghĩ một chút về việc trích xuất logic, mặc dù điều đó thực sự có nghĩa là tôi chia lớp thành hai, nhưng chúng vẫn được liên kết chặt chẽ (nên cấp quyền truy cập vào trường, sửa đổi hầu hết các lần ngụ ý sửa đổi cái khác, v.v.)
estani

@estani: vâng, họ có đôi chút gắn bó chặt chẽ, nhưng họ có trách nhiệm riêng biệt. Cũng nghĩ về điều này: khi bạn giới thiệu một lớp mới Cvà bạn muốn Somethingcó thể ánh xạ tới đó, thì bạn cần phải sửa đổi Something, đó là quá nhiều khớp nối. Chỉ cần thêm một cái mới SoemthingToCMapperlà ít xâm phạm.
Joachim Sauer

1
+1 cho điều này - nói chung, bạn nên ưu tiên sáng tác hơn kế thừa nếu bạn đang cố gắng đạt được những gì OP muốn (trong Java). Java 8 với các phương thức mặc định làm cho việc này thậm chí còn dễ dàng hơn - nhưng không phải ai cũng có thể nhảy vào giai đoạn chảy máu :-).
Martijn Verburg

0

Do không được phép thực hiện nhiều giao diện, bạn có thể xem xét việc sử dụng đóng gói. (ví dụ sử dụng java8 +)

// Mappable.java
public interface Mappable<M> {
    M mapTo(M mappableEntity);
}

// TwoMappables.java
public interface TwoMappables {
    default Mappable<A> mapableA() {
         return new MappableA();
    }

    default Mappable<B> mapableB() {
         return new MappableB();
    }

    class MappableA implements Mappable<A> {}
    class MappableB implements Mappable<B> {}
}

// Something.java
public class Something implements TwoMappables {
    // ... business logic ...
    mapableA().mapTo(A);
    mapableB().mapTo(B);
}

Vui lòng kiểm tra ở đây để biết thêm thông tin và thêm ví dụ: Làm thế nào để tạo một lớp Java thực hiện một giao diện với hai loại chung? .


-1
public interface IMappable<S, T> {
    T MapFrom(S source);
}

// T - target
// S - source

Nếu bạn muốn ánh xạ Người dùng thành UserDTO và ánh xạ Người dùng sang UserViewModel, thì bạn sẽ cần hai triển khai riêng biệt. Đừng chồng chất tất cả logic này vào một lớp duy nhất - không có nghĩa gì để làm điều đó.

Cập nhật để giữ cho Joachim hạnh phúc

public interface ITypeConverter<TSource, TDestination>
{
    TDestination Convert(TSource source);
}

Nhưng bây giờ chúng ta đang ở trong vương quốc Automapper ( http://automapper.codeplex.com/wikipage?title=Custom%20Type%20Converters )


Tôi không nghĩ IMappablelà một cái tên hay cho một cái gì đó ánh xạ những thứ khác. Mapper(hoặc IMapper, nếu bạn phải ;-)) có lẽ đúng hơn. (Nhân tiện: không, đó không phải là downvote của tôi).
Joachim Sauer

Tôi đã lấy những gì trong câu hỏi và có tiền tố I để làm nổi bật thực tế đó là một giao diện. Tôi đang giải quyết vấn đề thiết kế theo câu hỏi, trái ngược với vấn đề đặt tên.
CodeART

1
xin lỗi, nhưng theo tôi người ta không thể thực sự "giải quyết" một vấn đề thiết kế và bỏ qua việc đặt tên. Thiết kế có nghĩa là các cấu trúc dễ hiểu. Đặt tên sai là một vấn đề cần hiểu.
Joachim Sauer

Cập nhật sẽ khiến tâm trí bạn được nghỉ ngơi ;-)
CodeART

1
@CodeART Nếu tôi hiểu chính xác câu trả lời của bạn, nó ngụ ý "MapFrom" (cần được hạ thấp ;-) tạo đối tượng. Trong trường hợp của tôi, nó chỉ điền thông tin vào một đối tượng đã được tạo.
estani
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.