2012-05-02 12 views
65

thể trùng lặp:
Why use getters and setters?Điểm getters và setters là gì?

Tôi đã đọc cuốn sách về Java, nói rằng nó là tốt để tạo setters và thu khí cho các biến như xy. Ví dụ:

public int getX(){ 
    return x; 
} 

public void setX(int x){ 
    this.x = x; 
} 

Nhưng sự khác biệt từ đó và

...(shape.x)... // basically getX() 

shape.x = 90; // basically setX() 

Nếu setters và thu khí là tốt hơn, bạn có thể giải thích cho tôi những vấn đề thực tế sẽ nảy sinh là những gì ?

+2

xem http://stackoverflow.com/a/1568230/985143 –

+1

trùng lặp chính xác @ZazGmy –

+4

Sự thật là [gs] etters là một gánh nặng lớn và bạn thực sự nên cân nhắc chi phí so với lợi ích trong từng trường hợp. Có những ví dụ mà các lĩnh vực công cộng (đặc biệt là nếu bạn có thể làm cho họ cuối cùng!) Là đúng cách. –

Trả lời

71

Nhiều lý do:

  • Nếu bạn cho phép truy cập lĩnh vực như

    shape.x = 90

thì bạn không thể thêm bất kỳ logic trong tương lai để xác nhận dữ liệu .

nói nếu x không thể ít hơn 100 bạn không thể làm điều đó, tuy nhiên nếu bạn có setters như

public void setShapeValue(int shapeValue){ 
    if(shapeValue < 100){ 
    //do something here like throw exception. 
    } 
} 
  • Bạn không thể thêm một cái gì đó giống như bản sao trên ghi logic (thấy CopyOnWriteArrayList)
  • Một lý do khác là để truy cập các trường bên ngoài lớp học của bạn, bạn sẽ phải đánh dấu chúng là công khai, được bảo vệ hoặc mặc định và do đó bạn mất quyền kiểm soát. Khi dữ liệu là rất nhiều nội bộ để phá vỡ lớp học Encapsulation và nói chung phương pháp OOPS.

Mặc dù cho hằng như

public final String SOMETHING = "SOMETHING"; 

bạn sẽ cho phép truy cập lĩnh vực như họ không thể thay đổi, cho biến Ví dụ bạn sẽ đặt chúng với thu khí, setters.

  • Một trường hợp khác là khi bạn muốn lớp của bạn không thay đổi, nếu bạn cho phép truy cập trường thì bạn đang phá vỡ tính bất biến của lớp học. Nhưng nếu bạn cẩn thận thiết kế lớp học của bạn với getters và không có setters bạn giữ nguyên immutability.

Mặc dù trong trường hợp này, bạn phải cẩn thận trong phương pháp getter để đảm bảo bạn không đưa ra tham chiếu đối tượng (trong trường hợp lớp của bạn có đối tượng là trường hợp).

Chúng tôi có thể sử dụng các biến riêng tư trong bất kỳ gói nào bằng getters và setters.

+0

"copy on write" không có ý nghĩa 99% số lần. Hãy tưởng tượng "sao chép trên văn bản" trên 'hình dạng.x' ... – Pacerier

+1

Không nên có thể refactor thành viên lĩnh vực công cộng thành tư nhân với getters/setters nếu cần một số logic khác bên trong getters/setters bao giờ phát sinh? Người định cư và getters dường như là một sự tối ưu hóa sớm của nghĩa đen "viết dòng mã sau". Lý do duy nhất để viết getters/setters ngay từ đầu là nếu bạn muốn làm cho giao diện này công khai với một số mã mà bạn không có quyền kiểm soát và không muốn phá vỡ tính tương thích ngược. – andrybak

+0

Sai. Bạn có thể xác thực dữ liệu bằng câu lệnh if trong mã chính hoặc một hàm xác nhận hợp lệ. – DarmaniLink

7

Sử dụng hàm getter và setter cho phép các ràng buộc và đóng gói. Giả sử x là bán kính. shape.x = -10 sẽ không có ý nghĩa nhiều. Ngoài ra, nếu ai đó cố đặt giá trị bất hợp pháp, bạn có thể in lỗi, đặt giá trị mặc định hoặc không làm gì cả.

Thực tiễn tốt là đặt biến thành viên ở chế độ riêng tư để chúng không thể sửa đổi trực tiếp bằng các chương trình sử dụng chúng.

Mutator functions
Encapsulation

1

