Một cách để trả về nhiều giá trị trả về từ một phương thức: đặt phương thức bên trong lớp biểu thị giá trị trả về. Đó có phải là một thiết kế tốt?


15

Tôi cần trả về 2 giá trị từ một phương thức. Cách tiếp cận của tôi như sau:

  1. tạo một lớp bên trong với 2 trường sẽ được sử dụng để giữ 2 giá trị đó
  2. đặt phương thức bên trong lớp đó
  3. khởi tạo lớp và gọi phương thức.

Điều duy nhất sẽ được thay đổi trong phương thức là cuối cùng nó sẽ gán 2 giá trị đó cho các trường của thể hiện. Sau đó, tôi có thể giải quyết các giá trị đó bằng cách tham chiếu đến các trường của đối tượng đó.

Đó có phải là một thiết kế tốt và tại sao?


Một lựa chọn khác (có thể là một cái xấu): xem BitInteger[] java.math.BigInteger.divideAndRemainder(BitInteger val). Nó trả về 2 số nguyên là giá trị trả về của nó trong một mảng.
Earl Namless

Loại nào là hai giá trị bạn muốn trả về?
Tulains Córdova

Có thể trùng lặp - stackoverflow.com/questions/12668186/ .
m3th0dman

Câu trả lời:


15

Tôi sẽ tranh luận điều này dọc theo các dòng sau:

  • Tại sao chính xác phương thức của bạn trả về nhiều giá trị? Chúng ta đang nói về loại gắn kết nào - những giá trị đó có thực sự là các trường trên một lớp hay chúng chỉ được trả lại ngẫu nhiên theo cùng một phương thức, nhưng nếu không thì không liên quan? Nếu đó là cách thứ hai, bạn có thể muốn xem xét việc chia phương thức thành hai phương thức. Chỉnh sửa: sử dụng phán đoán của bạn ở đây; đôi khi một kiểu gắn kết "trùng hợp" có thể là lựa chọn tốt nhất. Một tùy chọn khác là sử dụng cấu trúc cặp hoặc tuple, mặc dù trong OOP, chúng thường không được nhìn thấy trong các API công khai (một số trường hợp ngoại lệ đáng chú ý là các bộ sưu tập tiêu chuẩn, v.v.).
  • Nếu các giá trị xứng đáng để tạo thành một lớp, tôi có thể khuyên bạn không nên sử dụng một lớp bên trong. Các lớp bên trong thường được sử dụng làm chi tiết triển khai bên trong, được ẩn từ bên ngoài. Có bất kỳ lý do tại sao kết quả này không phải là một lớp "toàn diện", theo đúng nghĩa của nó?
  • Ngoài việc giữ dữ liệu, hoạt động nào được áp dụng cho lớp mới này? Trong thiết kế hướng đối tượng, bạn muốn có hành vi liên quan gần với dữ liệu liên quan (dường như cũng là ý định của bạn). Có nên dùng phương pháp mà bạn đang đề cập đến không sống trên lớp này không?

Tóm lại, tôi sẽ xem liệu tôi có thể biến "đối tượng dữ liệu" này thành một lớp hoàn chỉnh với cả dữ liệu và hành vi hay không. Là một nhận xét bổ sung, bạn có thể muốn làm cho lớp không thay đổi, vì trạng thái của nó được đặt một lần. Làm cho nó không thay đổi sẽ giúp ngăn không cho nó được đặt không chính xác hoặc sửa đổi sau này (giả sử ai đó đặt một trong các trường thành null và chuyển nó đi cùng).

Chỉnh sửa: Như Patkos Csaba chỉ ra một cách chính xác, nguyên tắc được áp dụng ở đây là Nguyên tắc Trách nhiệm duy nhất ( SRP ) - lớp bạn đang cố gắng tạo nên thực sự có một trách nhiệm (được định nghĩa là lý do để thay đổi ). Hướng dẫn thiết kế này sẽ giúp bạn tìm hiểu xem hai trường của bạn có thuộc một lớp hay không. Để gắn bó với ví dụ Wikipedia, lớp của bạn có thể được xem như một loại báo cáo, trong trường hợp đó, nó phù hợp với SRP, nhưng thật khó để bình luận nếu không có thêm thông tin.


Mặc dù tôi đồng ý với ý kiến ​​chung của câu trả lời này, có những trường hợp hợp pháp khi hai phần dữ liệu liên quan chặt chẽ được tính toán với nhau, nhưng không có ý nghĩa gì để buộc chúng lại với nhau ở bất kỳ nơi nào khác trong chương trình. Trong trường hợp như vậy, nó có thể đủ sạch để trả lại một cái gì đó như Pair<OneClass, AnotherClass>. Một số người sẽ không đồng ý . Trong mọi trường hợp, Pairphải là một chi tiết triển khai và không bao giờ xuất hiện trong phương thức API công khai.
9000

