Cấu trúc như các đối tượng trong Java


195

Có hoàn toàn chống lại cách Java để tạo cấu trúc như các đối tượng không?

class SomeData1 {
    public int x;
    public int y;
}

Tôi có thể thấy một lớp với các trình truy cập và trình biến đổi giống Java hơn.

class SomeData2 {
    int getX();
    void setX(int x);

    int getY();
    void setY(int y);

    private int x;
    private int y;
}

Các lớp từ ví dụ đầu tiên là thuận tiện về mặt lý thuyết.

// a function in a class
public int f(SomeData1 d) {
    return (3 * d.x) / d.y;
}

Điều này không thuận tiện.

// a function in a class
public int f(SomeData2 d) {
    return (3 * d.getX()) / d.getY();
}

9
Thay vì các trường có thể thay đổi công khai, hãy xem xét các trường bất biến công khai hoặc các trường có thể thay đổi cục bộ. Hoặc là sẽ tốt hơn IMHO.
Peter Lawrey

Hãy nhớ rằng, trong khi các getters và setters là xấu xí / dài dòng, đó là loại trái tim của Java. Đó là một ngôn ngữ không súc tích. Tuy nhiên, ở mặt khác, bạn KHÔNG BAO GIỜ nên gõ bất kỳ thứ gì trong số đó, vì đó là những gì IDE của bạn làm cho bạn. Trong một ngôn ngữ động, bạn phải gõ ít hơn, nhưng bạn phải gõ (nói chung, mặc dù IDE có thể giúp đỡ).
Dan Rosenstark

Trớ trêu thay, trong khi OO có thế mạnh về đóng gói, thì đây là một cái giá phải trả cho CPU và lưu trữ thông minh. Trình thu gom rác (gần như hoàn toàn) loại bỏ sự cần thiết phải lo lắng về việc khi nào các tham chiếu đối tượng sẽ bị xóa. Xu hướng hiện tại đang diễn ra đầy đủ bằng cách sử dụng các cấu trúc giống như C ngoài luồng. Điều này là hoàn hảo cho các giải pháp kiểu bộ đệm, truyền thông giữa các quá trình, hoạt động chiếm bộ nhớ nhanh hơn, o / h thấp hơn và thậm chí có thể hưởng lợi từ o / h lưu trữ thấp hơn cho các tập dữ liệu của bạn. Nếu bạn biết bạn đang làm gì, bạn sẽ không hỏi câu hỏi này ... vì vậy hãy suy nghĩ lại!
dùng924272

@ user924272: Re "Xu hướng hiện tại đang diễn ra toàn diện bằng cách sử dụng các cấu trúc giống như C ngoài luồng". Bạn sẽ làm gì trong Java như thế nào ??? IMHO, đây là một khu vực nơi Java đang hiển thị tuổi của nó ...
ToolmakerSteve

@ToolmakerSteve -Tôi thấy một vòng tròn. Tôi không phải người duy nhất. Các công ty như Azul rất nóng trong việc thu gom rác tạm dừng. Java đã cũ. Thật. Các kỹ sư phát hiện ra một điểm yếu và làm gì đó về nó, thay vì rên rỉ? Họ xứng đáng được tôn trọng! +10 đến Azul từ tôi :-)
user924272

Câu trả lời:


62

Đây là một chủ đề thường được thảo luận. Hạn chế của việc tạo các trường công khai trong các đối tượng là bạn không có quyền kiểm soát các giá trị được đặt cho nó. Trong các dự án nhóm nơi có nhiều lập trình viên sử dụng cùng một mã, điều quan trọng là tránh các tác dụng phụ. Bên cạnh đó, đôi khi tốt hơn là trả lại một bản sao của đối tượng trường hoặc biến đổi nó bằng cách nào đó, v.v. Bạn có thể chế giễu các phương thức đó trong các thử nghiệm của mình. Nếu bạn tạo một lớp mới, bạn có thể không thấy tất cả các hành động có thể. Nó giống như lập trình phòng thủ - một ngày nào đó getters và setters có thể hữu ích, và nó không tốn nhiều chi phí để tạo / sử dụng chúng. Vì vậy, chúng đôi khi hữu ích.

Trong thực tế, hầu hết các lĩnh vực có getters và setters đơn giản. Một giải pháp khả thi sẽ như thế này:

public property String foo;   
a->Foo = b->Foo;

Cập nhật: Rất khó có khả năng hỗ trợ thuộc tính sẽ được thêm vào trong Java 7 hoặc có lẽ đã từng. Các ngôn ngữ JVM khác như Groovy, Scala, v.v ... hiện đã hỗ trợ tính năng này. - Alex Miller


28
Điều đó thật tệ, tôi thích các thuộc tính kiểu C # (nghe giống như những gì bạn đang nói)
Jon Onstott