Có rất nhiều lý do. Đây chỉ là một vài.

  1. Trình tiếp cận, getters nói riêng, thường xuất hiện trong giao diện. Bạn không thể quy định một biến thành viên trong một giao diện.
  2. Khi bạn vạch trần biến thành viên này, bạn không thể thay đổi ý định về cách nó được triển khai. Ví dụ: nếu bạn thấy nhu cầu sau đó chuyển sang mẫu như tổng hợp, nơi bạn muốn thuộc tính "x" thực sự xuất phát từ một số đối tượng lồng nhau, bạn phải sao chép giá trị đó và cố gắng đồng bộ hóa. Không tốt.
  3. Hầu hết thời gian bạn tốt hơn là tắt không phải để lộ trình thiết lập. Bạn không thể làm điều đó với các trường công khai như x.
0

Tôi sẽ nói rằng cả getters/setters lẫn các thành viên công cộng đều không có thiết kế hướng đối tượng tốt. Cả hai đều phá vỡ đóng gói OOP bằng cách phơi bày một dữ liệu đối tượng với thế giới mà có lẽ không nên truy cập vào các thuộc tính của đối tượng ở vị trí đầu tiên.

+0

Bạn nói đúng, đây là một ví dụ về đóng gói bằng thư, nhưng không phải là tinh thần. Tuy nhiên, bạn phải tính đến thực tế nơi chúng tôi thực sự chỉ cần cấu trúc đơn giản. Khi JavaBeans được định nghĩa, một phần không thể thiếu của thiết kế là 'PropertyChangeListener's - JavaBeans được thiết kế chủ yếu với Swing on mind. Nếu bạn muốn thay đổi thuộc tính quan sát được, bạn không thể thoát khỏi getters và setters. –

0

Điều này được thực hiện bằng cách áp dụng nguyên tắc encapsulation OOP.

Cơ chế ngôn ngữ để hạn chế quyền truy cập vào một số thành phần của đối tượng.

Điều này có nghĩa là bạn phải xác định mức độ hiển thị cho thuộc tính và phương pháp của lớp học. Có 3 mức độ phổ biến phổ biến:

  • Riêng tư: Chỉ lớp học mới có thể xem và sử dụng các thuộc tính/phương pháp.
  • Được bảo vệ: Chỉ lớp và con của nó mới có thể xem và sử dụng các thuộc tính/phương pháp.
  • Công khai: Mọi lớp đều có thể xem và sử dụng các thuộc tính/phương pháp.

Khi bạn khai báo thuộc tính riêng tư/được bảo vệ, bạn được khuyến khích tạo các phương thức để lấy giá trị (nhận) và thay đổi giá trị (bộ). Một ví dụ về khả năng hiển thị là lớp [ArrayList][2]: nó có thuộc tính size để biết kích thước thực tế của mảng bên trong.Chỉ có lớp phải thay đổi giá trị của nó, vì vậy mã là cái gì đó như

public class ArrayList<E> { 
    private int size; 
    private Object[] array; 
    public getSize() { 
     return this.size; 
    } 
    public void add(E element) { 
     //logic to add the element in the array... 
     this.size++; 
    } 
} 

Trong ví dụ này, bạn có thể thấy rằng giá trị kích thước có thể thay đổi chỉ trong các phương thức lớp, và bạn có thể nhận được kích thước thực tế của gọi nó trong mã của bạn (không biến đổi nó):

public void someMethod() { 
    List<String> ls = new ArrayList<String>(); 
    //adding values 
    ls.add("Hello"); 
    ls.add("World"); 
    for(int i = 0; i < ls.size(); i++) { 
     System.out.println(ls.get(i)); 
    } 
} 
0

Getters and setters đóng gói các trường của lớp bằng cách chỉ cho phép chúng truy cập thông qua các phương pháp công khai và giữ riêng tư các giá trị. Đó được coi là một nguyên tắc OO tốt.

Được cấp, nó thường có vẻ như mã dự phòng nếu nó không có gì hơn là đặt hoặc trả về một giá trị. Tuy nhiên, người định cư cũng cho phép bạn thực hiện xác thực hoặc dọn dẹp đầu vào. Có điều đó ở một nơi cải thiện tính toàn vẹn dữ liệu cho các đối tượng của bạn,

1

