Khi nào Getters và Setters được chứng minh


175

Getters và setters thường bị chỉ trích là không đúng OO. Mặt khác, hầu hết các mã OO mà tôi đã thấy có nhiều getters và setters.

Khi nào getters và setters hợp lý? Bạn có cố gắng tránh sử dụng chúng? Có phải họ đang sử dụng quá mức nói chung?

Nếu ngôn ngữ yêu thích của bạn có thuộc tính (của tôi) thì những thứ như vậy cũng được coi là getters và setters cho câu hỏi này. Chúng là những điều tương tự từ quan điểm phương pháp luận OO. Họ chỉ có cú pháp đẹp hơn.

Nguồn cho phê bình Getter / Setter (một số lấy từ các bình luận để cho họ thấy rõ hơn):

Để chỉ ra những lời chỉ trích đơn giản: Getters và Setters cho phép bạn thao tác trạng thái bên trong của các đối tượng từ bên ngoài đối tượng. Điều này vi phạm đóng gói. Chỉ bản thân đối tượng nên quan tâm đến trạng thái bên trong của nó.

Và một ví dụ phiên bản thủ tục của mã.

struct Fridge
{
    int cheese;
}

void go_shopping(Fridge fridge)
{
     fridge.cheese += 5;
}

Phiên bản mã biến đổi:

class Fridge
{
     int cheese;

     void set_cheese(int _cheese) { cheese = _cheese; }
     int get_cheese() { return cheese; }
 }

void go_shopping(Fridge fridge)
{
     fridge.set_cheese(fridge.get_cheese() + 5);        
}

Các getters và setters làm cho mã phức tạp hơn nhiều mà không cần đóng gói đúng. Bởi vì trạng thái bên trong có thể truy cập được đối với các đối tượng khác, chúng tôi không thu được nhiều bằng cách thêm các getters và setters này.

Câu hỏi đã được thảo luận trước đây về Stack Overflow:


21
Getters and setters are often criticized as being not proper OO- Xin trích dẫn.
Robert Harvey


45
@Job, tại sao vì nó có mã? Đó là một câu hỏi hay và những câu chuyện hay về cuộc chiến thánh nếu được thực hiện nghiêm túc.
Peter Turner

2
@Winston Ewert: "... câu hỏi là liệu bạn có nên truy cập dữ liệu bên trong lớp không.": Tốt, không ai bắt bạn phải thực hiện getters và setters cho tất cả các biến thành viên, bạn chỉ thực hiện những thứ bạn cần. Đó là lý do tại sao bạn sử dụng getters và setters thay vì các biến thành viên công cộng.
Giorgio

2
@Winston Ewert: Tôi không thấy sự vắng mặt của getters / setters sẽ giải quyết vấn đề này như thế nào: phải có cách để truy cập mọi phần dữ liệu nếu không nó là vô ích; nhiệm vụ của một thiết kế tốt là quyết định phần nào của dữ liệu có thể truy cập được với phần nào của mã. Nếu một người là một nhà thiết kế tồi, anh ta hoặc cô ta sẽ như vậy có hoặc không có getters và setters. Chỉ 2 xu của tôi.
Giorgio

Câu trả lời:


162

Có getters và setters không tự nó phá vỡ đóng gói. Những gì không đóng gói đóng gói là tự động thêm một getter và setter cho mọi thành viên dữ liệu (mọi trường , trong biệt ngữ java), mà không cần suy nghĩ gì. Trong khi điều này tốt hơn là làm cho tất cả các thành viên dữ liệu được công khai, nó chỉ là một bước nhỏ.

Điểm đóng gói không phải là bạn không thể biết hoặc thay đổi trạng thái của đối tượng từ bên ngoài đối tượng, mà là bạn nên có chính sách hợp lý để thực hiện.

  • Một số thành viên dữ liệu có thể hoàn toàn bên trong đối tượng và không nên có getters hay setters.

  • Một số thành viên dữ liệu nên ở chế độ chỉ đọc, vì vậy họ có thể cần getters nhưng không phải setters.

  • Một số thành viên dữ liệu có thể cần phải được giữ phù hợp với nhau. Trong trường hợp như vậy, bạn sẽ không cung cấp một setter cho mỗi cái, nhưng một phương thức duy nhất để đặt chúng cùng một lúc, để bạn có thể kiểm tra tính nhất quán của các giá trị.

  • Một số thành viên dữ liệu có thể chỉ cần được thay đổi theo một cách nhất định, chẳng hạn như tăng hoặc giảm theo một số tiền cố định. Trong trường hợp này, bạn sẽ cung cấp một increment()và / hoặc decrement()phương thức, thay vì setter.

  • Tuy nhiên, những người khác thực sự có thể cần phải đọc-ghi, và sẽ có cả getter và setter.

Hãy xem xét một ví dụ về a class Person. Giả sử một người có tên, số an sinh xã hội và tuổi. Hãy nói rằng chúng tôi không cho phép mọi người thay đổi tên hoặc số an sinh xã hội. Tuy nhiên, tuổi của người đó nên được tăng thêm 1 mỗi năm. Trong trường hợp này, bạn sẽ cung cấp một hàm tạo khởi tạo tên và SSN cho các giá trị đã cho và sẽ khởi tạo tuổi thành 0. Bạn cũng sẽ cung cấp một phương thức incrementAge(), sẽ tăng tuổi lên 1. Bạn cũng sẽ cung cấp getters cho cả ba. Không có setters được yêu cầu trong trường hợp này.

