Là getters bảo vệ tầm thường trắng trợn quá mức?


8

Một cái gì đó tôi thực sự chưa từng nghĩ đến trước đây (cú pháp AS3):

private var m_obj:Object;
protected function get obj():Object
{
    return m_obj;
}

private var m_str:String;
protected function get str():String
{
    return m_str;
}

Ít nhất các lớp con sẽ không thể đặt m_obj hoặc m_str (mặc dù chúng vẫn có thể sửa đổi m_obj).

Câu hỏi của tôi: Đây có phải là quá mức trắng trợn?


Câu hỏi của Lập trình viên này: Khi nào hoặc tại sao người ta nên sử dụng getters / setters cho các thuộc tính lớp thay vì chỉ đơn giản là biến chúng thành thuộc tính công khai? đã được đề xuất như là một bản sao.

Câu hỏi đó là khác nhau bởi vì nó chỉ giải quyết các thuộc tính công cộng và riêng tư và liệu một tài sản có nên được bọc bằng getters & setters hay không. Đối với câu hỏi của tôi, tôi đang tập trung vào các biến được bảo vệ và cách các lớp kế thừa sẽ tương tác với các biến đó.

Vì vậy, việc thực hiện thay thế sẽ là:

protected var m_obj:Object; //more accessible than a private variable with a protected getter
protected var m_str:String; //more accessible than a private variable with a protected getter

Câu hỏi của tôi tương tự bởi vì tôi đang nói về việc sử dụng các getters được bảo vệ tầm thường để chặn các lớp con viết thành các biến, nhưng để cho phép tất cả các truy cập khác.
Trong các ngôn ngữ như AS3, điều này thậm chí sẽ cho phép họ thực hiện bất kỳ thay đổi nào đối với các biến đối tượng có thể thay đổi, miễn là bản thân các tham chiếu không được sửa đổi.


2
tự hỏi mình đi; Có nguy hiểm không khi các lớp con có thể đặt nó thành một cái gì đó tùy ý nếu câu trả lời là có thì nó không quá mức
ratchet freak

1
Tôi đã chỉnh sửa câu hỏi để làm rõ hơn những gì tôi đang nói về việc không làm thay thế.
Panzercrisis

Câu trả lời:


6

Đây chỉ là quá mức trắng trợn?

Thông thường, đôi khi không.

Giữ m_objtrong một số trạng thái tốt đã biết giúp bảo vệ Nguyên tắc thay thế Liskov, giữ cho mã của bạn trở nên linh hoạt hơn và chất lượng cao hơn. Đôi khi bạn có thể tin tưởng những người thừa kế tôn trọng hành vi đó, đôi khi bạn không thể (do mô hình sử dụng hoặc sự tinh tế của hợp đồng mà nó thực hiện). Mặc dù mã / câu hỏi này cũng vấp phải một số lý do cho "Tại sao Mã sạch đề xuất tránh các biến được bảo vệ?"


Có đôi khi nó có giá trị không khi các lớp con vẫn có thể làm bất cứ điều gì chúng muốn m_obj ngoại trừ đặt nó thành null hoặc một thể hiện khác?
Panzercrisis

@Panzercrisis - nó phụ thuộc vào ngôn ngữ. Trong các ngôn ngữ được quản lý không có bộ nhớ, đặt nó thành null / thứ khác là một vấn đề lớn.
Telastyn

13

Getters bảo vệ tầm thường có ít nhất hai lợi thế thực tế so với một biến tiếp xúc:

  • Để gỡ lỗi, chúng là một nơi hoàn hảo để đặt một điểm dừng có điều kiện kích hoạt khi có giá trị hài hước đó vào chương trình của bạn. Sẽ khó hơn nhiều để có được sự kiểm soát tương tự nếu bạn truy cập trực tiếp vào giá trị.

  • Để chế nhạo một đối tượng, nếu bạn đang sử dụng phương pháp này để kiểm tra con cháu.

Chi phí sử dụng getters gần như bằng không, vì vậy không có lý do thực sự để không sử dụng chúng. Lanhung?


Cảm ơn bạn. Câu hỏi đã được chỉnh sửa cho rõ ràng.
Panzercrisis

Câu trả lời của tôi không đề cập đến publichoặc protectedthuộc tính của getter! :-)
Michael Le Barbier Grünewald

Oh xin lỗi về điều đó.
Panzercrisis

4
Chi phí là trong sự phình to mã. Chi phí không lớn, nhưng nó lớn hơn không. Chắc chắn, các getters là tầm thường, nhưng chúng phải được thông qua mỗi khi ai đó làm việc trên lớp. Và họ có thể sai: get x() ... { return _y; } Không ai sẽ gõ nó, nhưng điều đó có thể xảy ra do cắt và dán không chính xác.
kevin cline

3
Đó là một sự đánh đổi giữa các chi phí khác nhau, mặc dù. Và theo kinh nghiệm của tôi, chi phí theo dõi các lỗi trong đó các lớp con thỏa hiệp lớp cha mẹ của chúng theo những cách tinh tế là rất lớn so với chi phí duy trì getters và setters.
Julia Hayward

7

Bất kể bạn sử dụng getters hay truy cập trường, bất kỳ người đánh giá có kinh nghiệm nào cũng sẽ tìm ra vấn đề trong thiết kế của bạn và phàn nàn rằng các đối tượng của bạn phơi bày dữ liệu thay vì hành vi.