Trước khi nhận được câu trả lời, chúng ta phải biết điều gì đó trước ...! "JavaBeans".
JavaBeans là các lớp java có thuộc tính. Với mục đích của chúng ta, hãy nghĩ về các thuộc tính như các biến cá thể riêng. vì chúng là riêng tư, cách duy nhất chúng có thể được truy cập từ bên ngoài lớp học của chúng là thông qua 'phương thức' trong lớp.
Các phương pháp thay đổi giá trị của giá trị thích hợp được gọi là phương thức setter và các phương thức lấy giá trị của thuộc tính được gọi là phương thức getter.

0

Vì chúng tôi đang sử dụng ngôn ngữ lập trình hướng đối tượng. Ở đây chúng tôi đang sử dụng Ẩn dữ liệu và đóng gói. Biến không nên truy cập trực tiếp từ bên này thế giới ra (ví achiving dữ liệu ẩn) vì vậy chúng tôi sẽ tạo ra nó riêng để

shape.x

là không đúng. Phương pháp Getter và setter được sử dụng để lấy và thiết lập giá trị của x, đó là cách để đạt được đóng gói.

4

Một trong những lý do tốt nhất mà tôi có thể nghĩ đến cho getters và setters là tính lâu dài của API của lớp học. Trong các ngôn ngữ như python, bạn có thể truy cập các thành viên theo tên của chúng và chuyển chúng thành các phương thức sau này. Bởi vì các hàm hoạt động khác với các thành viên trong java khi bạn truy cập vào một thuộc tính thats it. Hạn chế phạm vi của nó sau đó phá vỡ khách hàng.

Bằng cách cung cấp getters và setters lập trình viên có thể linh hoạt sửa đổi thành viên và hành vi một cách tự do miễn là tuân theo hợp đồng được mô tả bởi API công khai.

3

Giả sử, theo giả thuyết, bạn tìm thấy một thư viện thực hiện công việc tốt hơn về những gì bạn đã làm trong lớp học của riêng bạn (YourClass). Điều tự nhiên cần làm vào thời điểm này là làm cho YourClass trở thành một giao diện trình bao bọc cho thư viện đó. Nó vẫn có một khái niệm "X" mà mã máy khách của bạn cần để có được hoặc thiết lập. Đương nhiên, tại thời điểm này bạn khá nhiều phải viết các hàm accessor.

Nếu bạn bỏ quên sử dụng chức năng truy cập và để mã khách hàng của bạn truy cập YourClass.x trực tiếp, bây giờ bạn sẽ phải viết lại tất cả mã máy khách của bạn đã chạm vào YourClass.x. Nhưng nếu bạn đang sử dụng YourClass.getX() và YourClass.setX() ngay từ đầu, bạn sẽ chỉ cần viết lại YourClass.

Một trong những khái niệm chính về lập trình và đặc biệt là lập trình hướng đối tượng, đang ẩn chi tiết triển khai để chúng không được sử dụng trực tiếp bằng mã trong các lớp hoặc mô-đun khác. Bằng cách này, nếu bạn đã từng thay đổi các chi tiết thực hiện (như trong ví dụ trên), mã máy khách không biết sự khác biệt và không cần phải sửa đổi. Đối với tất cả các mã khách hàng của bạn biết, "x" có thể là một biến, hoặc nó có thể là một giá trị được tính toán trên bay.

Đây là sự đơn giản hóa và không bao gồm tất cả các tình huống mà việc triển khai ẩn có lợi, nhưng đó là ví dụ rõ ràng nhất. Khái niệm ẩn chi tiết thực hiện là khá mạnh mẽ gắn liền với OOP bây giờ, nhưng bạn có thể tìm thấy các cuộc thảo luận của nó đi trở lại nhiều thập kỷ trước khi OOP đã được ước mơ lên. Nó trở lại với một trong những khái niệm cốt lõi của phát triển phần mềm, đó là lấy một vấn đề lớn, và phân chia nó thành các vấn đề nhỏ được xác định rõ có thể được giải quyết dễ dàng. Chức năng Accessor giúp giữ cho các tiểu nhiệm vụ của bạn riêng biệt và được xác định rõ ràng: Lớp học của bạn càng ít biết về nội bộ của nhau, thì càng tốt.

4

Một lý do tốt để sử dụng phương thức getter và setter có thể được hiểu bởi ví dụ sau

public class TestGetterSetter{ 
    private String name ; 


    public void setName(String name){ 
     this.name = name ; 
    } 


    public String getName(String name){ 
     return this.name ; 
    } 
} 

