Có một chút bí mật để nói ít nhất là bạn đã nhận ra điều đó có thể khiến tôi gãi đầu trong giây lát khi tôi bắt đầu bắt gặp mã của bạn tự hỏi bạn đang làm gì và nơi các lớp trợ giúp này được triển khai cho đến khi tôi bắt đầu chọn phong cách của bạn / thói quen (tại thời điểm đó tôi có thể hoàn toàn quen với nó).
Tôi làm như thế là bạn đang giảm lượng thông tin trong các tiêu đề. Đặc biệt là trong các cơ sở mã rất lớn, điều đó có thể có tác dụng thực tế để giảm sự phụ thuộc thời gian biên dịch và cuối cùng là xây dựng thời gian.
Tuy nhiên, phản ứng ruột của tôi là nếu bạn cảm thấy cần phải che giấu chi tiết triển khai theo cách này, để ưu tiên tham số chuyển đến các hàm đứng tự do với liên kết nội bộ trong tệp nguồn. Thông thường, bạn có thể triển khai các hàm tiện ích (hoặc toàn bộ các lớp) hữu ích để triển khai một lớp cụ thể mà không cần truy cập vào tất cả các phần bên trong của lớp và thay vào đó chỉ chuyển các hàm có liên quan từ việc triển khai phương thức sang hàm (hoặc hàm tạo). Và tự nhiên có phần thưởng giảm sự ghép nối giữa lớp của bạn và "người trợ giúp". Nó cũng có xu hướng khái quát hóa những gì có thể là "người trợ giúp" nếu bạn thấy rằng họ đang bắt đầu phục vụ một mục đích tổng quát hơn áp dụng cho nhiều hơn một lớp thực hiện.
Đôi khi tôi cũng co rúm một chút khi tôi thấy rất nhiều "người trợ giúp" trong mã. Điều đó không phải lúc nào cũng đúng nhưng đôi khi chúng có thể là triệu chứng của một nhà phát triển, người chỉ phân tách các hàm willy-nilly để loại bỏ sự sao chép mã với các đốm dữ liệu khổng lồ được truyền qua các hàm với các mục đích / tên gần như không thể hiểu được ngoài thực tế là chúng làm giảm số lượng mã cần thiết để thực hiện một số chức năng khác. Chỉ cần một chút suy nghĩ nhỏ hơn đôi khi có thể dẫn đến sự rõ ràng hơn nhiều về cách thức thực hiện của một lớp được phân tách thành các hàm tiếp theo và ưu tiên truyền các tham số cụ thể để truyền toàn bộ các đối tượng của bạn xung quanh với quyền truy cập đầy đủ vào bên trong phát huy phong cách tư duy thiết kế đó. Tôi không đề nghị bạn làm điều đó, tất nhiên (tôi không có ý tưởng),
Nếu điều đó trở nên khó sử dụng, tôi sẽ xem xét một giải pháp thứ hai, thành ngữ hơn đó là pimpl (Tôi nhận ra bạn đã đề cập đến các vấn đề với nó nhưng tôi nghĩ bạn có thể khái quát hóa một giải pháp để tránh những vấn đề đó với nỗ lực tối thiểu). Điều đó có thể di chuyển toàn bộ thông tin mà lớp của bạn cần được triển khai bao gồm cả dữ liệu riêng tư của nó khỏi bán buôn tiêu đề. Các vấn đề về hiệu năng của pimpl phần lớn có thể được giảm thiểu bằng bộ cấp phát thời gian không đổi rẻ tiền * như một danh sách miễn phí trong khi bảo tồn ngữ nghĩa giá trị mà không phải thực hiện ctor sao chép do người dùng xác định đầy đủ.
- Đối với khía cạnh hiệu suất, pimpl ít nhất cũng giới thiệu một con trỏ trên đầu, nhưng tôi nghĩ các trường hợp phải khá nghiêm trọng khi đặt ra mối quan tâm thực tế. Nếu địa phương không gian bị suy giảm đáng kể thông qua bộ cấp phát, thì các vòng lặp chặt chẽ của bạn lặp lại đối tượng (thường là đồng nhất nếu hiệu suất là điều đáng lo ngại) vẫn có xu hướng giảm thiểu việc bỏ lỡ bộ nhớ cache trong thực tế với điều kiện bạn sử dụng một cái gì đó như một danh sách miễn phí để phân bổ pimpl, đưa các trường của lớp vào các khối bộ nhớ tiếp giáp phần lớn.
Cá nhân chỉ sau khi cạn kiệt những khả năng đó, tôi sẽ xem xét một cái gì đó như thế này. Tôi nghĩ rằng đó là một ý tưởng hay nếu phương án thay thế giống như các phương thức riêng tư hơn tiếp xúc với tiêu đề có lẽ chỉ có bản chất bí truyền của nó là mối quan tâm thực tế.
Một sự thay thế
Một sự thay thế xuất hiện trong đầu tôi lúc nãy, phần lớn hoàn thành mục đích tương tự mà bạn bè vắng mặt là như thế này:
struct PredicateListData
{
int somePrivateField;
};
class PredicateList
{
PredicateListData data;
public:
bool match() const;
};
// In source file:
static bool fullMatch(const PredicateListData& p)
{
// Can access p.somePrivateField here.
}
bool PredicateList::match() const
{
return fullMatch(data);
}
Bây giờ có vẻ như là một sự khác biệt rất lớn và tôi vẫn gọi nó là "người trợ giúp" (theo nghĩa có thể là xúc phạm vì chúng ta vẫn chuyển toàn bộ trạng thái bên trong của lớp cho hàm dù nó có cần hay không) ngoại trừ việc nó tránh được yếu tố "sốc" khi gặp phải friend
. Nhìn chung có friend
vẻ hơi đáng sợ khi thường xuyên vắng mặt kiểm tra thêm, vì nó nói rằng các lớp bên trong của bạn có thể truy cập được ở nơi khác (mang hàm ý rằng nó có thể không có khả năng duy trì bất biến của chính nó). Với cách bạn đang sử dụng, friend
nó sẽ trở nên khá hơn nếu mọi người biết về việc thực hành kể từ khifriend
chỉ nằm trong cùng một tệp nguồn giúp thực hiện chức năng riêng tư của lớp, nhưng ở trên thực hiện được nhiều hiệu quả tương tự ít nhất là với lợi ích có thể tranh cãi mà nó không liên quan đến bất kỳ người bạn nào tránh được loại đó ("Ồ bắn, lớp này có một người bạn. Những người khác của nó được truy cập / biến đổi ở đâu? "). Trong khi đó phiên bản ngay lập tức thông báo ngay lập tức rằng không có cách nào để các tư nhân được truy cập / biến đổi bên ngoài bất kỳ điều gì được thực hiện trong quá trình thực hiện PredicateList
.
Điều đó có lẽ đang hướng tới các lãnh thổ có phần giáo điều với mức độ sắc thái này vì bất kỳ ai cũng có thể nhanh chóng nhận ra nếu bạn đặt tên một cách thống nhất *Helper*
và đặt tất cả chúng vào cùng một tệp nguồn mà tất cả được kết hợp với nhau như là một phần của việc thực hiện riêng tư của một lớp. Nhưng nếu chúng ta trở nên kén chọn thì có lẽ phong cách ngay lập tức sẽ không gây ra phản ứng giật đầu gối trong nháy mắt mà không có friend
từ khóa có xu hướng trông hơi đáng sợ.
Đối với các câu hỏi khác:
Một người tiêu dùng có thể định nghĩa lớp PrimateateList_HelperFifts của riêng họ và cho phép họ truy cập vào các trường riêng. Mặc dù tôi không coi đây là một vấn đề lớn (nếu bạn thực sự muốn ở những lĩnh vực riêng tư đó, bạn có thể thực hiện một số vai diễn), có lẽ nó sẽ khuyến khích người tiêu dùng sử dụng nó theo cách đó?
Đó có thể là một khả năng xuyên qua các ranh giới API nơi khách hàng có thể xác định một lớp thứ hai có cùng tên và có quyền truy cập vào các phần bên trong theo cách đó mà không có lỗi liên kết. Sau đó, một lần nữa, tôi phần lớn là một lập trình viên C làm việc trong lĩnh vực đồ họa trong đó mối quan tâm về an toàn ở mức "nếu như" này rất thấp trong danh sách ưu tiên, vì vậy những lo ngại như đây chỉ là những vấn đề tôi có xu hướng vẫy tay và nhảy cố gắng giả vờ như chúng không tồn tại. :-D Nếu bạn đang làm việc trong một miền mà những mối quan tâm như thế này khá nghiêm trọng, tôi nghĩ đó là một sự cân nhắc hợp lý để thực hiện.
Đề xuất thay thế ở trên cũng tránh được vấn đề này. Nếu bạn vẫn muốn sử dụng friend
mặc dù, bạn cũng có thể tránh vấn đề đó bằng cách biến người trợ giúp thành một lớp lồng riêng.
class PredicateList
{
...
// Declare nested class.
class Helper;
// Make it a friend.
friend class Helper;
public:
...
};
// In source file:
class PredicateList::Helper
{
...
};
Đây có phải là một mẫu thiết kế nổi tiếng mà có một tên cho?
Không có kiến thức của tôi. Tôi nghi ngờ sẽ có một vì nó thực sự đi vào chi tiết vụn vặt về kiểu dáng và phong cách.
"Người trợ giúp địa ngục"
Tôi đã nhận được yêu cầu làm rõ thêm về vấn đề đôi khi tôi co rúm người khi nhìn thấy các triển khai có nhiều mã "người trợ giúp" và điều đó có thể gây tranh cãi với một số người nhưng thực tế là tôi đã thực sự co rúm khi tôi gỡ lỗi về việc các đồng nghiệp của tôi thực hiện một lớp chỉ để tìm vô số "người trợ giúp". :-D Và tôi không phải là người duy nhất trong nhóm gãi đầu cố gắng tìm ra chính xác những gì những người trợ giúp này phải làm chính xác. Tôi cũng không muốn từ chối giáo điều như "Ngươi không được sử dụng người giúp đỡ", nhưng tôi sẽ đưa ra một gợi ý nhỏ rằng có thể giúp suy nghĩ về cách thực hiện những điều vắng mặt họ khi thực tế.
Không phải tất cả các chức năng thành viên tư nhân chức năng trợ giúp theo định nghĩa?
Và vâng, tôi bao gồm các phương pháp riêng tư. Nếu tôi thấy một lớp học với giống như một giao diện công cộng đơn giản nhưng cũng giống như một bộ vô tận của các phương pháp riêng được phần nào bệnh được xác định trong mục đích như find_impl
hay find_detail
hay find_helper
, sau đó tôi cũng co rúm người lại theo một cách tương tự.
Điều tôi đề xuất thay thế là các hàm không kết bạn không liên kết với liên kết nội bộ (được khai báo static
hoặc bên trong một không gian tên ẩn danh) để giúp triển khai lớp của bạn với ít nhất một mục đích tổng quát hơn là "một chức năng giúp thực hiện các mục khác". Và tôi có thể trích dẫn Herb Sutter từ C ++ 'Tiêu chuẩn mã hóa' ở đây để biết lý do có thể thích hợp hơn từ quan điểm SE chung:
Tránh phí thành viên: Nếu có thể, thích thực hiện các chức năng không phải là bạn bè. [...] Các chức năng không kết bạn không phải là người cải thiện việc đóng gói bằng cách giảm thiểu các phụ thuộc: Phần thân của chức năng không thể phụ thuộc vào các thành viên không công khai của lớp (xem Mục 11). Họ cũng phá vỡ các lớp nguyên khối để giải phóng chức năng có thể tách rời, tiếp tục giảm khớp nối (xem Mục 33).
Bạn cũng có thể hiểu "phí thành viên" mà anh ấy nói về một mức độ nào đó theo nguyên tắc cơ bản là thu hẹp phạm vi biến. Nếu bạn tưởng tượng, như một ví dụ cực đoan nhất, một đối tượng Thần có tất cả mã cần thiết cho toàn bộ chương trình của bạn để chạy, sau đó ưu tiên "người trợ giúp" loại này (chức năng, cho dù là chức năng thành viên hoặc bạn bè) có thể truy cập tất cả các bên trong ( riêng tư) của một lớp về cơ bản làm cho các biến đó không kém vấn đề so với các biến toàn cục. Bạn có tất cả những khó khăn trong việc quản lý trạng thái chính xác và an toàn luồng và duy trì các bất biến mà bạn sẽ nhận được với các biến toàn cục trong ví dụ cực đoan này. Và tất nhiên hầu hết các ví dụ thực tế đều hy vọng không ở bất kỳ nơi nào gần với thái cực này, nhưng việc che giấu thông tin chỉ hữu ích vì nó giới hạn phạm vi của thông tin được truy cập.
Bây giờ Sutter đã đưa ra một lời giải thích hay ở đây nhưng tôi cũng nói thêm rằng việc tách rời có xu hướng thúc đẩy như một sự cải thiện tâm lý (ít nhất là nếu bộ não của bạn hoạt động như của tôi) về cách bạn thiết kế các chức năng. Khi bạn bắt đầu thiết kế các hàm không thể truy cập mọi thứ trong lớp ngoại trừ chỉ các tham số có liên quan bạn vượt qua hoặc, nếu bạn truyền thể hiện của lớp làm tham số, chỉ các thành viên công khai của nó, nó có xu hướng thúc đẩy tư duy thiết kế ủng hộ các chức năng có mục đích rõ ràng hơn, trên cơ sở tách rời và thúc đẩy đóng gói được cải thiện, so với những gì bạn có thể muốn thiết kế nếu bạn có thể truy cập mọi thứ.
Nếu chúng ta quay trở lại các điểm cực đoan thì một cơ sở mã hóa với các biến toàn cục không chính xác cám dỗ các nhà phát triển thiết kế các chức năng theo cách rõ ràng và có mục đích chung chung. Rất nhanh chóng, bạn càng có nhiều thông tin có thể truy cập vào một chức năng, càng nhiều người trong chúng ta phải đối mặt với sự cám dỗ làm suy biến nó và giảm sự rõ ràng của nó để truy cập tất cả thông tin bổ sung này mà chúng ta có thay vì chấp nhận các tham số cụ thể và phù hợp hơn với chức năng đó để thu hẹp quyền truy cập vào trạng thái và mở rộng khả năng ứng dụng và cải thiện ý định rõ ràng của nó. Điều đó áp dụng (mặc dù nói chung với một mức độ thấp hơn) với các chức năng thành viên hoặc bạn bè.