Tránh các cạm bẫy hướng đối tượng, di chuyển từ C, điều gì làm việc cho bạn?


12

Tôi đã lập trình bằng các ngôn ngữ thủ tục khá lâu rồi và phản ứng đầu tiên của tôi đối với một vấn đề là bắt đầu chia nhỏ nó thành các nhiệm vụ để thực hiện thay vì xem xét các thực thể (đối tượng) khác nhau tồn tại và các mối quan hệ của chúng.

Tôi đã có một khóa học đại học về OOP và hiểu các nguyên tắc cơ bản của đóng gói, trừu tượng hóa dữ liệu, đa hình, mô đun hóa và kế thừa.

Tôi đã đọc /programming/2688910/learning-to-think-in-the-object-oriented-way/programming/1157847/learning-object-oriented-thinking , và sẽ xem xét một số cuốn sách được chỉ ra trong những câu trả lời đó.

Tôi nghĩ rằng một số dự án có quy mô vừa và lớn của tôi sẽ được hưởng lợi từ việc sử dụng OOP hiệu quả nhưng như một người mới, tôi muốn tránh mất thời gian, các lỗi phổ biến.

Dựa trên kinh nghiệm của bạn, những cạm bẫy này là gì và những cách hợp lý xung quanh chúng là gì? Nếu bạn có thể giải thích tại sao chúng là những cạm bẫy và cách đề xuất của bạn có hiệu quả trong việc giải quyết vấn đề thì nó sẽ được đánh giá cao.

Tôi đang suy nghĩ theo dòng của một cái gì đó như "Có thông thường có một số lượng lớn các phương thức quan sát và sửa đổi và sử dụng các biến riêng tư hoặc có các kỹ thuật để hợp nhất / giảm chúng không?"

Tôi không lo lắng về việc sử dụng C ++ như một ngôn ngữ OO thuần túy, nếu có lý do chính đáng để trộn các phương thức. (Gợi nhớ các lý do để sử dụng GOTO, mặc dù ít.)

Cảm ơn bạn!


2
không xứng đáng với một câu trả lời hoàn chỉnh, nhưng một câu trả lời quan trọng khiến tôi mất nhiều thời gian để chấp nhận (tức là tôi đã đọc về nó rất nhiều thời gian nhưng coi nó như một cuộc nói chuyện của fanboy): thích các chức năng miễn phí hơn các chức năng thành viên. Điều này giữ cho lớp học của bạn tối thiểu là một điều tốt.
stijn

@stijn Về cơ bản, bạn đang nói nếu không cần phải vào lớp, đừng đặt nó ở đó. Ví dụ, tôi thấy rất nhiều hàm thành viên tiện ích có thể dễ dàng là các hàm miễn phí trong mã tôi đã đọc cho đến nay.
Stephen

vâng đó là nó. Nếu bạn tìm kiếm 'thích người không phải là thành viên', bạn sẽ tìm thấy rất nhiều thông tin về nó. Cuối cùng, nó tuân thủ Nguyên tắc Trách nhiệm duy nhất
stijn

Câu trả lời:


10

Một điều lớn tôi đã học được là thiết kế các lớp từ bên ngoài. Thiết kế giao diện trước khi bạn bắt đầu nghĩ về việc thực hiện. Điều này sẽ làm cho lớp trở nên trực quan hơn nhiều đối với người dùng của bạn (những người sử dụng lớp) so với việc viết các thuật toán cơ bản và xây dựng lớp và viết các hàm thành viên công cộng mới khi cần.


7
Thêm vào đó, chúng ta có thể 'lập trình theo ý định', nghĩa là viết một số mã mẫu sẽ sử dụng lớp mới, xem loại phương thức và chính sách nào giúp sử dụng thoải mái. Sau đó sử dụng thông tin đó làm cơ sở cho việc triển khai.

2
Tuyệt vời trong lý thuyết - giao diện thiết kế và về cơ bản bạn đã hoàn thành. Nhưng nó không hoạt động đơn giản trong thực tế. Thiết kế và thực hiện giao diện thường đi đôi với nhau trong các lần lặp liên tiếp cho đến khi giao diện cuối cùng được kết tinh. Tại thời điểm đó bạn có khả năng sẽ thực hiện cuối cùng là tốt.
Gene Bushuyev