Mấu chốt của getter và setter là duy nhất mà họ có nghĩa là để được sử dụng để truy cập varialble tư nhân, mà họ nhận hoặc thiết lập. Bằng cách này, bạn cung cấp đóng gói và sẽ dễ dàng hơn để cấu trúc lại hoặc sửa đổi mã của bạn sau này.

Hãy tưởng tượng bạn sử dụng tên thay vì tên của nó. Sau đó, nếu bạn muốn thêm một cái gì đó như một mặc định (nói tên mặc định là 'Khách' nếu nó không được thiết lập trước đó), sau đó bạn sẽ phải sửa đổi cả getter và hàm sayName.

public class TestGetterSetter{ 
    private String name ; 


    public void setName(String name){ 
     this.name = name ; 
    } 


    public String getName(String name){ 
     if (this.name == null){ 
      setName("Guest"); 
     } 
     return this.name ; 
    } 
} 

Không yêu cầu getters và setter để bắt đầu nhận và đặt - chúng chỉ là các chức năng thành viên bình thường. Tuy nhiên đó là một quy ước để làm điều đó. (đặc biệt là nếu bạn sử dụng Hạt Java)

+5

Getters và setters là anathema tồi tệ nhất để tái cấu trúc! Hãy thử thay đổi 30 trong số 50 thuộc tính JavaBean của bạn từ các trường Chuỗi thành Ngày, ví dụ. –

3

Nguyên mẫu, mẫu getter/setter được tạo để quảng bá thiết kế hướng đối tượng tốt bằng encapsulating bên trong của class từ bên ngoài interface.

  • Ẩn các đại diện nội bộ của bất động sản

này là câu trả lời tốt nhất của câu hỏi của bạn Why use getters and setters?

6

Rất nhiều người đã đề cập đến đóng gói các chi tiết cụ thể của việc thực hiện, mà với tôi là lý do lớn nhất để sử dụng getters và setters trong một lớp học. Với điều này, bạn cũng nhận được rất nhiều lợi ích khác, bao gồm khả năng ném ra và thay thế việc triển khai trên một ý thích mà không cần phải chạm vào từng đoạn mã sử dụng lớp của bạn. Trong một dự án nhỏ, đó không phải là một lợi ích lớn, nhưng nếu mã của bạn kết thúc như một thư viện được sử dụng tốt (nội bộ hoặc công khai), nó có thể là một lợi ích lớn.

Một ví dụ cụ thể: số phức trong toán học. Một số ngôn ngữ có chúng như là một tính năng ngôn ngữ hoặc khung, những người khác thì không. Tôi sẽ sử dụng một lớp có thể thay đổi như một ví dụ ở đây, nhưng nó có thể dễ dàng thay đổi.

Một số phức có thể được viết trên biểu mẫu a + bi với các bộ phận thực và ảo, tự cho vay tốt với [gs]etRealPart[gs]etImaginaryPart.

Tuy nhiên, trong một số trường hợp, lý do dễ dàng hơn về số phức trên dạng cực re^(iθ), cho [gs]etRadius (r) và [gs]etAngle (θ).

Bạn cũng có thể hiển thị các phương pháp như [gs]etComplexNumber(realPart, imaginaryPart)[gs]etComplexNumber(radius, angle). Tùy thuộc vào loại đối số có thể hoặc không cần tên khác nhau, nhưng sau đó người tiêu dùng của lớp có thể sử dụng hoặc là phù hợp với nhu cầu của nó.

Hai biểu mẫu có thể hoán đổi cho nhau; bạn có thể dễ dàng chuyển đổi từ cái này sang cái khác, vì vậy hình thức mà lớp sử dụng cho bộ nhớ trong là không liên quan đến người tiêu dùng của lớp đó. Tuy nhiên, người tiêu dùng có thể sử dụng một trong hai hình thức. Nếu bạn chọn dạng + bi cho biểu diễn nội bộ, và cho biết sử dụng trường thay vì getters và setters, bạn không chỉ ép buộc người tiêu dùng lớp sử dụng biểu mẫu đó, bạn cũng không thể dễ dàng thay đổi ý định và thay thế nội bộ biểu diễn với^(iθ) vì điều đó hóa ra sẽ dễ thực hiện hơn trong kịch bản cụ thể của bạn. Bạn đang mắc kẹt với các API công cộng mà bạn đã xác định, trong đó yêu cầu cụ thể là các phần thực và tưởng tượng được tiếp xúc bằng cách sử dụng tên trường cụ thể.

+0

Điều này cũng áp dụng cho những thứ như đơn vị đo lường hoặc tọa độ tương đối so với tương đối. –