Trong thiết kế này, bạn cho phép trạng thái của đối tượng được kiểm tra từ bên ngoài lớp và bạn cho phép nó được thay đổi từ bên ngoài lớp. Tuy nhiên, bạn không cho phép thay đổi trạng thái tùy ý. Có một chính sách, trong đó nêu rõ rằng tên và SSN hoàn toàn không thể thay đổi và tuổi có thể tăng thêm 1 năm một lần.

Bây giờ hãy nói rằng một người cũng có một mức lương. Và mọi người có thể thay đổi công việc theo ý muốn, có nghĩa là tiền lương của họ cũng sẽ thay đổi. Để mô hình hóa tình huống này, chúng tôi không có cách nào khác ngoài việc cung cấp một setSalary()phương pháp! Cho phép mức lương được thay đổi theo ý muốn là một chính sách hoàn toàn hợp lý trong trường hợp này.

Bằng cách này, trong ví dụ của bạn, tôi sẽ cung cấp cho lớp Fridgecác putCheese()takeCheese()phương pháp, thay vì get_cheese()set_cheese(). Sau đó, bạn vẫn sẽ đóng gói.


public class Fridge {
  private List objects;
  private Date warranty;

  /** How the warranty is stored internally is a detail. */
  public Fridge( Date warranty ) {
    // The Fridge can set its internal warranty, but it is not re-exposed.
    setWarranty( warranty );
  }

  /** Doesn't expose how the fridge knows it is empty. */
  public boolean isEmpty() {
    return getObjects().isEmpty();
  }

  /** When the fridge has no more room... */
  public boolean isFull() {
  }

  /** Answers whether the given object will fit. */
  public boolean canStore( Object o ) {
    boolean result = false;

    // Clients may not ask how much room remains in the fridge.
    if( o instanceof PhysicalObject ) {
      PhysicalObject po = (PhysicalObject)o;

      // How the fridge determines its remaining usable volume is a detail.
      // How a physical object determines whether it fits within a specified
      // volume is also a detail.
      result = po.isEnclosedBy( getUsableVolume() );
    }

     return result;
  }

  /** Doesn't expose how the fridge knows its warranty has expired. */
  public boolean isPastWarranty() {
    return getWarranty().before( new Date() );
  }

  /** Doesn't expose how objects are stored in the fridge. */
  public synchronized void store( Object o ) {
    validateExpiration( o );

    // Can the object fit?
    if( canStore( o ) ) {
      getObjects().add( o );
    }
    else {
      throw FridgeFullException( o );
    }
  }

  /** Doesn't expose how objects are removed from the fridge. */
  public synchronized void remove( Object o ) {
    if( !getObjects().contains( o ) ) {
      throw new ObjectNotFoundException( o );
    }

    getObjects().remove( o );

    validateExpiration( o );
  }

  /** Lazily initialized list, an implementation detail. */
  private synchronized List getObjects() {
    if( this.list == null ) { this.list = new List(); }
    return this.list;
  }

  /** How object expiration is determined is also a detail. */
  private void validateExpiration( Object o ) {
    // Objects can answer whether they have gone past a given
    // expiration date. How each object "knows" it has expired
    // is a detail. The Fridge might use a scanner and
    // items might have embedded RFID chips. It's a detail hidden
    // by proper encapsulation.
    if( o implements Expires && ((Expires)o).expiresBefore( today ) ) {
      throw new ExpiredObjectException( o );
    }
  }

  /** This creates a copy of the warranty for immutability purposes. */
  private void setWarranty( Date warranty ) {
    assert warranty != null;
    this.warranty = new Date( warranty.getTime() )
  }
}

4
Xin đừng quá coi trọng ví dụ Tủ lạnh của tôi. :) Một tủ lạnh thực sự phải là một vật chứa, tôi mong nó biết cách giữ đồ vật mà không cần lo lắng chính xác chúng là gì. IE nó sẽ giống như một ArrayList. Đối với những gì làm cho nó trở thành một Tủ lạnh, giả sử nó tuần tự hóa các đối tượng vào đĩa khi được lưu trữ để chúng tồn tại khi hệ thống bị lỗi. Đồng ý. Đủ lấy ví dụ về tủ lạnh của tôi.
Winston Ewert

6
Nhưng không nên để tủ lạnh biết ngày hết hạn, để nó có thể cho bạn biết rằng phô mai đã bị hỏng và nên vứt đi? :) Ok, chúng ta phải dừng điều này! :)
Dima

10
Khá nhiều cuộc thảo luận thú vị về Logic tủ lạnh mà bạn đã tham gia ở đây ...
Mason Wheeler

35
Haha, tôi muốn xem quảng cáo về điều đó: "Đó là một loại tủ lạnh hoàn toàn mới: Nó ném mọi thứ vào bạn khi cố lấy thứ gì đó không có ở đó! Cách này bạn sẽ chỉ thử một lần! Bạn lo lắng về nhét tủ lạnh và để tủ lạnh lo lắng về hành vi bất hợp pháp! "
gablin