9

Đầu tiên là cạm bẫy của việc tiết lộ quá nhiều thông tin. Mặc định nên private, không public.

Sau đó đến quá nhiều getters / setters. Hãy nói rằng tôi có một thành viên dữ liệu. Tôi có thực sự cần dữ liệu này trong một lớp khác không? Ok, làm cho một getter. Tôi có thực sự, thực sự cần phải thay đổi dữ liệu này trong suốt tuổi thọ của đối tượng không? Sau đó thực hiện một setter.

Hầu hết các lập trình viên mới làm quen có mặc định để tạo một getter / setter cho mọi thành viên dữ liệu. Giao diện clutter này và thường là lựa chọn thiết kế xấu.


2
vâng, nó khá phổ biến trong số những người hiểu đóng gói một cách hời hợt để làm cho dữ liệu riêng tư và sau đó thổi đóng gói với getters và setters.
Gene Bushuyev

Java là về ngôn ngữ phổ biến duy nhất vẫn cần các phương thức get / set. Mọi ngôn ngữ khác đều hỗ trợ các thuộc tính, do đó không có sự khác biệt về cú pháp giữa thành viên dữ liệu công khai và thuộc tính. Ngay cả trong Java, không có tội gì khi có các thành viên dữ liệu công khai nếu bạn cũng kiểm soát tất cả người dùng của lớp.
kevin cline

Thành viên dữ liệu công cộng khó duy trì hơn. Với getters / setters (hoặc thuộc tính), bạn có thể giữ giao diện trong khi thay đổi biểu diễn dữ liệu nội bộ, mà không thay đổi bất kỳ mã bên ngoài nào. Trong các ngôn ngữ nơi các thuộc tính giống hệt nhau về mặt cú pháp với các biến thành viên theo quan điểm của người tiêu dùng, điểm này không giữ được.
tdammers

Cho đến nay tôi nghĩ rằng tôi đang xem các thành viên tư nhân "sắp xếp" như các biến cục bộ trong một hàm ... phần còn lại của thế giới không cần biết gì về chúng để fn hoạt động ... và nếu fn thay đổi, chỉ có giao diện cần nhất quán. Tôi không có xu hướng đi với spam getter / setter vì vậy tôi có thể đang đi đúng hướng, thực tế là nhìn vào một lớp đệm tôi đã viết, không có thành viên dữ liệu nào cả. Cảm ơn!
Stephen

2

Khi tôi vượt qua giới hạn đó, tôi đã giải quyết theo cách tiếp cận sau:

0) Tôi bắt đầu chậm chạp, với các ứng dụng thủ tục quy mô nhỏ / vừa và không có công việc quan trọng nào trong công việc.

1) ánh xạ thông qua đầu tiên đơn giản về cách tôi sẽ viết chương trình từ đầu theo kiểu OO - quan trọng nhất đối với tôi tại thời điểm đó và điều này là chủ quan của IS - là tìm ra tất cả các lớp cơ sở. Đối tượng của tôi là gói gọn hết mức có thể trong các lớp cơ sở. Phương thức ảo thuần túy cho mọi thứ có thể trong các lớp cơ sở.

2) Sau đó, bước tiếp theo là tạo các đạo hàm.

3) bước cuối cùng là - trong mã thủ tục ban đầu, tách cấu trúc dữ liệu khỏi mã obsever / modifier. Sau đó, sử dụng ẩn dữ liệu và ánh xạ tất cả dữ liệu chung vào các lớp cơ sở và trong các lớp con đã chuyển dữ liệu không phổ biến trong chương trình. Và cách xử lý tương tự đối với mã trình quan sát / sửa đổi thủ tục - tất cả logic 'được sử dụng ở mọi nơi' đã đi vào các lớp cơ sở. Và xem / sửa đổi logic chỉ hành động trên một tập hợp con của dữ liệu đã đi vào các lớp dẫn xuất.

