Các phương thức của một lớp có nên gọi các getters và setters riêng của nó không?


53

Nơi tôi làm việc tôi thấy rất nhiều lớp học làm những việc như thế này:

public class ClassThatCallsItsOwnGettersAndSetters {

    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

    public void methodWithLogic() {
        setField("value");
        //do stuff
        String localField = getField();
        //do stuff with "localField"
    }
}

Nếu tôi viết nó từ đầu, methodWithLogic()thay vào đó tôi sẽ viết như thế này:

public class ClassThatUsesItsOwnFields {

    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

    public void methodWithLogic() {
        field = "value";
        //do stuff            
        //do stuff with "field"
    }
}

Tôi cảm thấy rằng khi lớp gọi các getters và setters riêng của nó, nó làm cho mã khó đọc hơn. Đối với tôi nó gần như ngụ ý rằng logic phức tạp đang xảy ra trong cuộc gọi phương thức đó mặc dù trong trường hợp của chúng tôi, nó gần như không bao giờ. Khi tôi gỡ lỗi một số mã lạ, ai sẽ nói rằng lỗi không phải là một tác dụng phụ trong phương pháp đó? Nói cách khác, nó khiến tôi có nhiều chuyến đi phụ trên hành trình tìm hiểu mã.

Có lợi ích cho phương pháp đầu tiên? Là phương pháp đầu tiên thực sự tốt hơn?


Khi tôi gỡ lỗi một số mã lạ, ai sẽ nói rằng lỗi không phải là một tác dụng phụ trong phương pháp đó? Bài kiểm tra đơn vị của bạn. :)
Joshua Taylor

1
Sử dụng getters / setters trong mã nội bộ của bạn cho phép bạn tạo một điểm dừng trong mã của bạn nếu bạn đang trải qua nó với trình gỡ lỗi. Nó cũng cho phép bạn đảm bảo rằng nếu có bất cứ điều gì sai với cài đặt / nhận giá trị, bạn biết rằng đó là do phương thức đó chứ không phải do một số truy cập không hợp lệ. Nhìn chung, nó cung cấp cho mã thống nhất hơn.
callyalater

Câu trả lời:


46

Tôi sẽ không nói cái nào tốt hơn hay tệ hơn, bởi vì điều đó một phần phụ thuộc vào tình huống của bạn (theo ý kiến ​​của tôi). Nhưng hãy xem xét rằng các getters và setters của bạn có thể thay đổi việc thực hiện sau đó và bỏ qua chúng sẽ bỏ qua logic đó.

Ví dụ: điều gì xảy ra nếu bạn thêm cờ "bẩn" vào một số trường setter sau này? Bằng cách gọi setters của bạn trong mã nội bộ của bạn, bạn sẽ đặt cờ bẩn mà không thay đổi bất kỳ mã nào khác. Trong nhiều tình huống đây sẽ là một điều tốt.


Nhưng những gì về khi bạn khởi tạo dữ liệu trong phần phụ trợ và bạn không muốn nó bị hiểu là bẩn. Nếu bạn gọi setField()từ đầu, bạn vừa giới thiệu một lỗi.
Daniel Kaplan

6
Phải, đó là lý do tại sao tôi nói nó phụ thuộc một phần vào tình huống. Có thể khởi tạo dữ liệu của bạn nên bỏ qua các setters, nhưng mã logic khác thì không nên. Chọn quy tắc phù hợp và gắn bó với nó.
Matt S

3
@DanielKaplan: vấn đề là việc gọi getters và setters trong hầu hết các trường hợp là điều đúng đắn và chỉ trong các tình huống cụ thể thì không. Tuy nhiên, trong mã thế giới thực, khi bạn thay đổi triển khai getter / setter hiện tại để giới thiệu một số tác dụng phụ có chủ ý, rất có thể bạn sẽ phải kiểm tra mọi cuộc gọi đến getter hoặc setter bên trong lớp và cả mọi truy cập trực tiếp vào cánh đồng. Đó là lý do tại sao bạn nên cố gắng giữ cho lớp học của mình nhỏ nhất có thể.
Doc Brown

33

Gọi trực tiếp setter không phải là một vấn đề. Câu hỏi thực sự nên là: Tại sao mã của chúng tôi có setters ở khắp mọi nơi?

Các lớp có thể thay đổi là một trò chơi nguy hiểm, đặc biệt là khi chính lớp đó quản lý trạng thái của chính nó. Xem xét có bao nhiêu trong số những setters thực sự cần phải tồn tại. Có bao nhiêu có thể được thiết lập trong hàm tạo và sau đó được đóng gói hoàn toàn bởi chính lớp đó?

Nếu trường phải được giải quyết bên ngoài thì hãy tự hỏi liệu phương thức với logic có nên ở đó không. Là lớp học của bạn thực sự chỉ là một lớp dữ liệu / trạng thái? Lớp học này có nhiều hơn một trách nhiệm?