42
Ví dụ có tất cả sai. Không nên có trường Age cũng như phương thức setAge (). Tuổi là một chức năng của Người sinh ngày so với một số thời điểm. Mặc dù có vẻ tầm thường, nhưng đây chính xác là điều không đúng với thực tiễn hiện đại về khả năng biến đổi hoàn toàn của các đối tượng và các thiết kế xấu khác, như được thấy bằng các phương thức get / set cho các trường riêng so với xem xét cẩn thận những gì thực sự có thể thay đổi, một lĩnh vực, đối tượng là gì nên biết về hành vi của nó và những thay đổi trạng thái nào thực sự hợp lệ (phương thức setX nào phá hủy hoàn toàn).
Darrell Teague

41

Lý do cơ bản cho getters và setters trong Java rất đơn giản:

  • Bạn chỉ có thể chỉ định các phương thức , không phải các trường, trong một giao diện.

Do đó, nếu bạn muốn cho phép một trường đi qua giao diện, bạn sẽ cần một trình đọc và phương thức nhà văn. Chúng thường được gọi là getX và setX cho trường x.


44
Đó là một giới hạn ngôn ngữ. Câu hỏi thực sự là liệu chúng ta có nên cho phép các đối tượng khác thao túng trạng thái đó hay không.
Winston Ewert

3
@Peter Turner, rõ ràng không có gì ngăn cản một ngôn ngữ có giao diện bao gồm các thuộc tính. Trong các trang bìa có thể được triển khai như getter / setter nhưng sẽ đủ dễ dàng để thêm hỗ trợ cho các định nghĩa giao diện cho các thuộc tính.
Winston Ewert

2
@Winston, cuối cùng bạn sẽ cần phải cho phép các lớp truyền thông tin qua lại với nhau để thực sự hoàn thành công việc. Bạn sẽ đề nghị gì thay thế?

6
Một đối tượng được cho là cung cấp giao diện cấp cao hơn cho nó. Getters và Setters có xu hướng là một giao diện cấp thấp. Ví dụ, giả sử bạn có một lớp thực hiện cây nhị phân. Bạn có thể có các hàm như GetLeft (), GetRight (), GetValue () và GetKey () để điều hướng cây. Nhưng đó là cách tiếp cận hoàn toàn sai lầm. Lớp cây nhị phân của bạn sẽ cung cấp các hoạt động như Tìm, Chèn, Xóa.
Winston Ewert

6
Để lấy một ví dụ khác, hãy xem xét một mảnh tetris. Các mảnh tetris có một số trạng thái nội bộ như block_type, xoay, vị trí. Bạn có thể có các getters như GetBlockType (), GetRotation (), GetPocation (). Nhưng bạn thực sự không nên. Những gì bạn nên có là một phương thức GetSquares () trả về một danh sách tất cả các hình vuông được chiếm bởi mảnh này. Bạn cũng không nên có những thứ như SetP vị trí () hoặc SetRotation (). Thay vào đó, bạn nên có các hoạt động như MoveLeft (), MoveRight (), Rotate (), Fall ().
Winston Ewert

20

Từ http://www.adam-bien.com/cont/abien/entry/encapsulation_violation_with_getters_and

Kiểu JavaBean:

connection.setUser("dukie");
connection.setPwd("duke");
connection.initialize();

Kiểu OO:

connection.connect("dukie","duke");

Vâng, rõ ràng tôi thích cách tiếp cận sau; nó không làm hỏng chi tiết triển khai, nó đơn giản và ngắn gọn hơn, và tất cả các thông tin cần thiết đều được bao gồm trong lệnh gọi phương thức, vì vậy sẽ dễ dàng hơn để làm cho đúng. Tôi cũng thích thiết lập các thành viên riêng bằng cách sử dụng các tham số trong hàm tạo, bất cứ khi nào có thể.

Câu hỏi của bạn là, khi nào một getter / setter hợp lý? Có lẽ khi cần thay đổi chế độ, hoặc bạn cần thẩm vấn một đối tượng để biết một số thông tin.

myObject.GetStatus();
myObject.SomeCapabilitySwitch = true;

Nghĩ về nó, khi tôi mới bắt đầu viết mã bằng C #, tôi đã viết rất nhiều mã theo kiểu Javabeans được minh họa ở trên. Nhưng khi tôi có được kinh nghiệm về ngôn ngữ, tôi bắt đầu thực hiện nhiều cài đặt thành viên hơn trong hàm tạo và sử dụng các phương thức trông giống kiểu OO ở trên hơn.


6
Tại sao kiểu "cung cấp tất cả các đối số là tham số" theo kiểu OO?

5
Mặc dù "kiểu OO" của bạn phù hợp với 2 hoặc 3 tùy chọn, tôi sẽ thích kiểu JavaBean hơn khi bạn vượt lên trên đó. Có 20 phương thức quá tải cho mọi kết hợp tham số có thể có vẻ khủng khiếp
TheLQ

1
@TheLQ: C # 4.0 làm cho điều này dễ dàng hơn bằng cách cho phép các tham số tùy chọn và được đặt tên trong các lệnh gọi phương thức và phương thức.
Robert Harvey

7
@TheLQ Đó là những gì Builder với cú pháp lưu loát dành cho, xem ví dụ MapMaker của Guava .
maaartinus

5
@ Thorbjørn Raven Việc sử dụng phương thức kết nối (..) trong ví dụ này không đưa ra giả định nào như vậy; có thể bạn đặt thuộc tính người dùng và mật khẩu, có thể bạn không. Điều quan trọng là bạn tập trung vào khái niệm OO đúng: thông báo "kết nối".
Andres F.

13

Khi nào getters và setters hợp lý?

Khi hành vi "có được" và "thiết lập" thực sự khớp với hành vi trong mô hình của bạn, điều này thực tế không bao giờ .