Điều này là chủ quan nhưng nó NHANH CHÓNG, nếu bạn biết rõ về quy trình và cấu trúc dữ liệu. FAIL trong đánh giá mã là khi một chút dữ liệu hoặc logic không xuất hiện trong các lớp cơ sở mà được sử dụng ở mọi nơi.


2

Hãy xem các dự án thành công khác sử dụng OOP để có được cảm giác về phong cách tốt. Tôi khuyên bạn nên xem Qt , một dự án mà tôi luôn hướng đến khi đưa ra quyết định thiết kế của riêng mình.


Đúng! Trước khi một người trở thành bậc thầy của một cái gì đó, anh ta học cách bắt chước những bậc thầy vĩ đại đã làm việc trước anh ta. Nghiên cứu mã tốt người khác thực hiện là một cách tốt để học. Tôi sẽ khuyên bạn nên nhìn vào boost, bắt đầu từ những thiết kế đơn giản và đi sâu hơn khi sự hiểu biết của một người được cải thiện.
Gene Bushuyev

1

Tùy thuộc vào người bạn nói chuyện, tất cả dữ liệu trong OOP phải ở chế độ riêng tư hoặc được bảo vệ và chỉ có sẵn thông qua các bộ truy cập và bộ biến đổi. Nói chung, tôi nghĩ rằng đây là một thực hành tốt, nhưng có những lúc tôi chuyển hướng khỏi định mức này. Chẳng hạn, nếu bạn có một lớp (bằng Java, giả sử) mục đích duy nhất của họ là đưa một số phần dữ liệu vào một đơn vị logic, sẽ rất hợp lý khi để các trường công khai. Nếu đó là một viên nang bất biến, chỉ cần đánh dấu chúng cuối cùng và khởi tạo chúng trong hàm tạo. Điều này làm giảm lớp (trong trường hợp này) xuống ít hơn một cấu trúc (trên thực tế, sử dụng C ++, bạn thực sự nên gọi đây là một cấu trúc. Hoạt động giống như một lớp, nhưng khả năng hiển thị mặc định là công khai và bạn có ý định rõ ràng hơn), nhưng Tôi nghĩ rằng bạn sẽ thấy rằng sử dụng nó sẽ thoải mái hơn nhiều trong những trường hợp này.

Một điều bạn chắc chắn không muốn làm là có một trường phải được kiểm tra tính nhất quán trong trình biến đổi và để công khai.


3
Tôi cũng đề nghị, nếu bạn có các lớp như vậy chỉ chứa dữ liệu công khai, bạn nên khai báo chúng là structs - nó không có sự khác biệt về ngữ nghĩa, nhưng nó làm rõ ý định và làm cho việc sử dụng chúng có vẻ tự nhiên hơn, đặc biệt là đối với các lập trình viên C.

1
Vâng, tôi đồng ý ở đây nếu bạn ở C ++ (câu hỏi đã đề cập), nhưng tôi làm hầu hết công việc của mình ở Java và thật không may, chúng tôi không có cấu trúc.

bạn cần phân biệt rõ ràng giữa các lớp tổng hợp (trường hợp không thường xuyên) và các lớp có chức năng (trường hợp chung). Cái sau phải thực hành đóng gói và chỉ truy cập các thành viên của nó thông qua giao diện công cộng, không có dữ liệu nào được công khai.
Gene Bushuyev

1

Các mô hình triển khai cuốn sách của Kent Beck là một nền tảng tuyệt vời trong cách sử dụng, không sử dụng sai, các cơ chế hướng đối tượng.


1

Tôi không lo lắng về việc sử dụng C ++ như một ngôn ngữ OO thuần túy, nếu có lý do chính đáng để trộn các phương thức. (Gợi nhớ các lý do để sử dụng GOTO, mặc dù ít.)

Tôi thực sự không nghĩ rằng tôi có rất nhiều điều để nói chuyện cho đến khi tôi thấy điều này. Tôi phải không đồng ý với tình cảm. OOP chỉ là một trong những mô hình có thể và nên được sử dụng trong C ++. Thành thật mà nói, theo tôi đó không phải là một trong những tính năng mạnh nhất của nó.