2
Vì vậy, sử dụng quá tải ... private int _x; công khai void x (int value) {_x = value; } công khai int x () {return _x; }
Gordon

12
Tôi thích có thể sử dụng =, theo ý kiến ​​của tôi làm cho mã sạch hơn.
Svish

6
@ T-Bull: Chỉ vì bạn CÓ THỂ có hai xthứ là hai thứ khác nhau, không làm cho nó trở thành một ý tưởng hay. IMHO, đó là một gợi ý kém, vì nó có thể dẫn đến sự nhầm lẫn của độc giả loài người. Nguyên tắc cơ bản: không khiến độc giả thực hiện gấp đôi; làm rõ những gì bạn đang nói - Sử dụng các tên khác nhau cho các thực thể khác nhau. Ngay cả khi sự khác biệt chỉ đơn thuần là để trả trước một dấu gạch dưới. Đừng dựa vào dấu câu xung quanh để phân biệt các thực thể.
ToolmakerSteve

2
@ToolmakerSteve: Điều đó "có thể dẫn đến sự nhầm lẫn" là lý lẽ phổ biến nhất và vẫn là yếu nhất khi nói đến việc bảo vệ giáo điều mã hóa (trái ngược với phong cách mã hóa). Luôn luôn có một người nào đó bị nhầm lẫn bởi những điều đơn giản nhất. Bạn sẽ luôn thấy ai đó phàn nàn rằng anh ta đã phạm sai lầm sau khi tỉnh táo và viết mã trong nửa tuần hoặc một số như vậy và sau đó đổ lỗi cho phong cách mã hóa sai lệch. Tôi không để điều đó. Phong cách đó là hợp lệ, rõ ràng và giữ cho không gian tên sạch sẽ. Ngoài ra, không có các thực thể khác nhau ở đây, có / một / thực thể và một số mã soạn sẵn xung quanh nó.
T-Bull

290

Dường như nhiều người Java không quen thuộc với Nguyên tắc mã hóa Sun Java, nói rằng nó khá phù hợp để sử dụng biến cá thể công khai khi lớp về cơ bản là "Struct", nếu Java hỗ trợ "struct" (khi không có hành vi).

Mọi người có xu hướng nghĩ rằng getters và setters là cách Java, như thể họ là trung tâm của Java. Đây không phải là như vậy. Nếu bạn tuân theo Nguyên tắc mã hóa Sun Java, sử dụng các biến thể hiện công khai trong các tình huống thích hợp, bạn thực sự đang viết mã tốt hơn so với làm lộn xộn nó với các getters và setters không cần thiết.

Các quy ước về mã Java từ năm 1999 và vẫn không thay đổi.

10.1 Cung cấp quyền truy cập vào các biến thể lập thể và lớp

Đừng biến bất kỳ trường hợp hoặc biến lớp nào thành công khai mà không có lý do chính đáng. Thông thường, các biến đối tượng không cần phải được đặt hoặc nhận rõ ràng - thường xảy ra như là một tác dụng phụ của các cuộc gọi phương thức.

Một ví dụ về các biến thể hiện công khai thích hợp là trường hợp lớp về cơ bản là cấu trúc dữ liệu, không có hành vi. Nói cách khác, nếu bạn đã sử dụng một cấu trúc thay vì một lớp (nếu Java được hỗ trợ cấu trúc), thì nó phù hợp để biến các biến đối tượng của lớp thành công khai .

http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-137265.html#177

http://en.wikipedia.org/wiki/Plain_old_data_structure

http://docs.oracle.com/javase/1.3/docs/guide/collections/designfaq.html#28


88
+1 Để thực sự có một nguồn có thẩm quyền. Mọi câu trả lời khác là mọi người quay cuồng ý kiến ​​của họ giống như họ là sự thật.
ArtOfWarfare

1
Có một đặc tả Java Beans là cách truy cập các thuộc tính theo tiêu chuẩn ngành bằng cách sử dụng các phương thức get và set ... xem en.wikipedia.org/wiki/JavaBeans để biết tổng quan.
dùng924272

4
@ user924272: Thông số kỹ thuật của Đậu Đậu có liên quan đến câu trả lời này như thế nào, nó thảo luận khi nào là phù hợp để sử dụng "các biến thể hiện công khai"? Nếu thông số kỹ thuật là một cách tiêu chuẩn để tự động biến các biến đối tượng thành các thuộc tính, ala C #, thì nó có thể có liên quan. Nhưng nó không phải, phải không? Nó chỉ xác định cách đặt tên của getters và setters cần phải được tạo, để tạo ra một ánh xạ như vậy.
ToolmakerSteve