Mọi mục đích sử dụng khác chỉ là một mánh gian lận vì hành vi của lĩnh vực kinh doanh không rõ ràng.

Biên tập

Câu trả lời đó có thể đã xuất hiện dưới dạng flippant, vì vậy hãy để tôi mở rộng. Các câu trả lời ở trên hầu hết là đúng nhưng tập trung vào các mô hình lập trình của thiết kế OO và một số điều bỏ lỡ bức tranh lớn. Theo kinh nghiệm của tôi, điều này khiến mọi người nghĩ rằng việc tránh cài đặt và setters là một số quy tắc học thuật đối với các ngôn ngữ lập trình OO (ví dụ: mọi người nghĩ thay thế getters bằng các thuộc tính là lớn)

Trong thực tế, nó là một hệ quả của quá trình thiết kế. Bạn không cần phải tham gia vào các giao diện và đóng gói và các mẫu, tranh luận về việc liệu nó có hay không phá vỡ các mô hình này và chương trình OO tốt hay không. Điểm duy nhất quan trọng cuối cùng là nếu không có gì trong miền của bạn hoạt động như thế này bằng cách đặt chúng vào thì bạn không mô hình hóa miền của mình.

Thực tế là rất khó có hệ thống nào trong không gian tên miền của bạn có getters và setters. Bạn không thể đến gặp người đàn ông hoặc phụ nữ chịu trách nhiệm về bảng lương và chỉ cần nói "Đặt mức lương này thành X" hoặc "Nhận cho tôi mức lương này" . Hành vi như vậy đơn giản là không tồn tại

Nếu bạn đặt mã này vào mã của mình, bạn không thiết kế hệ thống của mình để phù hợp với mô hình miền của bạn. Vâng, phá vỡ giao diện và đóng gói, nhưng đó không phải là vấn đề. Vấn đề là bạn đang mô hình hóa một cái gì đó không tồn tại.

Nhiều hơn bạn có thể đang thiếu một bước hoặc quy trình quan trọng, bởi vì có lẽ có một lý do tôi không thể đi lên để trả cuộn và nói đặt mức lương này thành X.

Khi mọi người sử dụng getters và setters họ có xu hướng đẩy các quy tắc cho quá trình này vào sai vị trí. Điều này đang di chuyển ra khỏi miền của bạn thậm chí nhiều hơn. Sử dụng ví dụ trong thế giới thực, nó giống như tiền lương giả định rằng người ngẫu nhiên bước vào có quyền nhận được các giá trị này nếu không anh ta sẽ không yêu cầu họ. Không chỉ là không phải tên miền như thế nào, mà trên thực tế còn nói dối về cách thức tên miền.


điều này dường như không cung cấp bất cứ điều gì đáng kể qua các điểm được thực hiện và giải thích trong 17 câu trả lời trước
gnat

1
Các câu trả lời khác nói về các đối tượng, giao diện và các mô hình ngôn ngữ khác. Điều này là tốt, nhưng nó không phải là vấn đề trung tâm. Getters và setters không chỉ xấu bởi vì chúng phá vỡ một số quy ước mã hóa, mà chủ yếu là vì bạn hầu như không gặp phải hành vi này trong mô hình thực tế mà bạn đang đại diện. Điểm này có xu hướng bị lạc trong các cuộc thảo luận về các thực hành mã hóa tốt nhất.
Cormac Mulhall

Bạn nghĩ giao diện của bảng lương là gì nếu không phải là set/gettiền lương?
Winston Ewert

1
Nếu chúng được bao bọc xung quanh các lớp ủy quyền và bị giới hạn ở một số thực thể bên ngoài nhất định thì chúng không phải là getters và setters. Tôi không nói rằng không có bất kỳ phương pháp nào trong bộ phận biên chế thay đổi mức lương, nhưng phương pháp đó sẽ phù hợp với các quy trình nội bộ được áp dụng trong chính công ty. Ví dụ, hầu hết các công ty đều thiết lập mức lương dựa trên hợp đồng nhân viên, được ủy quyền bởi người quản lý. Nếu lương của nhân viên thay đổi, một hợp đồng mới sẽ được soạn thảo và người quản lý phải ký vào đó. Do đó, biên chế nên mong đợi một đối tượng hợp đồng và một số đối tượng ủy quyền thay đổi cuộn lương
Cormac Mulhall

3
Hoặc nói cách khác, có khá nhiều sự khác biệt giữa set_salary (new_salary, worker_id) và ủy quyền_salary_update (worker_contract, Authorising_manager). Quá trình nên mô hình hóa quy trình kinh doanh nội bộ
Cormac Mulhall

11

Theo nguyên tắc chung, getters và setters là một ý tưởng tồi. Nếu một trường không phải là một phần logic của giao diện và bạn đặt nó ở chế độ riêng tư, điều đó tốt. Nếu nó là một phần logic của giao diện và bạn công khai nó, điều đó tốt. Nhưng nếu bạn đặt nó ở chế độ riêng tư và sau đó quay lại và làm cho nó trở nên công khai một cách hiệu quả bằng cách cung cấp một getter và setter, bạn sẽ quay trở lại nơi bạn đã bắt đầu, ngoại trừ mã của bạn giờ dài hơn và bị xáo trộn.