Đừng hiểu sai ý tôi, sẽ có trường hợp điều này ổn. Tôi không nói rằng bạn nên loại bỏ setters trên các lớp với logic hoàn toàn. Nhưng những điều này nên là ngoại lệ, không phải là quy tắc. Và, trong vài trường hợp đó, rõ ràng nên truy cập trực tiếp.


1
Tôi hoàn toàn đồng ý / thực hành dòng suy nghĩ này. Tôi cũng nghĩ rằng bạn đưa ra một điểm thực sự quan trọng hơn câu hỏi của tôi. Điều đó nói rằng, tôi không cảm thấy như nó trực tiếp trả lời câu hỏi ban đầu của tôi. Trừ khi bạn nói, "trong sơ đồ lớn về những điều mà câu hỏi của bạn không quan trọng". Điều này cũng có thể đúng :)
Daniel Kaplan

@DanielKaplan: Tôi đang nói chính xác điều đó. :) Tôi đã bị giằng xé giữa câu trả lời và nhận xét với câu hỏi này, nhưng tôi thực sự không cảm thấy rằng có một câu trả lời đúng mà không hỏi câu hỏi lớn hơn.
pdr

9

Có, các phương thức của lớp của bạn nên gọi các getters và setters. Toàn bộ quan điểm trong việc viết getters và setters là bằng chứng trong tương lai. Bạn có thể biến mọi thuộc tính thành một trường và trực tiếp hiển thị dữ liệu cho người dùng của lớp. Lý do bạn xây dựng các getters và setters không nhất thiết là vì hiện tại có logic phức tạp nhưng để giao diện không bị hỏng trong tương lai nếu bạn phải thêm nó.


1
Tôi không thấy mối quan hệ giữa câu đầu tiên của bạn và phần còn lại của đoạn văn của bạn. Câu đầu tiên nói rằng một lớp nên gọi các getters và setters riêng của nó . Phần còn lại của đoạn giải thích lý do tại sao mã máy khách (ví dụ: mã sử dụng lớp) nên sử dụng getters và setters thay vì truy cập trực tiếp vào các trường. Nhưng tôi không thấy lý do tại sao lớp không nên truy cập trực tiếp vào các trường của chính nó .
Daniel Kaplan

1
@DanielKaplan Quan điểm của tôi là lý do là một và giống nhau. Rằng nếu bạn thêm logic setter sau này, nó sẽ tác động đến mã bên trong cũng nhiều (có khả năng) như bên ngoài.
Michael

2
@Michael: Thường có thể có những lý do chính đáng để một lớp không sử dụng getters / setters riêng của nó. Một tình huống phổ biến là có nhiều setters của biểu mẫu someField=newValue; refresh(); Nếu phương thức cho phép nhiều trường được đặt, việc gọi setters để viết các trường đó sẽ gây ra các hoạt động "làm mới" dư thừa. Viết tất cả các lĩnh vực và sau đó gọi refresh()một lần có thể mang lại hoạt động hiệu quả hơn và mượt mà hơn.
supercat

6

Để trả lời câu hỏi của bạn trong một từ, có.

Có một lớp gọi các getters và setters riêng của nó thêm khả năng mở rộng và cung cấp một cơ sở tốt hơn cho mã trong tương lai.

Nói rằng bạn có một cái gì đó như thế này:

public class Vehicle
{
    private int year;
    private String make;

    public Vehicle(int year, String make)
    {
        setYear(year);
        setMake(make);
    }

    public void setYear(int year)
    {
        this.year = year;
    }

    public void setMake(String make)
    {
        this.make = make;
    }
}

Gọi setters cho năm và hiện tại có thể không thêm bất kỳ chức năng nào, nhưng nếu bạn muốn thêm một cái gì đó như xác nhận đầu vào cho setters thì sao?

public class Vehicle
{
    private int year;
    private String make;

    public Vehicle(int year, String make)
    {
        setYear(year);
        setMake(make);
    }

    public void setYear(int year)
    {
        if(year > 0)
        {
            this.year = year;
        }
        else
        {
            System.out.println(year + " is not a valid year!");
        }
    }

    public void setMake(String make)
    {
        this.make = make;
    }
}

5

Một điều chưa được đề cập là getter và setters (như tất cả các phương thức) là ảo trong Java. Điều này thêm một tính năng khác để luôn sử dụng chúng trong mã của bạn. Một người dùng khác có thể mở rộng lớp học của bạn, ghi đè lên getters và setters của bạn. Lớp cơ sở của bạn sau đó sẽ sử dụng dữ liệu của lớp con thay vì dữ liệu của lớp đó. Trong một ngôn ngữ mà bạn đánh dấu rõ ràng các chức năng ảo, điều này tiện dụng hơn nhiều vì bạn có thể dự đoán và khai báo những chức năng này có thể được thực hiện. Trong Java, đây là điều bạn luôn phải nhận thức được. Nếu đó là hành vi mong muốn thì sử dụng chúng trong mã của bạn là một điều tốt, nếu không thì không nhiều.