Từ quan điểm OO tôi nghĩ rằng C ++ thực sự giảm một chút. Ý tưởng về việc có các chức năng không ảo chẳng hạn là một dấu hiệu chống lại nó trong khía cạnh này. Tôi đã tranh luận với những người không đồng ý với tôi, nhưng các thành viên không ảo chỉ không phù hợp với mô hình theo như tôi quan tâm. Đa hình là một thành phần quan trọng đối với OO và các lớp có chức năng không ảo không phải là đa hình theo nghĩa OO. Vì vậy, là một ngôn ngữ OO, tôi nghĩ C ++ thực sự khá yếu khi so sánh với các ngôn ngữ như Java hoặc Objective-C.

Mặt khác, lập trình chung, C ++ có cái này khá tốt. Tôi đã nghe nói rằng cũng có những ngôn ngữ tốt hơn cho việc này, nhưng sự kết hợp giữa các đối tượng và các chức năng chung là một thứ gì đó khá mạnh mẽ và biểu cảm. Hơn nữa, nó có thể rất nhanh cả về thời gian lập trình VÀ thời gian xử lý. Thực sự trong lĩnh vực này tôi nghĩ rằng C ++ tỏa sáng mặc dù phải thừa nhận rằng nó có thể tốt hơn (ví dụ như hỗ trợ ngôn ngữ cho các khái niệm). Ai đó nghĩ rằng họ nên bám vào mô hình OO và đối xử với những người khác theo thứ tự của tuyên bố goto ở mức độ vô đạo đức thực sự bị bỏ lỡ khi không nhìn vào mô hình này.

Khả năng siêu lập trình của các mẫu cũng khá ấn tượng. Kiểm tra thư viện Boost.Units chẳng hạn. Thư viện này cung cấp hỗ trợ kiểu cho số lượng chiều. Tôi đã sử dụng rộng rãi thư viện này trong công ty kỹ thuật mà tôi hiện đang làm việc. Nó chỉ cung cấp phản hồi tức thì hơn nhiều cho một khía cạnh của lập trình viên có thể, hoặc thậm chí lỗi đặc tả. Không thể biên dịch chương trình sử dụng công thức trong đó cả hai mặt của toán tử '=' không tương đương về chiều mà không cần truyền rõ ràng. Cá nhân tôi không có kinh nghiệm với bất kỳ ngôn ngữ nào khác, trong đó điều này là có thể, và chắc chắn không phải với ngôn ngữ cũng có sức mạnh và tốc độ của C ++.

Metaprogramming là một mô hình chức năng hoàn toàn.

Vì vậy, thực sự, tôi nghĩ rằng bạn đã bước vào C ++ với một số hiểu lầm đáng tiếc. Các mô hình khác ngoài OO là không thể tránh được, chúng sẽ được CHUYỂN ĐỔI. Sử dụng mô hình tự nhiên cho khía cạnh của vấn đề bạn đang làm việc. Đừng ép các đối tượng về những gì thực chất không phải là một vấn đề dễ xảy ra. Theo như tôi quan tâm, OO thậm chí không phải là một nửa câu chuyện cho C ++.


Không phải từ khóa ảo cung cấp chức năng lớp ảo trong C ++? Theo như nhận xét của goto, điều tôi đang lái xe là tôi không quan tâm đến việc phá vỡ các quy tắc thực nghiệm, miễn là tôi hiểu lý do. Tuy nhiên, tôi đã tìm kiếm hướng tới thủ tục / thủ tục xấu hổ có lợi cho OO hơn là có thể cần thiết. Cảm ơn.
Stephen

phương pháp ảo không phải là điều kiện tiên quyết cho đa hình, nó chỉ là một cách phổ biến để làm điều đó. trong thực tế, mọi thứ khác bằng nhau, sau đó tạo một phương thức ảo thực sự làm suy yếu việc đóng gói, bởi vì bạn đang tăng kích thước của api của lớp và bạn làm cho việc đảm bảo theo dõi liskov trở nên khó khăn hơn. làm cho một cái gì đó ảo chỉ khi bạn cần một đường may, một số nơi để tiêm hành vi mới thông qua thừa kế (mặc dù thừa kế cũng là một điều cần thận trọng trong OOP). ảo vì lợi ích ảo không tạo ra một lớp "thêm OOP"
sara