Rõ ràng, có những ngoại lệ. Trong Java, bạn có thể cần sử dụng các giao diện. Thư viện tiêu chuẩn Java có các yêu cầu tương thích ngược quá mức để vượt xa các biện pháp thông thường về chất lượng mã. Thậm chí có khả năng bạn thực sự có thể xử lý trường hợp huyền thoại nhưng hiếm gặp trong trường hợp có khả năng sau này bạn có thể thay thế một trường được lưu trữ bằng phép tính nhanh mà không làm hỏng giao diện. Nhưng đây là những ngoại lệ. Getters và setters là một mô hình chống cần sự biện minh đặc biệt.


6
Một getter và setter có nghĩa là thuộc tính, không phải trường, là một phần của giao diện. Giả sử getBirthDate () và SetBirthDate () lấy "yyyy / mm / dd" nhưng trường được lưu trữ là số ngày kể từ ngày 01/01/1000. Bạn có thể thay đổi thành 01/01/0001 và getter và setter sẽ giữ giao diện giống nhau. Đặc biệt vì 'công khai' có nghĩa là rác công khai, các biến không bao giờ được công khai; luôn luôn sử dụng một setter để xác thực giá trị đến, cập nhật mọi trường liên quan, chuyển đổi khi cần, v.v ... Hãy thử sử dụng một getter trong trường hợp bộ nhớ trong thay đổi.
Andy Canfield

@AndyCanfield thật mạnh mẽ khi nói rằng công khai có nghĩa là rác rưởi công khai..Đó chỉ vì vậy nếu bạn lập trình nó không tốt. Nếu bạn có một setter và giá trị sai được gửi tới nó thì bạn có thể đưa ra một ngoại lệ mà bạn có thể gọi là hủy chương trình nhưng nó sẽ thoát khỏi một lỗi. Nếu người dùng của đối tượng có thể đưa ra một giá trị và cud cho nó sai Giá trị sau đó bạn có thể chịu đựng điều đó và kiểm tra nó. Bạn có thể nói "ah nhưng tôi chỉ phải thực hiện 1 thử nghiệm nếu tôi sử dụng một setter", nhưng có thể mã của chính bạn cung cấp cho biến đó một giá trị xấu..như bộ setter có thể không giảm các bài kiểm tra của bạn.n bằng các bài kiểm tra, prog của bạn không bị "
vứt

3

cho dù trường có thể truy cập trực tiếp hoặc thông qua phương thức không thực sự quan trọng.

Lớp bất biến (những cái hữu ích) rất quan trọng. Và để bảo tồn chúng, đôi khi chúng ta cần không thể thay đổi một cái gì đó từ bên ngoài. Ví dụ. nếu chúng ta có lớp Square với chiều rộng và chiều cao tách biệt, việc thay đổi một trong số chúng làm cho nó trở thành một cái gì đó khác với hình vuông. Vì vậy, chúng ta cần thay đổi phương thức Nếu đó là Hình chữ nhật, chúng ta có thể có setters / trường công khai. Nhưng setter hơn sẽ kiểm tra xem nó lớn hơn 0 sẽ tốt hơn.

Và trong các ngôn ngữ cụ thể (ví dụ: java) là lý do tại sao chúng ta cần những phương thức (giao diện) đó. Và một lý do khác cho phương pháp là khả năng tương thích (nguồn và nhị phân). Vì vậy, dễ dàng hơn để thêm chúng sau đó suy nghĩ liệu lĩnh vực công cộng sẽ đủ.

btw. Tôi thích sử dụng các lớp giữ giá trị bất biến đơn giản với các trường cuối cùng công khai.


Đây là khá nhiều điều duy nhất nên tránh. Đi từ trường đến thuộc tính / getter / setter là một thay đổi đột phá trong hầu hết các ngôn ngữ.
MrDosu

2

Bạn có thể muốn thay đổi nội bộ của mình thành bất cứ điều gì trong khi vẫn giữ các giao diện giống nhau. Nếu giao diện của bạn không thay đổi, chúng sẽ không bị hỏng. Bạn vẫn có thể thay đổi nội bộ của bạn như bạn muốn.


1
Tôi ngạc nhiên câu trả lời này là rất xa. Getters và setters cho phép bạn làm nhiều hơn trong tương lai (ví dụ: tổ chức một sự kiện, xác thực đầu vào nhiều hơn, thực hiện ghi sổ nội bộ) mà không phá vỡ API hiện tại của bạn. Ngay cả với mã không công khai, càng ít API bạn phải thay đổi, càng ít phụ thuộc để cập nhật và càng ít lỗi được đưa ra khi thực hiện các cập nhật đó.
AmadeusDrZaius

0

Cách tiếp cận của tôi là thế này -

Khi tôi dự kiến ​​sẽ xử lý dữ liệu sau này, getter / setter là hợp lý. Ngoài ra, nếu thay đổi đang xảy ra, tôi thường đẩy dữ liệu vào getter / setter.

Nếu đó là cấu trúc POD, tôi để các vị trí có sẵn.

Ở mức độ trừu tượng hơn, câu hỏi là "ai quản lý dữ liệu" và điều đó phụ thuộc vào dự án.


0

Nếu sử dụng getters và setters cảm thấy phức tạp, vấn đề có thể là ngôn ngữ, không phải là khái niệm.

Đây là mã từ ví dụ thứ hai được viết bằng Ruby:

class Fridge
  attr_accessor :cheese
end

def go_shopping fridge
  fridge.cheese += 5
end