@ 9000 Tôi đồng ý; Tôi thực sự có nghĩa là từ xem xét được thực hiện theo nghĩa đen trong trường hợp này, nghĩa là chia phương pháp có thể không phải luôn luôn là giải pháp tốt nhất, nó chỉ là một dấu hiệu thô theo hướng đó. Tôi sẽ thực hiện chỉnh sửa dọc theo những dòng đó.
Daniel B

1
Câu trả lời tốt. Điều duy nhất tôi muốn thêm là một tham chiếu đến Nguyên tắc Trách nhiệm duy nhất (SRP) ( en.wikipedia.org/wiki/Single_responsibility_principl ). Nếu được kiểm tra, rất có thể phương thức trong cuộc thảo luận chỉ là vi phạm SRP đơn giản và phân tách là một giải pháp đơn giản. Theo kinh nghiệm của tôi, bất cứ khi nào một phương thức muốn trả về 2 hoặc nhiều giá trị, trong 90% các trường hợp có 2 phương thức ở đó hoặc một lớp khác nên được trích xuất.
Patkos Csaba

@PatkosCsaba cảm ơn, đã chỉnh sửa để đưa nó vào. Tôi thường bám vào để giải thích mọi thứ về mặt khớp nối và sự gắn kết, nhưng tôi đoán các nguyên tắc RẮN được xem là các quy tắc cơ bản để sống trong những ngày này.
Daniel B

@DanielB: Tôi thấy RẮN là cấp độ cao hơn và có thể dễ hiểu các khái niệm hơn. Khớp nối và sự gắn kết vẫn là cơ sở, nhưng chúng ở cấp độ thấp hơn. RẮN sử dụng tuyệt vời của khớp nối và sự gắn kết để giải thích các nguyên tắc của nó và để trình bày chúng ở cấp độ chung hơn.
Patkos Csaba

10

Có khái niệm về một Tuple được đặc trưng trong các ngôn ngữ khác, chẳng hạn như Python.

Người ta có thể trả về một thể hiện của lớp chung chung này có thể dễ dàng sử dụng lại:

public class TypedTuple<L, R> implements Serializable {
private static final long serialVersionUID = 1L;

  protected L left;
  protected R right;

  protected TypedTuple() {
    // Default constructor for serialization
  }

  public TypedTuple(L inLeft, R inRight) {
    left = inLeft;
    right = inRight;
  }

  public L getLeft() {
    return left;
  }

  public R getRight() {
    return right;
  }
}

2
Đôi khi thật tốt khi có một phương thức tĩnh chung create(), để tránh phải chỉ định các tham số loại trong hàm tạo. Ngoài ra, tôi sẽ đặt tên lớp này Pairthay vì Tuple, chỉ có thể đại diện cho 2-tuples của các giá trị.
augurar

5

Có vẻ như lớp này đang nhận trách nhiệm từ một lớp khác, và điều này khiến tôi nghĩ rằng thiết kế này không tuyệt vời.

Đối với một phương thức trả về các giá trị đa dạng, tôi muốn

  • trả về một containter chung (ví dụ: Danh sách hoặc Bản đồ) có chứa các giá trị trả về

hoặc là

  • tạo một Class cho giá trị trả về, chỉ chứa các trường cần thiết + getters + hàm tạo với tất cả các trường

Ví dụ cho tùy chọn thứ hai:

public Class FooBarRetval {
   private String foo;
   private int bar;

   public FooBarRetval (String foo, int bar) {
      this.foo = foo;
      this.bar = bar;
   }

   public String getFoo() {
      return foo;
   }

   public int getBar() {
      return bar;
   }
}

Làm cho các trường công khai thêm tùy chọn thay đổi các giá trị một cách riêng biệt, mặc dù rõ ràng các giá trị có mối quan hệ (nếu không chúng sẽ không phải được trả về bởi cùng một phương thức). Tôi rất khuyến khích mô hình này trong tình huống đặc biệt này. Nhưng vấn đề chính là, cuộc thảo luận về các thuộc tính công khai và riêng tư không liên quan gì đến câu hỏi của OP và tôi thấy không có lý do nào cho câu cuối cùng trong một câu trả lời hay.
Scarfridge

Cái đầu tiên nên được ưu tiên.
Juanin

Scarfridge: Điểm lấy, câu cuối cùng loại bỏ.
user281377

2
Chỉ cần sử dụng các lĩnh vực cuối cùng công cộng, không có lý do để lãng phí thời gian với người truy cập.
augurar

1

Việc đưa nhiều giá trị trả về từ một phương thức vào lớp / cấu trúc của chính nó thường được sử dụng trong các hệ thống dựa trên thông báo có một lớp cho Yêu cầu và phản hồi . Một ví dụ về điều này là Giao thức truy cập đối tượng đơn giản (SOAP) .

Đó có phải là một thiết kế tốt và tại sao?

Ít nhất là nó khá phổ biến. Nếu điều này tốt hay xấu phụ thuộc vào usecase đặc biệt của bạn.


0

Câu trả lời ngắn: Bạn có thể trả về một mảng hoặc Danh sách có hai giá trị.

Cá nhân tôi sẽ viết hai phương pháp riêng biệt, như

int x = obj.getX();
int y = obj.getY();
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.