2012-05-16 23 views
7

Tôi đã được thông báo rằng lập trình cho các giao diện cho các biến cục bộ là vô dụng và không nên được thực hiện vì nó chỉ làm tổn thương hiệu suất và không mang lại lợi ích gì.Cách thực hành 'lập trình giao diện' tốt nhất có áp dụng cho các biến cục bộ không?

public void foo() { 
    ArrayList<Integer> numbers = new ArrayList<Integer>(); 
    // do list-y stuff with numbers 
} 

thay vì

public void foo() { 
    List<Integer> numbers = new ArrayList<Integer>(); 
    // do list-y stuff with numbers 
} 

tôi cảm thấy như đạt hiệu suất là không đáng kể nhưng phải thừa nhận là không có nhiều để đạt được bằng cách sử dụng Danh sách ngữ nghĩa của ArrayList. Có những lý do mạnh mẽ để đi theo cách này hay cách khác không?

+18

Ai là người đã nói với bạn rằng nó sẽ "làm tổn thương hiệu suất"? Bạn có lẽ nên xem xét bỏ qua lời khuyên từ anh/cô ấy trong tương lai. –

Trả lời

12

Thực tiễn tốt nhất của 'lập trình để giao diện' có áp dụng cho các biến cục bộ không?

Mặc dù phạm vi biến như vậy là nhỏ hơn so với phạm vi của một biến thành viên, hầu hết các thebenefits cho mã hóa chống lại các giao diện khi nói đến các biến thành viên cũng áp dụng cho các biến cục bộ.

Trừ khi bạn cần Phương pháp cụ thể ArrayList, hãy đi tới Danh sách.

Tôi cảm thấy như hit hiệu suất là không đáng kể nhưng phải thừa nhận là không có nhiều để đạt được bằng cách sử dụng Danh sách ngữ nghĩa của ArrayList

Tôi chưa bao giờ nghe nói rằng lời gọi các phương pháp giao diện sẽ chậm hơn so với cách gọi phương pháp trên một loại lớp.

Sử dụng ArrayList thay vì List làm loại tĩnh âm thanh như tối ưu hóa sớm cho tôi. Như thường lệ, hãy ủng hộ khả năng đọc và bảo trì qua tối ưu hóa cho đến khi bạn đã lược tả toàn bộ chương trình của mình.

+0

Nếu bạn cần các phương thức cụ thể của arraylist, bạn có thể thực hiện một downcast từ một danh sách. –

+0

Cảm ơn. Đã chỉnh sửa câu trả lời của tôi :-) – aioobe

+2

@aioobe Bạn đã nói rằng nó tốt hơn vì nó linh hoạt hơn mà không giải thích lý do tại sao việc sử dụng Danh sách trên ArrayList linh hoạt hơn. Quan tâm để giải thích? Hãy nhớ rằng, chúng tôi chỉ nói các biến cục bộ tại đây. –

3

Sử dụng loại trừu tượng nhất cho phép bạn hoàn thành công việc. Nếu bạn chỉ cần List, thì hãy sử dụng nó. Nếu bạn cần phải hạn chế loại cho một cái gì đó cụ thể hơn, sau đó sử dụng nó thay thế. Ví dụ: trong một số trường hợp sử dụng List sẽ quá cụ thể và việc sử dụng Collection sẽ được ưu tiên hơn. Ví dụ khác, ai đó có thể muốn yêu cầu ConcurrentMap thay vì một số Map thông thường trong một số trường hợp.

13

Đi lớp này ví dụ:

public class Tmp { 
    public static void main(String[] args) { 
     ArrayList<Integer> numbers = new ArrayList<Integer>(); 
     numbers.add(1); 
    } 
} 

Nó biên dịch xuống này:

Compiled from "Tmp.java" 
public class Tmp extends java.lang.Object{ 
public Tmp(); 
    Code: 
    0: aload_0 
    1: invokespecial #1; //Method java/lang/Object."<init>":()V 
    4: return 

public static void main(java.lang.String[]); 
    Code: 
    0: new #2; //class java/util/ArrayList 
    3: dup 
    4: invokespecial #3; //Method java/util/ArrayList."<init>":()V 
    7: astore_1 
    8: aload_1 
    9: iconst_1 
    10: invokestatic #4; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 
    13: invokevirtual #5; //Method java/util/ArrayList.add:(Ljava/lang/Object;)Z 
    16: pop 
    17: return 

} 

Trong khi lớp này:

public class Tmp { 
    public static void main(String[] args) { 
     List<Integer> numbers = new ArrayList<Integer>(); 
     numbers.add(1); 
    } 
} 

biên dịch xuống này:

Compiled from "Tmp.java" 
public class Tmp extends java.lang.Object{ 
public Tmp(); 
    Code: 
    0: aload_0 
    1: invokespecial #1; //Method java/lang/Object."<init>":()V 
    4: return 

public static void main(java.lang.String[]); 
    Code: 
    0: new #2; //class java/util/ArrayList 
    3: dup 
    4: invokespecial #3; //Method java/util/ArrayList."<init>":()V 
    7: astore_1 
    8: aload_1 
    9: iconst_1 
    10: invokestatic #4; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 
    13: invokeinterface #5, 2; //InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z 
    18: pop 
    19: return 

} 

Bạn sẽ thấy rằng điểm khác biệt duy nhất là sử dụng số ArrayList) gọi invokevirtual và một số khác (sử dụng List sử dụng) invokeinterface.invokeinterface thực sự là một mái tóc chậm hơn invokevirtual (~ 38% theo số this guy). Đây là apparently do tối ưu hóa mà JVM có thể thực hiện khi tìm kiếm thông qua các bảng phương thức ảo của lớp bê tông so với các bảng phương thức của giao diện. Vì vậy, những gì bạn đang nói là thực sự đúng sự thật. Yêu cầu giao diện chậm hơn lời gọi lớp bê tông.

Tuy nhiên, bạn phải cân nhắc loại tốc độ mà chúng ta đang nói đến. Đối với 100.000.000 lời gọi, sự khác biệt thực tế là 0,03 giây. Vì vậy, bạn phải có tấn các lời gọi để thực sự có tốc độ giảm đáng kể.

Mặt khác, như @ChinBoon chỉ ra, mã hóa giao diện giúp việc sử dụng mã của bạn trở nên dễ dàng hơn, đặc biệt nếu mã của bạn trả về một số loại List. Vì vậy, trong số lớn nhất phần lớn các trường hợp, dễ dàng mã hóa đến nay vượt quá chi phí hiệu suất tương đối.


gia tăng sau khi đọc @ bình luận MattQuigley và cho nó một suy nghĩ trên đường về nhà

gì tất cả điều này có nghĩa là đây không phải là điều bạn nên lo lắng về quá nhiều. Mọi lợi ích hoặc hình phạt về hiệu suất có thể là rất nhỏ.

Xin lưu ý, việc sử dụng giao diện cho các loại trả lại và thông số của bạn về phương pháp là một ý tưởng rất hay. Điều đó cho phép bạn và bất kỳ ai sử dụng mã của bạn để sử dụng bất kỳ việc thực hiện nào của List phù hợp nhất với nhu cầu của họ. Ví dụ của tôi cũng cho thấy rằng, nếu bạn tình cờ sử dụng phương thức trả về List, 99% thời gian, bạn là không phải là tốt hơn khi bỏ nó vào một lớp cụ thể chỉ để tăng hiệu suất. Thời gian cần để đúc sẽ có khả năng lớn hơn mức tăng hiệu suất.

Điều đó đang được nói, ví dụ này cũng cho thấy rằng, đối với biến số cục bộ, bạn thực sự nên sử dụng lớp bê tông thay vì giao diện. Nếu phương pháp duy nhất bạn sử dụng là phương pháp trên List, thì bạn có thể chuyển đổi các lớp triển khai mà không có tác dụng phụ. Ngoài ra, bạn sẽ có quyền truy cập vào các phương pháp cụ thể triển khai nếu bạn cần chúng. Thêm vào đó là một tăng hiệu suất nhỏ.

tl; dr

Luôn luôn sử dụng giao diện với nhiều loại lợi nhuận và các thông số về phương pháp. Bạn nên sử dụng các lớp cụ thể cho các biến cục bộ. Nó mang lại hiệu suất nhỏ và không mất chi phí để chuyển đổi các triển khai nếu các phương thức duy nhất bạn sử dụng có trên giao diện. Cuối cùng, bạn không nên lo lắng quá nhiều. (Ngoại trừ loại trả lại và thông số. Điều quan trọng.)

+0

Vâng điểm chuẩn là một chút ngây thơ. Tuy nhiên, sự chậm chạp tương đối của 'invokeinterface' được viết khá tốt. Mức độ mà điều này là đúng có thể thay đổi sau khi khởi động máy ảo, nhưng nó vẫn đúng. –

+0

Dường như bạn đã đúng. Tôi có thể tái sản xuất, ngay cả sau khi khởi động. Bạn có bất kỳ tham chiếu đến một lời giải thích tốt về điều này? – aioobe

+0