Lưu ý rằng việc thu hút "được bảo vệ" sẽ không giúp ích gì, vì mục đích của kế thừa là cho phép các lớp con điều chỉnh / điều chỉnh hành vi của cha mẹ ( LSP ), chứ không phải để biện minh cho việc gây rối bừa bãi với dữ liệu gốc. Như Bertrand Meyer nói,

Cả nguyên tắc Đóng-Đóng hay xác định lại trong thừa kế đều là một cách để giải quyết các lỗi thiết kế ... (Ngoại lệ tiềm năng duy nhất cho quy tắc này là trường hợp phần mềm bị lỗi mà bạn không được tự do sửa đổi.)

Lý luận "Getters là xấu" áp dụng một cách thiếu tôn trọng vào việc bạn đánh vần getrõ ràng hoặc che giấu nó đằng sau truy cập trường.

xem bài viết này hoặc bài viết này của Alan Holub ...

Bạn không nên sử dụng các phương thức truy cập (getters và setters) trừ khi thực sự cần thiết bởi vì các phương thức này phơi bày thông tin về cách một lớp được triển khai và do đó làm cho mã của bạn khó bảo trì hơn. Đôi khi các phương thức get / set là không thể tránh khỏi, nhưng một nhà thiết kế OO có kinh nghiệm có thể loại bỏ 99% số người truy cập hiện có trong mã của bạn mà không gặp nhiều khó khăn.

Các phương thức Getter / setter thường thực hiện theo cách của họ bởi vì người viết mã đã suy nghĩ theo thủ tục. Cách tốt nhất để thoát ra khỏi lối tư duy thủ tục đó là suy nghĩ về một cuộc trò chuyện giữa các đối tượng có trách nhiệm được xác định rõ ...

Nếu bạn thực sự quan tâm đến vấn đề này, điều đầu tiên cần xem xét là thiết kế lại mã để loại bỏ cả getters và truy cập trường không riêng tư , thay vì chơi các trò chơi trí tuệ vô bổ chọn cách nào không phù hợp nhất .

Đáng nhấn mạnh một lần nữa, che giấu thiết kế rắc rối đằng sau màn khói "được bảo vệ" sẽ không làm cho vấn đề biến mất - sự kế thừa không biện minh cho các thủ thuật như thế. Diễn giải câu hỏi của bạn, cố gắng thiết kế mọi thứ theo cách tránh việc thừa kế các lớp tương tác với các biến đó - không thông qua getters hay thông qua truy cập trường. Nói, đừng hỏi :

Mã thủ tục nhận thông tin sau đó đưa ra quyết định. Mã hướng đối tượng cho các đối tượng làm việc.

Dành thời gian và nỗ lực để quyết định cách nào để có được thông tin, trái với nguyên tắc cổ điển khuyên bạn nên tránh điều này hoàn toàn, bây giờ nghe có vẻ như quá mức cần thiết đối với tôi.


Chúng tôi học cùng trường. Câu hỏi không nên là, "ồ, lớp cơ sở không hoạt động đúng, vậy làm thế nào để tôi làm xáo trộn dữ liệu của nó", làm thế nào để tôi làm cho lớp cơ sở hoạt động để tôi không cần phải làm phiền với trạng thái nội bộ của nó? " Hiếm khi tôi thực hiện getters. Hầu như không bao giờ tôi thực hiện setters. Các phương thức trên các lớp của tôi là các lệnh trong tĩnh mạch của "Tell Don't Ask." Đôi khi tôi cảm thấy như một trong những trách nhiệm công việc chính của mình là sửa lỗi bằng cách ẩn các chi tiết triển khai được phơi bày bởi những lập trình viên lười biếng hoặc thiếu suy nghĩ.
dash-tom-bang

1

Trong trường hợp triển khai lớp bất biến, làm cho các biến nội bộ của bạn được bảo vệ sẽ phá vỡ hợp đồng bạn đã đặt, trong trường hợp đó, các getters được bảo vệ sẽ không quá mức cần thiết (mặc dù bạn sẽ phải trả lại các bản sao của biến của mình, không phải là con trỏ cho nó ).

Trong các lớp có thể thay đổi, tôi vẫn thích getters / setters để cấp quyền truy cập trực tiếp vào các biến nội bộ của tôi. Điều này đảm bảo rằng bạn có thể thay đổi cách bạn xử lý các biến nội bộ của mình theo ý muốn mà không sợ phá vỡ bất kỳ mã khách hàng nào. Ví dụ, hãy tưởng tượng bạn có một lớp Java như sau:

public abstract class SomeClass {
    protected int[] someVariable;

    // Rest of implementation
}

Mở rộng lớp khách hàng SomeClasssau đó sẽ trực tiếp truy cập someVariable, thao tác nó theo cách họ thấy phù hợp. Bây giờ, điều này có thể tạo ra tất cả các loại vấn đề nếu các lớp này không để someVariableở trạng thái chấp nhận được. Hơn nữa, nếu bạn quyết định trong một bản phát hành tiếp theo để thay đổi định nghĩa SomeClasssử dụng List<Integers>thay vì một mảng, thì bạn sẽ phá vỡ tính tương thích với cơ sở mã hiện có, dự kiến SomeClasssẽ có một mảng bên trong int, không phải là danh sách ints.

Sử dụng getters được bảo vệ trong trường hợp này sẽ cho phép bạn thực hiện thay đổi này mà không phá vỡ bất kỳ hợp đồng được thiết lập trước.

public abstract class SomeClass {
    List<Integer> someVariable;

    protected int[] getSomeVariable () {
        // Return int[] version of someVariable
    }

    // Rest of implementation
}
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.