Helpex - Trao đổi & giúp đỡ Đăng nhập

Getter Setter: Sử dụng hay không sử dụng?

Java Getter Setter

Tại sao chúng ta giữ các biến cá thể ở chế độ riêng tư? Chúng tôi không muốn các lớp khác phụ thuộc vào chúng. Hơn nữa, nó đòi hỏi sự linh hoạt để thay đổi kiểu hoặc cách triển khai của một biến theo ý thích hoặc sự thúc đẩy. Vậy tại sao các lập trình viên tự động thêm getters và setters vào các đối tượng của họ, để lộ các biến private của họ như thể chúng là công khai?

Phương thức truy cập

Accessors (còn được gọi là getters và setters) là các phương thức cho phép bạn đọc và ghi giá trị của một biến thể hiện của một đối tượng.

public class AccessorExample {
  private String attribute; 
  public String getAttribute() { 
    return attribute;
  } 
  public void setAttribute(String attribute) { 
    this.attribute = attribute;
  }
}

Tại sao lại là người truy cập?

Trên thực tế, có nhiều lý do chính đáng để xem xét việc sử dụng trình truy cập thay vì trực tiếp hiển thị các trường của một lớp.

Getter và Setters làm cho các API ổn định hơn. Ví dụ, hãy xem xét một trường public trong một lớp được các lớp khác truy cập. Sau đó, nếu chúng tôi muốn thêm bất kỳ logic bổ sung nào trong khi lấy và thiết lập biến, điều này sẽ ảnh hưởng đến ứng dụng khách hiện có sử dụng API. Vì vậy, bất kỳ thay đổi nào đối với trường công cộng này sẽ yêu cầu thay đổi đối với từng lớp tham chiếu đến nó. Mặt khác, với các phương thức của trình truy cập, người ta có thể dễ dàng thêm một số logic, như bộ nhớ đệm một số dữ liệu hoặc khởi tạo lười biếng.

Các phương thức của trình truy cập cũng cho phép chúng tôi kích hoạt một sự kiện đã thay đổi thuộc tính nếu giá trị mới khác với giá trị trước đó.

Một ưu điểm khác của việc sử dụng bộ định thời để đặt giá trị là chúng ta có thể sử dụng phương pháp để bảo toàn một bất biến hoặc thực hiện một số xử lý đặc biệt khi đặt giá trị.

Tất cả điều này sẽ liền mạch với lớp nhận giá trị bằng phương thức truy cập.

Tôi có nên có Phương thức Accessor cho tất cả các Trường của mình không?

Các trường có thể được khai báo công khai cho các lớp lồng nhau gói-riêng hoặc riêng tư. Việc hiển thị các trường trong các lớp này ít tạo ra sự lộn xộn trực quan hơn so với phương pháp tiếp cận phương thức truy cập, cả trong định nghĩa lớp và mã máy khách sử dụng nó.

Nếu một lớp là gói-riêng hoặc là một lớp lồng nhau riêng tư, thì không có gì sai khi để lộ các trường dữ liệu của nó - giả sử chúng thực hiện một công việc thích hợp là mô tả phần trừu tượng được cung cấp bởi lớp.

Mã như vậy bị giới hạn trong gói mà lớp được khai báo, trong khi mã máy khách được gắn với đại diện bên trong của lớp. Chúng tôi có thể thay đổi nó mà không cần sửa đổi bất kỳ mã nào bên ngoài gói đó. Hơn nữa, trong trường hợp của một lớp lồng nhau riêng tư, phạm vi thay đổi bị hạn chế hơn nữa đối với lớp bao quanh.

Một ví dụ khác về thiết kế sử dụng các trường công khai là các đối tượng mục nhập JavaSpace. Ken Arnold đã mô tả quá trình họ đã trải qua để quyết định đặt các trường đó ở chế độ công khai thay vì riêng tư với các phương thức get và set ở đây .

Bây giờ, điều này đôi khi khiến mọi người không thoải mái vì họ được bảo là không có trường công cộng và trường công cộng là xấu. Nội quy có lý do. Và lý do cho quy tắc dữ liệu riêng tư không áp dụng trong trường hợp cụ thể này. Đó là một ngoại lệ hiếm hoi đối với quy tắc. Tôi cũng nói với mọi người không nên đặt các trường công cộng vào các đối tượng của họ, nhưng vẫn có ngoại lệ. Đây là một ngoại lệ đối với quy tắc vì nó đơn giản hơn và an toàn hơn nếu chỉ nói rằng nó là một trường. Chúng tôi ngồi lại và hỏi: tại sao lại có quy định như vậy? Nó có áp dụng không? Trong trường hợp này, nó không.

Trường riêng + Trình truy cập công cộng == Đóng gói

Hãy xem xét ví dụ dưới đây:

public class A {
  public int a;
}

Thông thường, đây được coi là thực hành mã hóa xấu vì nó vi phạm tính đóng gói. Cách tiếp cận thay thế là:

public class A {
  private int a; 

  public void setA(int a) { 
    this.a =a;
  } 

  public int getA() { 
    return this.a;
  }
}

Người ta lập luận rằng điều này đóng gói thuộc tính. Bây giờ đây có thực sự là sự đóng gói?