@ToolmakerSteve. Đây là một câu hỏi java. Ngoài ra, câu hỏi gợi ra một vấn đề phổ biến trong đó một đặc tả tồn tại. Từ quan điểm của trường học cũ, việc gỡ lỗi đột biến trường trở nên dễ dàng hơn nhiều khi có một cách tiêu chuẩn để thực hiện - đặt điểm dừng ở một setter. Điều này có lẽ đã trở nên lỗi thời với các trình gỡ lỗi hiện đại, nhưng tôi buộc phải nhăn mặt với các lớp trực tiếp "đấm" các đối tượng ... trong khi điều này là ổn đối với các ứng dụng nhỏ hơn, nó thực sự gây đau đầu cho các ứng dụng lớn hơn và các tổ chức lớn
user924272

223

Sử dụng thông thường thực sự. Nếu bạn có một cái gì đó như:

public class ScreenCoord2D{
    public int x;
    public int y;
}

Sau đó, có rất ít điểm trong việc gói chúng trong getters và setters. Bạn sẽ không bao giờ lưu trữ tọa độ x, y trong toàn bộ pixel theo bất kỳ cách nào khác. Getters và setters sẽ chỉ làm bạn chậm lại.

Mặt khác, với:

public class BankAccount{
    public int balance;
}

Bạn có thể muốn thay đổi cách tính số dư tại một thời điểm nào đó trong tương lai. Điều này thực sự nên sử dụng getters và setters.

Luôn luôn tốt hơn để biết lý do tại sao bạn áp dụng thực hành tốt, để bạn biết khi nào nên chấp nhận các quy tắc.


3
Tôi đi cùng với câu trả lời này và nói thêm rằng bạn có thể tạo một lớp với các trường công khai miễn là các trường được in đậm độc lập với nhau. tức là một lĩnh vực không phụ thuộc vào một lĩnh vực khác. Điều này có thể khá hữu ích trong nhiều trường hợp, đối với nhiều giá trị trả về từ một hàm hoặc cho các đồng phân cực. {góc, độ dài} đi cùng nhau nhưng không phụ thuộc vào nhau theo bất kỳ cách thức nội tại nào.
Spacen Jasset

@SpacenJasset: FYI, tôi không thấy các ví dụ của bạn (nhiều giá trị trả về; tọa độ cực) có liên quan đến việc có nên sử dụng các trường công khai so với getter / setters không. Trong trường hợp có nhiều giá trị trả về, nó thậm chí có thể phản tác dụng, vì người ta cho rằng người gọi chỉ nên NHẬN các giá trị được trả về, lập luận ủng hộ các công cụ thu thập công cộng và setters riêng (không thay đổi). Điều này cũng có thể đúng khi trả về tọa độ cực từ một đối tượng (x, y) - xem xét các lỗi toán học TƯƠNG LAI, vì các thay đổi đối với các thành phần riêng lẻ của tọa độ cực được chuyển đổi trở lại (x, y).
ToolmakerSteve

@SpacenJasset: Nhưng tôi đồng ý với nguyên tắc của bạn.
ToolmakerSteve

1
Bạn có một điểm hợp lệ, nhưng tôi cảm thấy như pixel là một ví dụ tồi vì đảm bảo pixel nằm trong cửa sổ (chỉ là một ví dụ) điều mà ai đó có thể làm, và bên cạnh đó, cho phép ai đó đặt pixel thành (-5, -5)có thể không một ý tưởng tốt :-)
Ngựa

50

Để giải quyết mối quan tâm về tính biến đổi, bạn có thể khai báo x và y là cuối cùng. Ví dụ:

class Data {
  public final int x;
  public final int y;
  public Data( int x, int y){
    this.x = x;
    this.y = y;
  }
}

Gọi mã cố gắng ghi vào các trường này sẽ nhận được lỗi thời gian biên dịch của "trường x được khai báo là cuối cùng; không thể được chỉ định".

Mã khách hàng sau đó có thể có sự tiện lợi 'tay ngắn' mà bạn đã mô tả trong bài đăng của mình

public class DataTest {
    public DataTest() {
        Data data1 = new Data(1, 5);
        Data data2 = new Data(2, 4);
        System.out.println(f(data1));
        System.out.println(f(data2));
    }

    public int f(Data d) {
        return (3 * d.x) / d.y;
    }

    public static void main(String[] args) {
        DataTest dataTest = new DataTest();
    }
}

3
Cảm ơn - một câu trả lời hữu ích và súc tích. Chỉ ra cách nhận được lợi ích của cú pháp trường, khi không muốn tính đột biến.
ToolmakerSteve

@ToolmakerSteve - cảm ơn bạn đã phản hồi - đánh giá cao.
Brian

Tôi đã cố gắng sử dụng các thể hiện cuối cùng của một lớp cuối cùng với các trường cuối cùng là "cá thể" cấu trúc, nhưng trong một casebiểu thức đề cập đến trường thể hiện như vậy, tôi đã nhận được Case expressions must be constant expressions. cách này là gì? khái niệm thành ngữ ở đây là gì?
n611x007

