2009-03-19 20 views
7

Tôi đang duy trì một cơ sở mã Java cũ hơn (jvm 1.4) dường như sử dụng nhân bản như là một thay thế cho instantiation đối tượng, tôi đoán là tối ưu hóa hiệu suất. Dưới đây là một ví dụ giả tạo:Nhân bản có cung cấp cải tiến hiệu suất hơn các phương thức xây dựng/nhà máy không?

public class Foo { 
    private SomeObject obj; // SomeObject implements Cloneable 
    public Foo() { 
    obj = new SomeObject(); 
    obj.setField1("abc"); // these fields will have the same value every time 
    obj.setField2("def"); 
    } 
    public void doStuff() { 
    SomeObject newObj = obj.clone(); // clone it instead of using a factory method 
    // do stuff with newObj 
    } 
} 

Thông thường, hãy thận trọng về tối ưu hóa sớm, đây có phải là thành ngữ được đề nghị không?

+0

Có vẻ lạ. Tôi sẽ sử dụng constructor cho instantiation object mới. –

Trả lời

3

Một lý do để gọi clone() thay vì hàm tạo bản sao hoặc phương thức gốc là không có tùy chọn nào khác có thể có sẵn.

Thực hiện clone() để thực hiện bản sao đối tượng cạn (bản sao sâu có liên quan nhiều hơn) là tầm thường so với triển khai trình tạo bản sao hoặc phương thức nhà máy để thực hiện cùng một thao tác. Để triển khai clone(), một lớp chỉ cần triển khai giao diện Cloneable và phương thức ghi đè clone() với phương thức gọi số super.clone() thường gọi là Object.clone(). Object.clone() sao chép mọi thuộc tính của đối tượng ban đầu vào thuộc tính tương ứng của bản sao, do đó tạo bản sao nông.

Mặc dù triển khai clone() rất đơn giản, vẫn dễ quên thực hiện Cloneable. Do đó, nguy cơ tiềm ẩn khi sử dụng clone() để nhân đôi đối tượng là nếu lớp của đối tượng đó không bỏ qua để thực hiện Cloneableclone() gọi trực tiếp hoặc gián tiếp Object.clone(), nó sẽ ném CloneNotSupportedException.

Xem điều này code example và trước đó discussion trên poor design của giao diện Cloneable.

4

Có lẽ họ muốn có một bản sao. Có lẽ họ muốn truyền nó đến một chức năng khác, và không thể chắc chắn rằng chức năng đó sẽ không thay đổi nó. Đó là một cách để đảm bảo rằng phương thức doStuff() là const đối với trạng thái của đối tượng Foo mà nó được gọi.

+0

Bumping này lên 'nguyên nhân (sau khi nhìn vào mã) nó xuất hiện hoặc nhiều khả năng để áp dụng hơn câu trả lời bí truyền của tôi. – MarkusQ

+0

Lưu ý rằng đây là mục đích chính của nhân bản (giả sử các giáo sư của tôi biết họ đang nói về cái gì). – Chris

+0

+1: Yup, có vẻ như ở đây '.clone()' là cách truyền theo giá trị Java. –

1

Nó có thể là một tối ưu hóa hiệu suất, tùy thuộc vào bao nhiêu công việc được thực hiện trong các nhà xây dựng.

Có nhiều khả năng được sử dụng hơn vì ngữ nghĩa khác nhau. Nhân bản cung cấp một cách để thực hiện "ngữ nghĩa nguyên mẫu" (như trong javascript, tự, vv) trong một ngôn ngữ không bình thường có xu hướng theo cách đó.

+0

Làm thế nào? Tôi mặc dù ngữ nghĩa nguyên mẫu chỉ có nghĩa là bạn có thể thay đổi hành vi của hàm tạo hoặc các trường hoặc phương thức khác khi chạy. –

+0

Nhân bản cho phép bạn đặt các giá trị ban đầu, vv và (bằng cách sử dụng các đại biểu) cũng thay đổi hành vi. Nó là một loại kludge, nhưng bạn thường không thổi toàn ý nghĩa ngữ nghĩa, chỉ là một chút nước ép, vì vậy nó thường hoạt động trong thực tế. – MarkusQ

0

Nếu hàm tạo SomeObject thực hiện công việc tốn kém, chẳng hạn như lấy một thứ gì đó từ cơ sở dữ liệu hoặc phân tích cú pháp gì đó hoặc đọc nội dung nào đó từ một tệp thì sao chép sẽ có ý nghĩa để tránh thực hiện công việc.

Nếu hàm tạo không làm gì thì thực sự không cần sử dụng bản sao.

Edit: thêm mã để hiển thị bản sao mà không phải làm công việc giống như các nhà xây dựng:

class Main 
    implements Cloneable 
{ 
    private final double pi; 

    public Main() 
    { 
     System.out.println("in Main"); 
     // compute pi to 1,000,000,000 decimal palaces 
     pi = 3.14f; 
    } 

    public Object clone() 
    { 
     try 
     { 
      return (super.clone()); 
     } 
     catch(final CloneNotSupportedException ex) 
     { 
      throw new Error(); // would not throw this in real code 
     } 
    } 


    public String toString() 
    { 
     return (Double.toString(pi)); 
    } 

    public static void main(String[] args) 
    { 
     final Main a; 
     final Main b; 

     a = new Main(); 
     b = (Main)a.clone(); 

     System.out.println("a = " + a); 
     System.out.println("b = " + b); 
    } 
} 

Các construtor chính được gọi là một lần, pi tính toán được thực hiện một lần.

+0

nếu bạn định đánh dấu điều gì đó ít nhất là bình luận về những gì bạn cảm thấy là không chính xác về nó! – TofuBeer

+0

Nếu người tạo bản sao làm công việc tốn kém, thì sao chép sẽ phải làm như vậy. –

+0

sao chép bỏ qua các nhà xây dựng - làm thế nào để bạn có được nó sẽ phải làm như vậy? – TofuBeer

2

Một vấn đề lớn với các nhà xây dựng bản sao là loại đối tượng phải được biết tại thời gian biên dịch.Nếu một lớp có thể kế thừa hỗ trợ một hàm tạo bản sao, và hàm tạo được truyền một đối tượng lớp dẫn xuất, hàm tạo sẽ tạo ra một đối tượng lớp cơ sở có các thuộc tính lớp cơ sở thường khớp với đối tượng được truyền vào, nhưng đối tượng mới đã thắng t hỗ trợ bất kỳ tính năng nào có mặt trong đối tượng được truyền vào không có trong lớp cơ sở. Có thể giải quyết vấn đề này phần nào bằng cách tạo ra một hàm tạo bản sao "được bảo vệ", và có một phương thức sao chép nhà máy có thể ghi đè trong mọi lớp dẫn xuất gọi là hàm tạo bản sao của lớp, mà lần lượt gọi hàm tạo bản sao của cơ sở của nó. lớp học. Mỗi lớp dẫn xuất sẽ cần một hàm tạo bản sao và ghi đè lên phương thức sao chép, mặc dù nó có thêm bất kỳ trường mới nào hay không. Nếu trường hợp sử dụng "clone", mã bổ sung này có thể được loại bỏ.

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