Thực tế là, getters / setters không liên quan gì đến việc đóng gói. Ở đây, dữ liệu không bị ẩn hoặc bị đóng gói hơn là trong trường công khai. Các đối tượng khác vẫn có kiến ​​thức sâu sắc về nội bộ của lớp. Các thay đổi được thực hiện đối với lớp có thể xuất hiện và thực thi các thay đổi trong các lớp phụ thuộc. Theo cách này, Getters và setters nói chung đang phá vỡ tính đóng gói. Một lớp thực sự được đóng gói tốt không có bộ định tuyến và tốt hơn là không có bộ định vị. Thay vì yêu cầu một lớp cung cấp một số dữ liệu và sau đó tính toán một cái gì đó với nó, lớp phải chịu trách nhiệm tính toán một cái gì đó với dữ liệu của nó và sau đó trả về kết quả.

Hãy xem xét ví dụ dưới đây:

public class Screens {
  private Map screens = new HashMap(); 

  public Map getScreens() { 
    return screens;
  } 

  public void setScreens(Map screens) { 
    this.screens = screens;
  }

  // remaining code here
}

Nếu chúng ta cần lấy một màn hình cụ thể, chúng ta viết một số mã như dưới đây:

Screen s = (Screen)screens.get(screenId); 

Có những điều đáng lưu ý ở đây ...

Khách hàng cần lấy một Đối tượng từ Bản đồ và truyền nó đến đúng loại. Hơn nữa, điều tồi tệ nhất là bất kỳ khách hàng nào của Bản đồ đều có quyền xóa nó, điều này có thể không phải là trường hợp chúng tôi thường muốn.

Một cách triển khai thay thế của cùng một logic là:

public class Screens {
  private Map screens = new HashMap(); 

  public Screen getById(String id) { 
    return (Screen) screens.get(id);
  }

  // remaining code here
}

Ở đây, cá thể Bản đồ và giao diện ở ranh giới (Bản đồ) bị ẩn.

Người nhận và người định cư được sử dụng quá nhiều

Việc tạo các trường riêng tư và sau đó sử dụng IDE để tự động tạo các getters và setters cho tất cả các trường này gần như không tốt bằng việc sử dụng các trường công khai.

Một lý do cho việc lạm dụng là trong một IDE, bây giờ chỉ cần một vài cú nhấp chuột để tạo các trình truy cập này. Mã getter / setter hoàn toàn vô nghĩa đôi khi dài hơn logic thực trong một lớp và bạn sẽ đọc các hàm này nhiều lần ngay cả khi bạn không muốn.

Tất cả các trường nên được giữ riêng tư, nhưng, các bộ thiết lập chỉ nên được giữ ở chế độ riêng tư khi nó có ý nghĩa, điều này làm cho đối tượng đó Bất biến. Thêm một getter không cần thiết sẽ làm lộ ra cấu trúc bên trong, đây là cơ hội để tăng khả năng ghép nối. Để tránh điều này, mỗi lần trước khi thêm trình truy cập, chúng ta nên phân tích xem liệu chúng ta có thể đóng gói hành vi thay thế hay không.

Hãy lấy một ví dụ khác:

public class Money {
  private double amount; 
  public double getAmount() { 
    return amount;
  } 
  public void setAmount(double amount) { 
    this.amount = amount;
  } 

  //client
  Money pocketMoney = new Money();
  pocketMoney.setAmount(15d);
  double amount = pocketMoney.getAmount();  // we know its double
  pocketMoney.setAmount(amount + 10d);
} 

Với logic trên, sau này, nếu chúng ta giả định rằng double không phải là loại phù hợp để sử dụng và chúng ta nên sử dụng BigDecimal để thay thế, thì client hiện tại sử dụng lớp này cũng sẽ bị hỏng.

Hãy cấu trúc lại ví dụ trên:

public class Money {
  private BigDecimal amount; 
  public Money(String amount) {
    this.amount = new BigDecimal(amount);
  } 
  public void add(Money toAdd) {
    amount = amount.add(toAdd.amount);
  } 

  // client
  Money balance1 = new Money("10.0");
  Money balance2 = new Money("6.0");
  balance1.add(balance2); 
}

Bây giờ thay vì yêu cầu một giá trị, lớp có trách nhiệm tăng giá trị của chính nó. Với cách tiếp cận này, yêu cầu thay đổi đối với bất kỳ loại dữ liệu nào khác trong tương lai không yêu cầu thay đổi mã máy khách.

Kết luận

Việc sử dụng trình truy cập để hạn chế quyền truy cập trực tiếp vào một biến trường được ưu tiên hơn so với việc sử dụng các trường công khai, tuy nhiên, việc tạo getters và setters cho mỗi và mọi trường là quá mức cần thiết. Tuy nhiên, nó cũng phụ thuộc vào tình hình. Đôi khi bạn chỉ muốn một đối tượng dữ liệu câm. Người truy cập phải được thêm vào một trường mà chúng thực sự cần thiết. Một lớp nên thể hiện hành vi lớn hơn xảy ra khi sử dụng trạng thái của nó, chứ không phải là một kho lưu trữ trạng thái để các lớp khác thao tác.

Đọc thêm

15 hữu ích 0 bình luận 98k xem chia sẻ

Có thể bạn quan tâm

loading