Lưu ý rằng nó trông rất giống ví dụ đầu tiên trong Java? Khi getters và setters được coi là công dân hạng nhất, chúng không phải là một việc vặt để sử dụng và tính linh hoạt được thêm vào đôi khi có thể là một lợi ích thực sự - ví dụ, chúng tôi có thể quyết định trả lại giá trị mặc định cho phô mai trên tủ lạnh mới:

class Fridge
  attr_accessor :cheese

  def cheese
    @cheese || 0
  end
end

Tất nhiên, sẽ có nhiều biến không nên phơi bày công khai. Việc phơi bày các biến nội bộ không cần thiết sẽ làm cho mã của bạn trở nên tồi tệ hơn, nhưng bạn khó có thể đổ lỗi cho điều đó trên getters và setters.


Đó là tuyên bố đẹp. Các chức năng của trình tạo đã được thêm vào vì chúng tôi muốn tạo các đối tượng bất biến trong khi dần dần xây dựng công thức: có thể nếu bạn thêm chất làm ngọt nhân tạo, bạn không cần thêm đường. Sử dụng các tham số được đặt tên và thậm chí với quá tải, rất khó để xây dựng một phương thức làm tất cả. Và dĩ nhiên, java không có các tham số được đặt tên, do đó, một lý do nữa khiến mọi người sử dụng mẫu Builder.
YoYo

0

Hãy xem xét một Sizelớp đóng gói chiều rộng và chiều cao. Tôi có thể loại bỏ setters bằng cách sử dụng hàm tạo nhưng làm thế nào để giúp tôi vẽ một hình chữ nhật với Size? Chiều rộng và chiều cao không phải là dữ liệu nội bộ cho lớp; chúng được chia sẻ dữ liệu phải có sẵn cho người tiêu dùng Kích thước.

Đối tượng bao gồm các hành vi và trạng thái - hoặc thuộc tính. Nếu không có trạng thái tiếp xúc thì chỉ có các hành vi là công khai.

Không có nhà nước, làm thế nào bạn sắp xếp một bộ sưu tập các đối tượng? Làm thế nào bạn sẽ tìm kiếm một ví dụ cụ thể của một đối tượng? Nếu bạn chỉ sử dụng các hàm tạo, điều gì xảy ra khi đối tượng của bạn có một danh sách dài các thuộc tính?

Không có tham số phương thức nào được sử dụng mà không cần xác nhận. Vì vậy, đó là sự lười biếng để viết:

setMyField(int myField){
    this.myField = myField;
}

Nếu bạn viết nó theo cách đó thì ít nhất bạn đã chuẩn bị cho việc sử dụng setter để xác nhận; nó tốt hơn một lĩnh vực công cộng - nhưng chỉ vừa đủ. Nhưng ít nhất bạn có một giao diện công cộng nhất quán mà bạn có thể quay lại và đưa vào các quy tắc xác thực mà không vi phạm mã của khách hàng.

Getters, setters, property, mutators, gọi chúng là những gì bạn sẽ, nhưng chúng là cần thiết.


Nhưng ít nhất bạn có một giao diện công cộng nhất quán mà bạn có thể quay lại và đưa vào các quy tắc xác thực mà không vi phạm mã của khách hàng. Đo không phải sự thật. Thêm các quy tắc xác nhận xa hơn rất có thể "phá vỡ mã của khách hàng của bạn". Lấy một intví dụ, ví dụ, và tưởng tượng rằng bạn quyết định thêm quy tắc xác thực để chỉ chấp nhận các giá trị không âm. Vấn đề là mã khách hàng của bạn có thể đã dựa vào khả năng đặt mã thành giá trị âm ...
jub0bs

0

Nếu getters và setters vi phạm đóng gói và OO thực sự, thì tôi thực sự gặp rắc rối.

Tôi luôn cảm thấy một đối tượng đại diện cho bất cứ điều gì tốt nhất đến với tâm trí mà bạn cần nó để làm.

Tôi vừa viết xong một chương trình tạo Mazes bằng Java và tôi có lớp đại diện cho "Maze Squares". Tôi có dữ liệu trong lớp này đại diện cho tọa độ, tường và booleans, v.v.

Tôi phải có một số cách để thay đổi / thao tác / truy cập dữ liệu này! Tôi phải làm gì khi không có getters và setters? Java không sử dụng các thuộc tính và đặt tất cả dữ liệu của tôi là cục bộ cho lớp này thành công khai là HOÀN TOÀN vi phạm đóng gói và OO.


6
Đây là câu hỏi: Nếu bạn đặt tất cả các trường đó thành công khai và ngừng sử dụng getters và setters thì mã của bạn sẽ khác như thế nào?
Winston Ewert

Về lý thuyết thì không. Tại sao thậm chí làm một lớp học tại thời điểm đó? Lập luận tương tự có thể được thực hiện với bất cứ điều gì. Nhưng bài viết dưới đây của tôi dường như đã nói nó thanh lịch hơn. (Getters và setters là một cách để trao một giá trị một cách an toàn cho một đối tượng hoặc lấy một giá trị từ đối tượng đó.) Bài đăng tiếp tục ..
Bryan Harrington

3
Tôi chỉ ra rằng: cuối cùng poster đó đã đồng ý với tôi và đăng một câu trả lời khác cho hiệu ứng đó. Về cơ bản, sử dụng getters và setters cũng giống như thao tác trực tiếp với dữ liệu. Bạn không thực sự đạt được OOness bằng cách làm điều đó. Để trở thành OO, bạn phải cung cấp giao diện cấp cao hơn cho dữ liệu.
Winston Ewert