1
các trường cuối cùng vẫn là các tham chiếu đến các đối tượng, không phải là hằng số, vì chúng sẽ được khởi tạo khi lớp được sử dụng lần đầu tiên. Trình biên dịch không thể biết "giá trị" của các tham chiếu đối tượng. Các hằng số phải được biết khi biên dịch.
Johan Tidén

1
+1 Câu trả lời thực sự quan trọng. Theo tôi, tính hữu dụng của các lớp bất biến có thể được đánh giá thấp. Các ngữ nghĩa "lửa và quên" của họ làm cho việc lập luận về mã thường đơn giản hơn, đặc biệt là trong các môi trường đa luồng, nơi chúng có thể được chia sẻ tùy ý giữa các luồng mà không cần đồng bộ hóa.
TheOperator 7/03/2016

11

Không được dùng public các trường

Không sử dụng publiccác trường khi bạn thực sự muốn bao bọc hành vi nội bộ của một lớp. Lấy java.io.BufferedReaderví dụ. Nó có trường sau:

private boolean skipLF = false; // If the next character is a line feed, skip it

skipLFđược đọc và viết trong tất cả các phương pháp đọc. Điều gì xảy ra nếu một lớp bên ngoài chạy trong một luồng riêng biệt đã sửa đổi độc hại trạng thái skipLFở giữa một lần đọc? BufferedReaderchắc chắn sẽ đi haywire.

Sử dụng publiccác lĩnh vực

Lấy Pointlớp này làm ví dụ:

class Point {
    private double x;
    private double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public double getX() {
        return this.x;
    }

    public double getY() {
        return this.y;
    }

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

    public void setY(double y) {
        this.y = y;
    }
}

Điều này sẽ làm cho việc tính khoảng cách giữa hai điểm rất khó viết.

Point a = new Point(5.0, 4.0);
Point b = new Point(4.0, 9.0);
double distance = Math.sqrt(Math.pow(b.getX() - a.getX(), 2) + Math.pow(b.getY() - a.getY(), 2));

Lớp học không có bất kỳ hành vi nào khác ngoài getters và setters đơn giản. Có thể chấp nhận sử dụng các trường công khai khi lớp chỉ đại diện cho cấu trúc dữ liệu và không có sẽ không bao giờ có hành vi (getters và setters mỏng không được coi là hành vi ở đây). Nó có thể được viết tốt hơn theo cách này:

class Point {
    public double x;
    public double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }
}

Point a = new Point(5.0, 4.0);
Point b = new Point(4.0, 9.0);
double distance = Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2));

Dọn dẹp!

Nhưng hãy nhớ rằng: Không chỉ lớp học của bạn phải vắng mặt trong hành vi, mà nó cũng không có lý do để có hành vi trong tương lai.


(Đây chính xác là những gì câu trả lời này mô tả. Để trích dẫn "Các quy ước mã cho ngôn ngữ lập trình Java: 10. Thực tiễn lập trình" :

Một ví dụ về các biến thể hiện công khai thích hợp là trường hợp lớp về cơ bản là cấu trúc dữ liệu, không có hành vi. Nói cách khác, nếu bạn đã sử dụng structthay vì một lớp (nếu được Java hỗ trợ struct), thì nó phù hợp để biến các biến đối tượng của lớp thành công khai.

Vì vậy, tài liệu chính thức cũng chấp nhận thực hành này.)


Ngoài ra, nếu bạn thêm chắc chắn rằng các thành viên của Pointlớp trên sẽ không thay đổi, thì bạn có thể thêm finaltừ khóa để thực thi nó:

public final double x;
public final double y;

8

Nhân tiện, cấu trúc mà bạn đưa ra làm ví dụ đã tồn tại trong thư viện lớp cơ sở Java như java.awt.Point. Nó có x và y là các trường công khai, hãy tự kiểm tra .

Nếu bạn biết những gì bạn đang làm và những người khác trong nhóm của bạn biết về nó, thì có thể có các lĩnh vực công cộng. Nhưng bạn không nên dựa vào nó bởi vì chúng có thể gây đau đầu vì các lỗi liên quan đến các nhà phát triển sử dụng các đối tượng như thể chúng là các cấu trúc được phân bổ ngăn xếp (các đối tượng java luôn được gửi đến các phương thức dưới dạng tham chiếu chứ không phải là bản sao).


+1 Đề cập tốt đến một vấn đề - một cách mà kết quả vẫn KHÔNG giống như cấu trúc C. Tuy nhiên, vấn đề bạn nêu ra về các đối tượng java luôn luôn được tham chiếu, không được cải thiện bằng cách tạo một trình thiết lập thay vì có một trường có thể ghi công khai (đó là bản chất của câu hỏi của OP - nên sử dụng biểu diễn nào). Thay vào đó, nó là một đối số có lợi cho việc làm NEITHER. Đó là một đối số cho IMMUTABILITY. Điều này có thể được thực hiện như một public finallĩnh vực, như trong câu trả lời của Brian, hoặc bằng cách có một trình thu thập công khai, nhưng không có trình thiết lập công khai. Đó là, cho dù một người sử dụng các lĩnh vực hoặc người truy cập là không quan trọng.
ToolmakerSteve