@aioobe Vâng tôi đã thêm một chút xáo trộn về nó trong một bản sửa đổi câu trả lời của tôi. Tôi tìm thấy lời giải thích trong [câu trả lời SO này] (http://stackoverflow.com/a/1505476/836738). Không chính xác có uy tín, nhưng nó là * giải thích * (có vẻ hợp lý). –

0

Đặc biệt nếu phương thức trả về danh sách, mã hóa thành giao diện sẽ giúp bạn nếu bạn phải thay đổi triển khai thành danh sách được liên kết một ngày. Và đó là một lợi ích, bạn sẽ không cần phải thay đổi phương thức để trả về một danh sách liên kết hoặc một danh sách mảng.

+0

+1 Câu trả lời hay nhất cho đến nay. – GETah

+1

Tôi không chắc chắn tôi nhận được câu trả lời này. OP hỏi về loại biến cục bộ, không phải kiểu trả về của phương thức. (Ví dụ của anh ta thậm chí còn trả về void.) Thậm chí nếu một phương thức được khai báo trả về một 'List ', bạn cũng có thể trả về một biến với 'ArrayList ' làm kiểu tĩnh. – aioobe

+0

@aioobe tất cả các câu trả lời đã trình bày rằng pov, tôi quyết định mở rộng ra. –

-1

Có @Matthew, vẫn tốt hơn là thực hành tốt nhất sử dụng

List<Integer> numbers = new ArrayList<Integer> 

vì bạn có thể sử dụng các phương pháp lớp ArrayList và Danh sách lớp. Tôi hy vọng giúp bạn.

+0

* vì bạn có thể sử dụng các phương thức lớp ArrayList và Danh sách lớp * - Không, (A) bạn không thể làm ví dụ như 'numbers.ensureCapacity (10)' (B) 'List' không phải là một lớp. – aioobe

+0

Ok, Java là tất cả các lớp. Cảm ơn bạn. – hekomobile

+0

((số ArrayList)) .ensureCapacity (10) –

5

TLDR: Phương pháp invocations chậm hơn trên giao diện/lớp trừu tượng, và hầu như là không có trở ngại cho việc thay đổi việc thực hiện của một biến cục bộ tại một điểm sau này trong thời gian mà làm cho hầu hết các lập luận một điểm tranh luận.

Có rất nhiều câu trả lời ở đây được nói một cách mù quáng để khai báo biến cục bộ dưới dạng giao diện mà không hiểu tại sao. Trước hết, lời gọi phương thức chậm hơn - khoảng 3x, như tôi nhớ trong các thử nghiệm. Phương thức add trong khối mã đầu tiên chậm gấp 3 lần lần thứ hai. Bạn có thể tự mình kiểm tra bằng một phép thử đơn giản đo thời gian trôi qua trong vòng lặp for.

List<Integer> numbers = new ArrayList<Integer>(); 
numbers.add(1); // slower 

ArrayList<Integer> numbers = new ArrayList<Integer>(); 
numbers.add(1); // faster 

Thứ hai, câu hỏi là về biến cục bộ, không phải là nguyên tắc thiết kế hướng đối tượng của giao diện nói chung. Có những lý do OO tốt mà một phương thức lớp công khai sẽ trả về một giao diện hoặc lớp trừu tượng (List) thay vì thực hiện (ArrayList). Bạn có thể đọc về các lý do như vậy ở nơi khác, nhưng câu hỏi này là không phải về điều đó - đó là về các biến cục bộ. 99,999% số lần bạn sẽ không bao giờ thay đổi việc triển khai biến cục bộ đó từ ArrayList thành số Vector. Tuy nhiên, trong 0,001% thời gian bạn thực sự làm, không có trở ngại để làm như vậy - bạn chỉ cần sao chép và dán từ Vector qua từ ArrayList và bạn đã hoàn tất. Mọi người dường như nghĩ rằng sự thay đổi như vậy là khó khăn. Nó không phải.

Thứ ba, LinkedList không giống như ArrayList. Bạn chọn một hoặc một lý do khác (thời gian cần để truy cập/nối/chèn/xóa). Khai báo nó như là một ArrayList là một sự khẳng định cho phép bạn biết rằng biến cục bộ này có nghĩa là để truy cập ngẫu nhiên nhanh chóng. Các lập luận rằng nó là tối ưu hóa sớm là ngớ ngẩn - nó được tối ưu hóa một cách này hay cách khác ngay sau khi nó được khởi tạo.

+0

câu hỏi này đã được hỏi nhiều lần; và câu trả lời đúng luôn có điểm thấp nhất :) http://stackoverflow.com/questions/3768869/use-interface-or-type-for-variable-definition-in-java/3774026#3774026 – irreputable

0

đây là một đề xuất: khai báo Danh sách là cuối cùng, nhưng với giao diện.

final List<Integer> numbers = new ArrayList<Integer>(); 

Nếu tôi đã phải mất một đoán (và tôi không phải là một chuyên gia về trình biên dịch java chính nó), nhưng làm cho biến thức, trình biên dịch sẽ có thể loại bỏ các invokeinterface ủng hộ invokevirtual, kể từ nó biết loại sẽ không thay đổi khi biến được khai báo.

+1

Thật không may, từ khóa cuối cùng trên biến cục bộ không mang lại bất kỳ lợi ích nào cho hiệu suất. Cuối cùng trên các biến cục bộ không cho phép trình biên dịch sử dụng. Lợi ích duy nhất là cho ngữ nghĩa thiết kế. –

Các vấn đề liên quan