4
Tôi nghĩ rằng cách tốt nhất để mô tả nó sẽ là để nói rằng bạn nên thiết kế giao diện của bạn từ bên ngoài vào chứ không phải từ trong ra ngoài. Giao diện mà đối tượng của bạn cung cấp phải được quyết định bởi cách đối tượng được sử dụng chứ không phải cách nó được thực hiện. Vì vậy, đừng viết getters và cho phép đối tượng bên ngoài để giải thích dữ liệu. Tìm ra câu hỏi họ cần trả lời, và viết một phương pháp trả lời câu hỏi đó. Đừng cho phép các đối tượng bên ngoài sửa đổi trạng thái bằng một setter, tìm hiểu loại thao tác nào chúng cần để thực hiện và viết các phương thức thực hiện điều đó.
Winston Ewert

2
Trong một số trường hợp, Getters và Setters là phương pháp tốt nhất. Đôi khi chúng là giao diện tốt nhất bạn có thể cung cấp. Tuy nhiên, trong nhiều trường hợp thì không. Trong nhiều trường hợp, bạn chỉ rò rỉ chi tiết triển khai vào các lớp khác.
Winston Ewert

-1

Đầu tiên, có hai loại đối tượng mà tôi sẽ bình luận, loại giá trị và loại dịch vụ.

Các loại dịch vụ không bao giờ nên có setters, bất kỳ phụ thuộc nào chúng yêu cầu không nên có được. Cách tốt nhất để vượt qua sự phụ thuộc là thông qua một nhà xây dựng hoặc nhà máy theo cách mà tất cả các trường hợp được hình thành đầy đủ ngay từ đầu, đơn giản và đơn giản.

Các loại giá trị cũng không thay đổi, mặt khác, đôi khi điều này không thực tế như ORM hoặc một số ánh xạ khác, chẳng hạn như từ một widget đến một đối tượng. Tất cả các loại giá trị khác được truyền xung quanh hệ thống từ lớp này sang lớp khác nên không thay đổi và không nên có setters.


Tôi muốn đề xuất một loại thứ ba: một thùng chứa có thể thay đổi, với mục đích là giữ một hoặc nhiều giá trị; trong nhiều trường hợp, một container không nên được dự kiến ​​sẽ có nhiều thứ theo cách logic hoặc xác nhận. Mã cần một phương pháp để tính sáu số nguyên dương có thể chuyển một bộ chứa cho sáu số nguyên cho phương thức sẽ lưu trữ kết quả trong đó. Trách nhiệm đảm bảo các số là dương thường nằm trong cả người gọi hoặc phương thức được gọi, không phải với vùng chứa.
supercat

-1

Một Getter / Setter là hợp lý giống như bất kỳ phương pháp công khai nào khác là hợp lý. Sự biện minh chủ yếu là vì chúng tôi muốn cung cấp một giao diện cho thế giới bên ngoài xác định cách lớp của chúng tôi tương tác với các lớp khác. Chúng tôi làm điều này chủ yếu vì chúng tôi muốn giảm khớp nối giữa các thực thể riêng biệt. Giảm khớp nối là một điều tốt vì nhiều lý do:

  • Tôi có thể sử dụng từ mã hiện tại của mình cùng một giao diện lớp mặc dù trong khi đó lớp đó đã thêm các phương thức mới (nhưng vẫn giữ nguyên giao diện cũ)
  • Có thể thay đổi đại diện nội bộ của một lớp mà không ảnh hưởng đến người tiêu dùng
  • Giảm khả năng xảy ra lỗi: nếu bạn đặt dữ liệu nội bộ của mình ở chế độ riêng tư, bạn có thể biết chắc chắn rằng mã bên ngoài sẽ không gây rối với dữ liệu nội bộ của bạn

-1

Có, getters và setters là một mô hình chống trong lập trình hướng đối tượng và không bao giờ nên được sử dụng: http://www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html . Tóm lại, chúng không phù hợp với mô hình hướng đối tượng vì chúng khuyến khích bạn đối xử với một đối tượng như cấu trúc dữ liệu. Đó là một quan niệm sai lầm lớn.


2
điều này dường như không cung cấp bất cứ điều gì đáng kể qua các điểm được thực hiện và giải thích trong 18 câu trả lời trước
gnat

4
Nó cũng hơi mạnh để nói không bao giờ .
Winston Ewert

-4

Tôi hy vọng rằng bất cứ điều gì mà IDE phát triển bởi công ty phát triển ngôn ngữ lập trình của bạn tự động tạo ra đều không vi phạm "Nguyên tắc định hướng đối tượng".

Điều đó đang được nói, bất cứ khi nào bạn có một biến phạm vi công khai, hãy sử dụng một getter và setter và bạn là vàng. Dường như với tôi là yếu tố cần thiết của nguyên tắc đóng gói hướng đối tượng cực kỳ - ngay cả khi nó trông giống như rất nhiều mã Junky.

Lý do để sử dụng chúng là việc đóng gói thích hợp có thể vẫn diễn ra và miễn là bạn đã trừu tượng hóa lớp cho phép người khỉ với đối tượng của bạn cảm thấy đối tượng của bạn, bạn có thể lấy đối tượng đó đi mà không cần anh ta biết không?