8

Re: aku, izb, John Topley ...

Coi chừng các vấn đề về tính đột biến ...

Nó có vẻ hợp lý để bỏ qua getters / setters. Nó thực sự có thể ổn trong một số trường hợp. Vấn đề thực sự với mô hình đề xuất được hiển thị ở đây là tính đột biến.

Vấn đề là một khi bạn vượt qua một tham chiếu đối tượng có chứa các trường công khai, không phải là chung kết. Bất cứ điều gì khác với tham chiếu đó là miễn phí để sửa đổi các lĩnh vực. Bạn không còn có quyền kiểm soát trạng thái của đối tượng đó nữa. (Hãy nghĩ điều gì sẽ xảy ra nếu Chuỗi có thể thay đổi.)

Nó trở nên tồi tệ khi đối tượng đó là một phần quan trọng của trạng thái nội bộ của người khác, bạn vừa tiếp xúc với việc thực hiện nội bộ. Để ngăn chặn điều này, một bản sao của đối tượng phải được trả lại thay thế. Điều này hoạt động, nhưng có thể gây ra áp lực GC lớn từ hàng tấn bản sao sử dụng một lần được tạo.

Nếu bạn có các trường công khai, hãy xem xét làm cho lớp chỉ đọc. Thêm các trường làm tham số cho hàm tạo và đánh dấu các trường cuối cùng. Mặt khác, đảm bảo bạn không để lộ trạng thái nội bộ và nếu bạn cần xây dựng các thể hiện mới cho giá trị trả về, hãy đảm bảo rằng nó sẽ không được gọi quá mức.

Xem: " Java hiệu quả " của Joshua Bloch - Mục số 13: Bất biến bất lợi.

PS: Cũng lưu ý, tất cả các JVM ngày nay sẽ tối ưu hóa getMethod nếu có thể, dẫn đến chỉ một lệnh đọc trường duy nhất.


12
Làm thế nào để một getter / setter giải quyết vấn đề này. Bạn vẫn có một tài liệu tham khảo, bạn không có bất kỳ sự đồng bộ hóa nào với các hoạt động. Getters / setters không cung cấp bảo vệ trong và của chính họ.
he_the_great

1
Getters và setters có thể cung cấp đồng bộ hóa nếu cần. Bạn cũng đang mong đợi getters và setter sẽ làm được nhiều hơn những gì chúng được chỉ định làm. Bất kể điều này, vấn đề đồng bộ hóa vẫn còn vitamin.
dùng924272

7

Tôi đã thử điều này trong một vài dự án, trên lý thuyết rằng các getters và setters làm lộn xộn mã với ngữ nghĩa vô nghĩa về mặt ngữ nghĩa và các ngôn ngữ khác dường như chỉ làm tốt với việc ẩn dữ liệu dựa trên quy ước hoặc phân vùng trách nhiệm (ví dụ python).

Như những người khác đã lưu ý ở trên, có 2 vấn đề mà bạn gặp phải và chúng không thực sự có thể khắc phục được:

  • Chỉ cần về bất kỳ công cụ tự động nào trong thế giới java đều dựa vào quy ước getter / setter. Ditto, như được lưu ý bởi những người khác, thẻ jsp, cấu hình lò xo, công cụ nhật thực, v.v ... ... Chống lại những gì công cụ của bạn mong đợi là một công thức cho các phiên dài trolling thông qua google cố gắng tìm ra cách khởi tạo không chuẩn đó đậu xuân. Thực sự không đáng để gặp rắc rối.
  • Khi bạn có ứng dụng được mã hóa thanh lịch của mình với hàng trăm biến công khai, bạn có thể sẽ tìm thấy ít nhất một tình huống khi chúng không đủ - nơi bạn thực sự cần bất biến hoặc bạn cần kích hoạt một số sự kiện khi biến được đặt hoặc bạn muốn ném một ngoại lệ trên một thay đổi biến vì nó đặt trạng thái đối tượng thành một cái gì đó khó chịu. Sau đó, bạn bị mắc kẹt với các lựa chọn không thể chối cãi giữa việc làm lộn xộn mã của bạn với một số phương thức đặc biệt ở khắp mọi nơi mà biến được tham chiếu trực tiếp, có một số hình thức truy cập đặc biệt cho 3 trong số 1000 biến trong ứng dụng của bạn.

Và đây là trường hợp tốt nhất để làm việc hoàn toàn trong một dự án tư nhân khép kín. Khi bạn xuất toàn bộ sang thư viện có thể truy cập công khai, các vấn đề này sẽ còn lớn hơn nữa.