3

Nếu bạn đang cố gắng thực hiện một cái gì đó được cung cấp bởi giao diện công cộng, thì hãy sử dụng getters / setters.

Tuy nhiên, với tư cách là chủ sở hữu / nhà phát triển của lớp, bạn được phép truy cập vào các phần riêng tư của mã của mình (từ trong lớp tất nhiên), nhưng bạn cũng có trách nhiệm giảm thiểu các nguy hiểm.

Vì vậy, có thể bạn có một getter lặp qua một số danh sách nội bộ và bạn muốn nhận giá trị hiện tại mà không cần tăng iterator. Trong trường hợp này, sử dụng biến riêng.

public class MyClass
{
    private int i;
    private List<string> list;
    public string getNextString()
    {
        i++;
        return list[i];
    }

    private void getString()
    {
        // Do not increment
        string currentString = list[i];

        // Increment
        string nextString = getNextString();
    }
}

Bạn có thể đưa ra lý do cho điều này?
Daniel Kaplan

1

Đúng. Getters và setters đại diện cho trạng thái, vì vậy hãy chuyển câu hỏi này - bạn có muốn theo dõi nhiều cách thay đổi trạng thái đối tượng theo cùng một cách trừ khi bạn phải không?

Càng ít điều bạn phải theo dõi tốt hơn - đây là lý do tại sao các đối tượng bất biến dễ đối phó hơn.

Hãy xem xét, không có gì ngăn cản bạn có cả lĩnh vực công cộng và công cụ getter / setter công khai - nhưng bạn sẽ đạt được gì?

Thỉnh thoảng có thể mong muốn hoặc thậm chí cần thiết để truy cập trực tiếp vào trường vì một hoặc nhiều lý do, bạn không nên né tránh điều đó nếu nó xảy ra. Nhưng bạn thực sự chỉ nên làm điều đó nếu có một lợi ích xác định để làm như vậy.


Quan điểm của câu hỏi của tôi là tôi chỉ hỏi về quyền truy cập trực tiếp của các trường trong cùng một lớp. Tôi đã hiểu mục đích của getters và setters cho mã khách hàng.
Daniel Kaplan

@DanielKaplan: Tôi hiểu điều đó, và điều tôi đang nói là theo thực tế, bạn nên đối xử với họ như nhau.
jmoreno

.... Sự khác biệt giữa kịch bản này và những gì bạn mô tả là bạn không thể tránh bị có thể truy cập vào trạng thái bên ngoài của getters / setters, nhưng bạn có thể tránh thực sự làm như vậy. Đừng làm phức tạp mã của bạn trừ khi bạn phải.
jmoreno

1

Cả hai phương pháp đều có trường hợp sử dụng của họ. Vì setter công khai giữ giá trị trường (và / hoặc giá trị ràng buộc) nhất quán, bạn nên sử dụng setter khi logic phương thức của bạn không can thiệp vào logic nhất quán này. Nếu bạn chỉ đặt "thuộc tính" của mình, hãy sử dụng setter. Mặt khác, có những tình huống, trong đó bạn cần truy cập trực tiếp vào một số trường, ví dụ như các hoạt động hàng loạt với setter nặng hoặc các hoạt động trong đó khái niệm setter quá đơn giản.

Trách nhiệm của bạn là giữ mọi thứ nhất quán. Setter thực hiện điều này theo định nghĩa, nhưng không thể bao gồm các trường hợp phức tạp.


1

Tôi sẽ nói không..

Nếu getters / setters của bạn chỉ nhận và thiết lập (không khởi tạo lười biếng, không kiểm tra, v.v.) thì chỉ cần sử dụng các biến của bạn. Nếu trong tương lai bạn thay đổi getters / setters của mình, bạn rất có thể thay đổi methodWithLogic()(vì lớp của bạn đang thay đổi) và bạn có thể gọi getter / setter thay vì gán trực tiếp. Bạn có thể ghi lại cuộc gọi này cho getter / setter (vì sẽ rất lạ khi gọi getter / setter khi phần còn lại của mã lớp của bạn trực tiếp sử dụng biến).

JVM sẽ thực hiện các cuộc gọi thường xuyên đến getters / setters. Vì vậy, nó không bao giờ là một vấn đề hiệu suất. Lợi ích của việc sử dụng các biến là khả năng đọc và IMHO, tôi sẽ sử dụng nó.

Hi vọng điêu nay co ich..

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.