Có thể nói, một ngôi nhà bị chia không thể đứng, một trong những người thuê OO sẽ không tự bật và bạn không thể phục vụ cả đóng gói và tối ưu hóa sớm.


4
và chính xác tôi đã đạt được gì khi thêm nhiều mã để gọi setFoo () và getFoo () mỗi khi tôi xử lý biến?
Winston Ewert

7
Đây là điều, tôi không nghĩ rằng bạn có được đóng gói. Bạn vẫn đang thao túng trạng thái của đối tượng từ những nơi khác. Bạn chỉ đang làm nó dài hơn. Để đạt được đóng gói, bạn cần tập trung thao tác các giá trị trong lớp sở hữu giá trị đó. Bất cứ điều gì khác chỉ là mã thủ tục trong quần áo OO. (Không có gì sai với điều đó, nhưng nó là như vậy).
Winston Ewert

2
Ưu điểm của đóng gói là tất cả logic liên quan đến các giá trị cụ thể nằm ở một nơi (nhiều hơn hoặc ít hơn). Tôi không có được điều đó với getters và setters. Do đó, tôi không nghĩ rằng tôi nhận được nhiều lợi ích cho họ.
Winston Ewert

1
Tuy nhiên, tôi sẽ đồng ý rằng tôi muốn có mã với getters và setters sau đó một mã thao tác dữ liệu tự do thông qua truy cập công cộng. Bằng cách đó tôi ít nhất có thể đính kèm xác thực dữ liệu đến.
Winston Ewert

3
Tôi chắc chắn không phải là một fan hâm mộ của sự dài dòng. Nhưng sự phản đối của tôi thực sự là những người biến các biến của họ thành riêng tư và sau đó gọi getters và setters trên chúng tự do kết thúc với khá nhiều không có sự khác biệt. Nếu bạn muốn sử dụng getters / setters trong lớp của bạn thì tốt. Câu hỏi là tại sao? Lợi ích chính mà tôi biết là bạn có thể thực hiện một số xác nhận trên dữ liệu để bắt lỗi trước đó. Điều này ít hữu ích hơn khi tất cả các mã thao tác dữ liệu ở cùng một nơi. Nhưng nó vẫn có thể hữu ích. Lý tưởng nhất, bạn có một ngôn ngữ hỗ trợ các thuộc tính và có thể tránh được tính dài dòng.
Winston Ewert

-4

Tôi nghĩ rằng chúng ta sẽ có câu trả lời thông thường hơn ở đây?

Getters và setters nói chung là cực kỳ OOP (bất cứ ai nói bất cứ điều gì khác là sai, đơn giản như vậy).

Mặc dù bạn cần nhớ không quá kỹ sư, không bao giờ tạo ra một getter hoặc setter không cần thiết. Bất kỳ thông tin nào bạn sẽ không sử dụng bởi một lớp khác mà bạn không nên có bất kỳ getter hoặc setter nào.

Mặc dù vậy, lý do tại sao bạn không công khai các trường và thay vào đó có getters và setters chủ yếu là:

  • Không thể thực hiện các thử nghiệm thích hợp bằng cách sử dụng các trường. Getters / setters là tốt hơn nhiều về vấn đề đó.

  • Không thể sử dụng đúng các trường trong cài đặt lập trình thời gian thực. Getters / setters được đồng bộ hóa là cách để đi.

  • Chủ đề giao diện (đã được giải thích nên tôi sẽ không).

  • Nó dễ sử dụng hơn khi sử dụng lại hệ thống đúng cách.

  • Thật khó để biết lớp nào đang sử dụng lớp của bạn và lớp nào sẽ bị hỏng nếu bạn thay đổi một trường hơn là getter / setter.

Do đó, lần duy nhất bạn sử dụng trường công cộng thay vì getter / setter là khi bạn không thực hiện một dự án chính thức mà thay vào đó chỉ làm cho vui và không thể bận tâm với getter / setters.


2
+1 cho khả năng kiểm tra. Sốc mà không ai nhắc đến nó sớm hơn. Một tính năng hữu ích khác của các thuộc tính là gỡ lỗi chúng. (dễ dàng hơn để chèn một điểm dừng trong mã so với yêu cầu điểm dừng phần cứng / bộ nhớ cho các trường).
Đánh dấu H

9
Sự phản đối đối với getters và setters là chúng thường được sử dụng để tuân theo thư của luật OOP trong khi bỏ qua tinh thần và ý định. OOP nói rằng bạn không nên cho phép các đối tượng khác làm quen với nội bộ của mình. Nếu bạn xác định getters và setters cho phép bạn làm điều đó dù sao đi nữa, bạn sẽ vi phạm OOP.
Winston Ewert

6
Tôi sẽ đồng ý rằng có nhiều lợi ích của getters và setters trên các lĩnh vực công cộng đơn giản. Tuy nhiên, tôi không rõ làm thế nào thử nghiệm là tốt hơn với họ. Bạn có thể giải thích?
Winston Ewert

4
@Skarion: Tôi không đồng ý với câu trả lời của bạn. Bạn dường như đang trình bày hai lựa chọn thay thế: hoặc sử dụng getters / setters hoặc làm cho các trường công khai. Nhưng có một lựa chọn thứ ba: không sử dụng getters / setters hay phơi bày các trường! Tại sao bạn cần kiểm tra trạng thái của một trường nội bộ? Bạn có nên thử nghiệm API công khai của đối tượng không?
Andres F.
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.