Java rất dài dòng và đây là một điều hấp dẫn để làm. Đừng làm điều đó.


Thảo luận tuyệt vời về các vấn đề đi xuống con đường của các lĩnh vực công cộng. Một thiếu sót nhất định của Java, điều này làm tôi khó chịu khi tôi phải quay lại Java từ C # (học được từ sự bất tiện của Java ở đây).
ToolmakerSteve

4

Nếu cách Java là cách OO, thì có, việc tạo một lớp với các trường công khai phá vỡ các nguyên tắc xung quanh việc che giấu thông tin nói rằng một đối tượng nên quản lý trạng thái bên trong của chính nó. (Vì vậy, vì tôi không chỉ nói về thuật ngữ của bạn, một lợi ích của việc che giấu thông tin là hoạt động bên trong của một lớp được ẩn sau một giao diện - giả sử bạn muốn thay đổi cơ chế mà lớp struct của bạn đã lưu một trong các trường của nó, có lẽ bạn sẽ cần phải quay lại và thay đổi bất kỳ lớp nào sử dụng lớp ...)

Bạn cũng không thể tận dụng sự hỗ trợ cho các lớp tuân thủ đặt tên JavaBean, điều này sẽ gây tổn hại nếu bạn quyết định sử dụng lớp trong Trang máy chủ Java được viết bằng Ngôn ngữ biểu thức.

Bài viết JavaWorld Tại sao Phương thức Getter và Setter là Ác cũng có thể khiến bạn quan tâm khi nghĩ đến việc không nên thực hiện các phương thức accessor và mutator.

Nếu bạn đang viết một giải pháp nhỏ và muốn giảm thiểu số lượng mã liên quan, cách Java có thể không đúng cách - tôi đoán nó luôn phụ thuộc vào bạn và vấn đề bạn đang cố gắng giải quyết.


1
+1 cho liên kết đến bài viết "Tại sao phương pháp Getter và Setter là xấu xa". Tuy nhiên, câu trả lời của bạn sẽ rõ ràng hơn nếu bạn chỉ ra rằng CẢ HAI lĩnh vực công cộng VÀ bộ thu / phát công khai là THIẾT BỊ KHÔNG theo cách Java: Như đã giải thích trong bài viết đó - khi có thể, đừng làm như vậy. Thay vào đó, cung cấp cho khách hàng các phương thức cụ thể cho những gì họ cần làm, thay vì phản ánh biểu diễn bên trong của thể hiện. TUY NHIÊN, điều này không thực sự giải quyết câu hỏi đang được hỏi (nên sử dụng, đối với trường hợp "struct" đơn giản), được trả lời tốt hơn bởi developer.g, izb và Brian.
ToolmakerSteve

3

Không có gì sai với loại mã đó, với điều kiện tác giả biết rằng chúng là cấu trúc (hoặc con thoi dữ liệu) thay vì các đối tượng. Rất nhiều nhà phát triển Java không thể phân biệt sự khác biệt giữa một đối tượng được hình thành tốt (không chỉ là một lớp con của java.lang.Object, mà là một đối tượng thực sự trong một miền cụ thể) và một quả dứa. Ergo, cuối cùng họ viết các cấu trúc khi họ cần các đối tượng và ngược lại.


quả dứa làm tôi cười :)
Guillaume

Tuy nhiên, câu trả lời này không nói gì về sự khác biệt đó. Rầm rộ trên các nhà phát triển không có kỹ năng không giúp những nhà phát triển đó biết khi nào nên tạo cấu trúc và khi nào nên tạo một lớp.
ToolmakerSteve

Nó không nói bất cứ điều gì về sự khác biệt bởi vì tác giả của họ nhận thức được sự khác biệt (anh ta biết thực thể giống như cấu trúc là gì). Tác giả đang hỏi liệu sử dụng structs có phù hợp với ngôn ngữ OO không và tôi nói có (tùy thuộc vào miền vấn đề.) Nếu câu hỏi là về điều gì làm cho một đối tượng trở thành một đối tượng miền thực, thì bạn sẽ có một chân để đứng trong tranh luận.
luis.espinal

Hơn nữa, loại nhà phát triển không có kỹ năng duy nhất không biết sự khác biệt sẽ là loại vẫn còn ở trường. Điều này là vô cùng cơ bản, giống như biết sự khác biệt giữa danh sách được liên kết và bảng băm. Nếu một người tốt nghiệp với bằng phần mềm 4 năm mà không biết đối tượng thực sự là gì, thì người đó không bị cắt cho sự nghiệp này, hoặc anh ta / cô ta nên quay lại trường và yêu cầu hoàn lại tiền. Tôi nghiêm túc đấy
luis.espinal

