it-swarm-vi.com

Trình xây dựng thường không nên gọi các phương thức

Tôi đã mô tả cho một đồng nghiệp tại sao một nhà xây dựng gọi một phương thức có thể là một phản mẫu.

ví dụ (trong C++ rỉ sét của tôi)

class C {
public :
    C(int foo);
    void setFoo(int foo);
private:
    int foo;
}

C::C(int foo) {
    setFoo(foo);
}

void C::setFoo(int foo) {
    this->foo = foo
}

Tôi muốn thúc đẩy tốt hơn thực tế này thông qua đóng góp bổ sung của bạn. Nếu bạn có ví dụ, tài liệu tham khảo sách, trang blog hoặc tên của các nguyên tắc, chúng sẽ rất được hoan nghênh.

Chỉnh sửa: Tôi đang nói chung, nhưng chúng tôi đang mã hóa bằng python.

12
Stefano Borini

Bạn chưa chỉ định một ngôn ngữ.

Trong C++, hàm tạo phải cẩn thận khi gọi hàm ảo, trong đó hàm thực tế mà nó đang gọi là lớp thực hiện. Nếu nó là một phương thức ảo thuần túy mà không có triển khai, đây sẽ là một vi phạm truy cập.

Một constructor có thể gọi các hàm không ảo.

Nếu ngôn ngữ của bạn là Java trong đó các hàm nói chung là ảo theo mặc định, điều đó có nghĩa là bạn phải hết sức cẩn thận.

C # dường như xử lý tình huống theo cách bạn mong đợi: bạn có thể gọi các phương thức ảo trong các hàm tạo và nó gọi phiên bản cuối cùng nhất. Vì vậy, trong C # không phải là một mô hình chống.

Một lý do phổ biến cho việc gọi các phương thức từ các hàm tạo là bạn có nhiều hàm tạo muốn gọi một phương thức "init" chung.

Lưu ý rằng các hàm hủy sẽ có cùng một vấn đề với các phương thức ảo, do đó bạn không thể có một phương thức "dọn dẹp" ảo nằm bên ngoài hàm hủy của bạn và hy vọng nó sẽ được gọi bởi hàm hủy của lớp cơ sở.

Java và C # không có hàm hủy, chúng có bộ hoàn thiện. Tôi không biết hành vi với Java.

C # xuất hiện để xử lý dọn dẹp chính xác về vấn đề này.

(Lưu ý rằng mặc dù Java và C # có bộ sưu tập rác, chỉ quản lý cấp phát bộ nhớ. Có một cách dọn dẹp khác mà bộ hủy của bạn cần làm là không giải phóng bộ nhớ).

26
CashCow

OK, bây giờ sự nhầm lẫn về phương thức lớp vs phương thức cá thể đã được xóa, tôi có thể đưa ra câu trả lời :-)

Vấn đề không nằm ở việc gọi các phương thức cá thể nói chung từ một hàm tạo; đó là với cách gọi phương thức ảo (trực tiếp hoặc gián tiếp). Và lý do chính là trong khi bên trong hàm tạo, đối tượng chưa được xây dựng đầy đủ . Và đặc biệt là các phần lớp con của nó hoàn toàn không được xây dựng trong khi hàm tạo của lớp cơ sở đang thực thi. Vì vậy, trạng thái bên trong của nó không nhất quán theo cách phụ thuộc ngôn ngữ và điều này có thể gây ra các lỗi tinh vi khác nhau trong các ngôn ngữ khác nhau.

C++ và C # đã được thảo luận bởi những người khác. Trong Java, phương thức ảo của loại dẫn xuất nhất sẽ được gọi, tuy nhiên kiểu đó chưa được khởi tạo. Vì vậy, nếu phương thức đó đang sử dụng bất kỳ trường nào từ loại dẫn xuất, các trường đó có thể chưa được khởi tạo đúng vào thời điểm đó. Vấn đề này được thảo luận chi tiết trong Effecive Java Phiên bản 2 , Mục 17: Thiết kế và tài liệu để thừa kế hoặc nếu không thì cấm.

Lưu ý rằng đây là trường hợp đặc biệt của vấn đề chung về xuất bản tham chiếu đối tượng sớm . Các phương thức sơ thẩm có một tham số this ẩn, nhưng việc truyền this rõ ràng cho một phương thức có thể gây ra các vấn đề tương tự. Đặc biệt là trong các chương trình đồng thời trong đó nếu tham chiếu đối tượng được xuất bản sớm sang một luồng khác, thì luồng đó có thể gọi các phương thức trên nó trước khi hàm tạo trong luồng đầu tiên kết thúc.

18
Péter Török