1

Tôi muốn chấp nhận một câu trả lời cho câu hỏi này, nhưng tôi không thể quyết định một câu trả lời để ban cho dấu kiểm. Vì vậy, tôi đã nâng cao các tác giả ban đầu và tạo ra điều này như một câu trả lời tóm tắt. Cảm ơn tất cả mọi người đã dành vài phút, tôi thấy rằng sự sáng suốt mà bạn cung cấp đã cho tôi một hướng đi tốt và một chút trấn an rằng tôi đã không ra khỏi đường ray.

@ nightcracker

Đầu tiên là cạm bẫy của việc tiết lộ quá nhiều thông tin. Mặc định phải là riêng tư, không công khai. Sau đó đến quá nhiều getters / setters.

Tôi cảm thấy rằng tôi đã quan sát vấn đề này trong hành động trong quá khứ. Nhận xét của bạn khiến tôi cũng nhớ rằng bằng cách ẩn các biến cơ bản và cách triển khai của chúng, tôi có thể tự do thay đổi cách thực hiện mà không phá hủy bất cứ thứ gì phụ thuộc vào chúng.

Đa Minh Gurto

Thiết kế giao diện trước khi bạn bắt đầu nghĩ về việc thực hiện. Thiết kế và thực hiện Giao diện Gene Bushuyev thường đi đôi với nhau trong các lần lặp liên tiếp cho đến khi giao diện cuối cùng được kết tinh.

Tôi nghĩ nhận xét của Dominic là một lý tưởng tuyệt vời để khao khát, nhưng tôi nghĩ rằng nhận xét của Gene thực sự đánh vào thực tế của tình huống. Cho đến nay tôi đã thấy điều này trong hành động ... và cảm thấy tốt hơn một chút rằng nó không phải là hiếm. Tôi nghĩ rằng khi tôi trưởng thành như một lập trình viên, tôi sẽ nghiêng về các thiết kế hoàn thiện hơn, nhưng ngay bây giờ tôi vẫn phải chịu đựng việc nhảy vào và nhận được một số mã được viết.

muốn TheBest

Tôi bắt đầu chậm chạp, với các ứng dụng thủ tục quy mô nhỏ / vừa và không có công việc quan trọng nào trong công việc. trong mã thủ tục ban đầu, tách cấu trúc dữ liệu khỏi mã obsever / modifier

Điều này rất có ý nghĩa ... Tôi thích ý tưởng giữ mọi thứ hoạt động tại nơi làm việc, nhưng để cấu trúc lại một số nội dung không quan trọng với các lớp học.

chiều

Một điều bạn chắc chắn không muốn làm là có một trường phải được kiểm tra tính nhất quán trong trình biến đổi và để nó ở chế độ công khai

Tôi đã biết một thời gian rằng đây là một trong những thế mạnh của việc đóng gói dữ liệu ... có thể thực thi tính nhất quán và cho điều kiện / phạm vi / vấn đề đó.

Eddie điên

Ai đó nghĩ rằng họ nên bám vào mô hình OO và đối xử với những người khác theo thứ tự của tuyên bố goto ở mức độ vô đạo đức thực sự bị bỏ lỡ khi không nhìn vào mô hình này. Khả năng siêu lập trình của các mẫu cũng khá ấn tượng.

Ban đầu tôi đã bỏ lỡ rất nhiều trong câu trả lời của Crazy Eddie, tôi nghĩ bởi vì tôi đã không đọc về một số chủ đề được đề cập ... như siêu lập trình. Tôi nghĩ rằng thông điệp tuyệt vời trong bài viết của CE là C ++ là sự pha trộn giữa các khả năng và phong cách mà mỗi thứ nên được sử dụng cho tiềm năng tốt nhất của họ ... bao gồm cả bắt buộc nếu điều đó có ý nghĩa.

Vì vậy, một lần nữa, cảm ơn tất cả những người đã trả lời!


0

Cạm bẫy lớn nhất là niềm tin rằng OOP là một viên đạn bạc, hay "một mô hình hoàn hảo".

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.