Nhưng để thỏa mãn khiếu nại của bạn, tôi sẽ cung cấp một câu trả lời (không liên quan gì đến câu hỏi ban đầu và xứng đáng với chủ đề riêng của nó). Đối tượng có hành vi và đóng gói trạng thái. Cấu trúc không. Đối tượng là máy trạng thái. Cấu trúc chỉ để tổng hợp dữ liệu. Nếu mọi người muốn có một câu trả lời phức tạp hơn, họ có thể tự do tạo ra một câu hỏi mới, nơi chúng ta có thể giải thích nó theo nội dung của trái tim mình.
luis.espinal

2

Vấn đề với việc sử dụng truy cập trường công cộng cũng giống như sử dụng phương thức mới thay vì phương thức xuất xưởng - nếu bạn đổi ý sau này, tất cả các cuộc gọi hiện tại đều bị hỏng. Vì vậy, từ quan điểm tiến hóa API, thông thường nên cắn viên đạn và sử dụng getters / setters.

Một nơi mà tôi đi theo một cách khác là khi bạn kiểm soát mạnh mẽ quyền truy cập vào lớp, ví dụ như trong một lớp tĩnh bên trong được sử dụng làm cấu trúc dữ liệu nội bộ. Trong trường hợp này, việc sử dụng truy cập trường có thể rõ ràng hơn nhiều.

Nhân tiện, theo khẳng định của e-bartek, IMO rất khó có khả năng hỗ trợ tài sản sẽ được thêm vào trong Java 7.


1
Điều này đúng, nhưng nếu bạn đang sử dụng Java thì điều đó không đáng lo ngại, vì bạn có thể cấu trúc lại toàn bộ cơ sở mã chỉ bằng một cú nhấp chuột (Eclipse, Netbeans, có thể là VIM và Emacs). Nếu bạn đã chọn cho một trong những người bạn năng động của nó, như Groovy, ngay cả việc đóng gói đơn giản cũng có thể khiến bạn sửa chữa hàng giờ hoặc nhiều ngày. May mắn thay, bạn có các trường hợp thử nghiệm sẽ thông báo cho bạn ... bạn phải sửa thêm bao nhiêu mã.
Dan Rosenstark

2
Tất nhiên, bạn cho rằng tất cả người dùng đều ở trong cơ sở mã của bạn nhưng tất nhiên, điều đó thường không đúng. Thông thường, thậm chí không thể vì nhiều lý do phi kỹ thuật khác nhau để thay đổi mã có thể nằm trong cơ sở mã của riêng bạn.
Alex Miller

2

Tôi thường sử dụng mẫu này khi xây dựng các lớp bên trong riêng để đơn giản hóa mã của mình, nhưng tôi không khuyên bạn nên phơi bày các đối tượng như vậy trong API công khai. Nói chung, bạn càng thường xuyên có thể làm cho các đối tượng trong API công khai của mình trở nên tốt hơn và không thể xây dựng đối tượng 'giống như cấu trúc' của bạn theo cách không thay đổi.

Bên cạnh đó, ngay cả khi tôi đang viết đối tượng này như một lớp bên trong riêng tư, tôi vẫn sẽ cung cấp một hàm tạo để đơn giản hóa mã để khởi tạo đối tượng. Phải có 3 dòng mã để có được một đối tượng có thể sử dụng khi một người sẽ làm chỉ là lộn xộn.


2

Một câu hỏi rất cũ, nhưng hãy để tôi đóng góp ngắn. Java 8 đã giới thiệu các biểu thức lambda và các tham chiếu phương thức. Biểu thức Lambda có thể là các tham chiếu phương thức đơn giản và không khai báo phần thân "đúng". Nhưng bạn không thể "chuyển đổi" một trường thành một tham chiếu phương thức. Như vậy

stream.mapToInt(SomeData1::x)

không hợp pháp, nhưng

stream.mapToInt(SomeData2::getX)

Là.


Trong trường hợp này bạn có thể sử dụng data -> data.x, điều này vẫn hợp lý
James Kraus

1

Tôi không thấy tác hại nếu bạn biết rằng nó sẽ luôn là một cấu trúc đơn giản và bạn sẽ không bao giờ muốn gắn hành vi với nó.


1

Đây là một câu hỏi về Thiết kế hướng đối tượng, không phải ngôn ngữ Java. Nói chung, đó là cách tốt để ẩn các loại dữ liệu trong lớp và chỉ hiển thị các phương thức là một phần của API lớp. Nếu bạn để lộ các loại dữ liệu nội bộ, bạn không bao giờ có thể thay đổi chúng trong tương lai. Nếu bạn ẩn chúng, nghĩa vụ duy nhất của bạn đối với người dùng là các kiểu đối số và trả về của phương thức.


1

Ở đây tôi tạo một chương trình để nhập Tên và Tuổi của 5 người khác nhau và thực hiện sắp xếp lựa chọn (tuổi khôn ngoan). Tôi đã sử dụng một lớp hoạt động như một cấu trúc (như ngôn ngữ lập trình C) và một lớp chính để thực hiện thao tác hoàn chỉnh. Dưới đây tôi đang cung cấp mã ...