Tôi sẽ không coi các cuộc gọi phương thức ở đây là một antipotype trong chính nó, hơn nữa là một mùi mã. Nếu một lớp cung cấp một phương thức reset, nó sẽ trả một đối tượng về trạng thái ban đầu, sau đó gọi reset() trong hàm tạo là DRY. (Tôi không đưa ra bất kỳ tuyên bố nào về các phương thức thiết lập lại).

Đây là một bài viết có thể giúp đáp ứng sự hấp dẫn của bạn đối với chính quyền: http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/

Nó không thực sự là về các phương thức gọi, mà là về các hàm tạo quá nhiều. IMHO, các phương thức gọi trong hàm tạo là một mùi có thể chỉ ra rằng hàm tạo quá nặng.

Điều này có liên quan đến việc kiểm tra mã của bạn dễ dàng như thế nào. Lý do bao gồm:

  1. Thử nghiệm đơn vị liên quan đến rất nhiều sáng tạo và phá hủy - do đó việc xây dựng phải nhanh chóng.

  2. Tùy thuộc vào những gì các phương thức đó làm, có thể gây khó khăn cho việc kiểm tra các đơn vị mã rời rạc mà không dựa vào một số điều kiện tiên quyết (có khả năng không thể kiểm chứng) được thiết lập trong hàm tạo (ví dụ: lấy thông tin từ mạng).

7
Paul Butcher

Về mặt triết học, mục đích của nhà xây dựng là biến một đoạn bộ nhớ thô thành một thể hiện. Trong khi hàm tạo đang được thực thi, đối tượng chưa tồn tại, do đó gọi các phương thức của nó là một ý tưởng tồi. Rốt cuộc, bạn có thể không biết những gì họ làm trong nội bộ và họ có thể coi đối tượng đó ít nhất là tồn tại (duh!) Khi họ được gọi.

Về mặt kỹ thuật, có thể không có gì sai với điều đó, trong C++ và đặc biệt là Python, bạn phải cẩn thận.

Thực tế, bạn nên giới hạn các cuộc gọi chỉ các phương thức như vậy để khởi tạo các thành viên lớp.

3
user17621

Đây không phải là một vấn đề mục đích chung. Đó là một vấn đề trong C++, cụ thể là khi sử dụng kế thừa và phương thức ảo, vì việc xây dựng đối tượng xảy ra ngược và (các) con trỏ vtable được đặt lại với mỗi lớp của hàm tạo trong cấu trúc phân cấp thừa kế, vì vậy nếu bạn gọi một phương thức ảo, cuối cùng bạn có thể không nhận được một phương thức thực sự tương ứng với lớp bạn đang cố gắng tạo, nó đánh bại toàn bộ mục đích sử dụng các phương thức ảo.

Trong các ngôn ngữ có sane OOP, đặt con trỏ vtable chính xác ngay từ đầu, vấn đề này không tồn tại.

2
Mason Wheeler

Có hai vấn đề với việc gọi một phương thức:

  • gọi một phương thức ảo, có thể làm điều gì đó bất ngờ (C++) hoặc sử dụng các phần của các đối tượng chưa được khởi tạo
  • gọi một phương thức công khai (nên thực thi các bất biến lớp), vì đối tượng chưa nhất thiết phải hoàn thành (và do đó, bất biến của nó có thể không giữ được)

Không có gì sai khi gọi một hàm trợ giúp, miễn là nó không thuộc hai trường hợp trước.

2
Matthieu M.

Tôi không mua cái này. Trong một hệ thống hướng đối tượng, gọi một phương thức là điều duy nhất bạn có thể làm. Trên thực tế, đó ít nhiều là định nghĩa của "hướng đối tượng". Vì vậy, nếu một nhà xây dựng không thể gọi bất kỳ phương thức nào, thì có thể nó sẽ làm gì?

1
Jörg W Mittag

Trong O.O.P. về lý thuyết, điều đó không quan trọng, nhưng trong thực tế, mỗi ngôn ngữ lập trình O.O.P. xử lý các hàm tạo khác nha. Tôi không sử dụng phương pháp tĩnh rất thường xuyên.

Trong C++ & Delphi, nếu tôi phải đưa ra các giá trị ban đầu cho một số thuộc tính ("thành viên trường") và mã được mở rộng, tôi thêm một số phương thức phụ làm phần mở rộng của các hàm tạo.

Và đừng gọi các phương pháp khác làm những việc phức tạp hơn.

Đối với các phương thức "getters" & "setters", tôi thường sử dụng các biến riêng tư/được bảo vệ để lưu trữ trạng thái của chúng, cộng với các phương thức "getters" & "setters".

Trong hàm tạo, tôi gán các giá trị "mặc định" cho các trường trạng thái thuộc tính, KHÔNG CÓ gọi "bộ truy cập".

0
umlcat