import java.io.*;

class NameList {
    String name;
    int age;
}

class StructNameAge {
    public static void main(String [] args) throws IOException {

        NameList nl[]=new NameList[5]; // Create new radix of the structure NameList into 'nl' object
        NameList temp=new NameList(); // Create a temporary object of the structure

        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));

        /* Enter data into each radix of 'nl' object */

        for(int i=0; i<5; i++) {
            nl[i]=new NameList(); // Assign the structure into each radix

            System.out.print("Name: ");
            nl[i].name=br.readLine();

            System.out.print("Age: ");
            nl[i].age=Integer.parseInt(br.readLine());

            System.out.println();
        }

        /* Perform the sort (Selection Sort Method) */

        for(int i=0; i<4; i++) {
            for(int j=i+1; j<5; j++) {
                if(nl[i].age>nl[j].age) {
                    temp=nl[i];
                    nl[i]=nl[j];
                    nl[j]=temp;
                }
            }
        }

        /* Print each radix stored in 'nl' object */

        for(int i=0; i<5; i++)
            System.out.println(nl[i].name+" ("+nl[i].age+")");
    }
}

Đoạn mã trên không có lỗi và được kiểm tra ... Chỉ cần sao chép và dán nó vào IDE của bạn và ... Bạn biết gì không ??? :)


0

Bạn có thể tạo một lớp đơn giản với các trường công khai và không có phương thức nào trong Java, nhưng nó vẫn là một lớp và vẫn được xử lý theo cú pháp và về mặt phân bổ bộ nhớ giống như một lớp. Không có cách nào để tái tạo thực sự các cấu trúc trong Java.


0

Đôi khi tôi sử dụng lớp như vậy, khi tôi cần trả về nhiều giá trị từ một phương thức. Tất nhiên, đối tượng như vậy là ngắn ngủi và tầm nhìn rất hạn chế, vì vậy nó sẽ ổn.


0

Như với hầu hết mọi thứ, có quy tắc chung và sau đó có những trường hợp cụ thể. Nếu bạn đang thực hiện một ứng dụng đã đóng, bị bắt để bạn biết cách sử dụng một đối tượng cụ thể, thì bạn có thể thực hiện nhiều tự do hơn để ưu tiên khả năng hiển thị và / hoặc hiệu quả. Nếu bạn đang phát triển một lớp sẽ được sử dụng công khai bởi những người khác ngoài tầm kiểm soát của bạn, thì hãy nghiêng về mô hình getter / setter. Như với tất cả mọi thứ, chỉ cần sử dụng thông thường. Bạn có thể thực hiện một vòng ban đầu với công chúng và sau đó thay đổi chúng thành getter / setters sau đó.


0

Lập trình hướng theo khía cạnh cho phép bạn bẫy các bài tập hoặc tìm nạp và gắn logic chặn vào chúng, mà tôi đề xuất là cách đúng đắn để giải quyết vấn đề. (Vấn đề liệu chúng nên được công khai hay được bảo vệ hoặc được bảo vệ theo gói là trực giao.)

Do đó, bạn bắt đầu với các trường không bị chặn với vòng loại truy cập phù hợp. Khi các yêu cầu chương trình của bạn phát triển, bạn đính kèm logic để có thể xác thực, tạo một bản sao của đối tượng được trả về, v.v.

Triết lý getter / setter áp đặt chi phí cho một số lượng lớn các trường hợp đơn giản mà không cần thiết.

Cho dù phong cách khía cạnh là sạch hơn hay không là một phần định tính. Tôi sẽ thấy dễ dàng khi chỉ nhìn thấy các biến trong một lớp và xem logic riêng biệt. Trên thực tế, lý do để lập trình theo định hướng Apect là nhiều mối quan tâm đang xuyên suốt và ngăn chặn chúng trong cơ thể lớp không phải là lý tưởng (đăng nhập là một ví dụ - nếu bạn muốn đăng nhập tất cả, Java sẽ muốn bạn viết một loạt các getters và giữ chúng đồng bộ nhưng AspectJ cho phép bạn một lớp lót).

Vấn đề của IDE là cá trích đỏ. Nó không phải là quá nhiều gõ vì nó là ô nhiễm đọc và hình ảnh phát sinh từ get / bộ.

Các chú thích có vẻ tương tự như lập trình hướng theo khía cạnh ngay từ cái nhìn đầu tiên, tuy nhiên chúng yêu cầu bạn phải liệt kê triệt để các điểm cắt bằng cách đính kèm các chú thích, trái ngược với đặc tả kỹ thuật cắt điểm giống như thẻ hoang dã trong AspectJ.

Tôi hy vọng nhận thức về AspectJ ngăn mọi người giải quyết sớm các ngôn ngữ động.

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.