JavaRush /Blog Java /Random-VI /Di truyền như một hiện tượng
articles
Mức độ

Di truyền như một hiện tượng

Xuất bản trong nhóm
Nói thật với bạn, ban đầu tôi không có ý định viết bài này. Tôi coi những vấn đề muốn bàn ở đây là chuyện nhỏ nhặt, thậm chí không có gì đáng nói. Tuy nhiên, trong quá trình viết bài cho trang này, tôi đã nêu ra một cuộc thảo luận về đa kế thừa trên một diễn đàn. Kết quả là hầu hết các nhà phát triển đều có hiểu biết rất mơ hồ về tính kế thừa. Và theo đó, anh ấy mắc rất nhiều sai lầm. Vì tính kế thừa là một trong những tính năng quan trọng nhất của OOP (nếu không muốn nói là quan trọng nhất!), nên tôi quyết định dành một bài viết riêng cho hiện tượng này. * * * Đầu tiên tôi muốn phân biệt hai khái niệm - đối tượng và lớp. Những khái niệm này liên tục bị nhầm lẫn. Trong khi đó, chúng là trung tâm của OOP. Và theo tôi, cần phải biết sự khác biệt giữa chúng. Vì vậy, đối tượng. Về cơ bản, nó là bất cứ thứ gì. Đây là khối lập phương. Gỗ, màu xanh. Chiều dài của cạnh là 5 cm, đây là một vật thể. Và có một kim tự tháp. Nhựa, màu đỏ. sườn 10cm. Đây cũng là một đối tượng Họ có đặc điểm gì chung? Kích cỡ khác nhau. Hình dạng khác nhau. Chất liệu khác nhau. Tuy nhiên, họ có một điểm chung. Trước hết, cả khối lập phương và kim tự tháp đều là những khối đa diện đều. Những thứ kia. tổng số đỉnh và số mặt lớn hơn số cạnh là 2. Hơn nữa. Cả hai hình đều có mặt, cạnh và đỉnh. Cả hai hình đều có đặc điểm như kích thước của xương sườn. Cả hai hình dạng có thể được xoay. Cả hai hình đều có thể được vẽ. Hai thuộc tính cuối cùng đã là hành vi. Và như thế. Thực hành lập trình cho thấy việc thao tác với các đối tượng đồng nhất sẽ dễ dàng hơn nhiều so với các đối tượng không đồng nhất. Và vì vẫn có điểm chung giữa những nhân vật này nên người ta mong muốn bằng cách nào đó làm nổi bật điểm chung này. Đây là nơi mà khái niệm giai cấp phát huy tác dụng. Vì vậy, định nghĩa.
Một lớp là một mô tả các thuộc tính chung của một nhóm đối tượng. Các thuộc tính này có thể vừa là đặc điểm của đối tượng (kích thước, trọng lượng, màu sắc, v.v.) vừa là hành vi, vai trò, v.v.
Bình luận. Từ “tất cả” (mô tả tất cả các thuộc tính) không được nói ra. Điều đó có nghĩa là bất kỳ đối tượng nào cũng có thể thuộc về nhiều lớp khác nhau. Kế thừa như một hiện tượng - 1 Hãy lấy ví dụ tương tự với các hình dạng hình học làm cơ sở. Mô tả chung nhất là khối đa diện đều . Bất kể kích thước cạnh, số mặt và đỉnh. Điều duy nhất chúng ta biết là hình này có các đỉnh, các cạnh và các mặt và độ dài của các cạnh bằng nhau. Hơn nữa. Chúng tôi có thể làm cho mô tả cụ thể hơn. Giả sử chúng ta muốn vẽ khối đa diện này . Hãy để chúng tôi giới thiệu một khái niệm như một khối đa diện đều được vẽ . Chúng ta cần gì để vẽ? Mô tả phương pháp vẽ chung không phụ thuộc vào tọa độ đỉnh cụ thể. Có lẽ là màu sắc của vật thể. Bây giờ hãy giới thiệu các lớp CubeTetrahedron . Các đối tượng thuộc các lớp này chắc chắn là các khối đa diện đều. Sự khác biệt duy nhất là số đỉnh, cạnh và mặt đã được cố định nghiêm ngặt cho từng lớp mới. Hơn nữa, biết loại của một hình cụ thể, chúng ta có thể đưa ra mô tả về phương pháp vẽ. Điều này có nghĩa là bất kỳ đối tượng nào của các lớp Khối lập phương hoặc Tứ diện cũng là một đối tượng của lớp đa diện đều được vẽ . Có một hệ thống phân cấp của các lớp. Trong hệ thống phân cấp này, chúng tôi đi từ mô tả chung nhất đến mô tả cụ thể nhất. Lưu ý rằng một đối tượng của bất kỳ lớp nào cũng phù hợp với mô tả của bất kỳ lớp tổng quát nào hơn trong hệ thống phân cấp. Mối quan hệ lớp này được gọi là sự kế thừa . Mỗi lớp con kế thừa tất cả các thuộc tính của lớp cha, tổng quát hơn và (có thể) thêm một số thuộc tính của chính nó vào các thuộc tính này. Hoặc nó ghi đè một số thuộc tính của lớp cha. Ở đây tôi muốn trích dẫn từ cuốn sách kinh điển của Gradi Bucha về thiết kế hướng đối tượng:
Do đó, tính kế thừa xác định một hệ thống phân cấp "là" giữa các lớp, trong đó lớp con kế thừa từ một hoặc nhiều lớp cha. Thực chất đây là phép thử để thừa kế. Với các lớp A và B, nếu A "không phải là" loại B thì A không phải là lớp con của B.
Dịch ra nó có vẻ như thế này:
Do đó, tính kế thừa xác định hệ thống phân cấp "is" giữa các lớp, trong đó một lớp con kế thừa từ một hoặc nhiều lớp cha. Trên thực tế, đây là bài kiểm tra xác định (nghĩa đen là bài kiểm tra giấy quỳ, ghi chú của tôi) về khả năng kế thừa. Nếu chúng ta có các lớp A và B, và nếu lớp A "không phải" là một biến thể của lớp B thì A không được là lớp con của B.
Những ai đọc đến đây chắc hẳn sẽ hoang mang chỉ tay vào thái dương. Ý nghĩ đầu tiên là điều này thật tầm thường! Điều này là đúng. Nhưng giá như bạn biết tôi đã thấy bao nhiêu hệ thống phân cấp thừa kế điên rồ! Trong cuộc thảo luận mà tôi đã đề cập ngay từ đầu, một trong những người tham gia đã khá nghiêm túc thừa kế một chiếc xe tăng từ... súng máy!!! Vì lý do đơn giản là xe tăng CÓ súng máy. Và đây là sai lầm phổ biến nhất. Kế thừa bị nhầm lẫn với tập hợp - sự bao gồm một đối tượng trong một đối tượng khác. Xe tăng không phải là súng máy, nó chứa một khẩu súng máy. Và vì lỗi này nên thường có mong muốn sử dụng nhiều quyền kế thừa. Bây giờ chúng ta hãy chuyển trực tiếp sang Java. Có gì về mặt thừa kế? Có hai loại lớp trong ngôn ngữ - những loại có khả năng chứa phần triển khai và những loại không thể làm như vậy. Cái sau được gọi là giao diện, mặc dù về bản chất chúng là các lớp hoàn toàn trừu tượng. Kế thừa như một hiện tượng - 2 Vì vậy, ngôn ngữ cho phép bạn kế thừa một lớp từ một lớp khác có khả năng chứa phần triển khai. NHƯNG CHỈ TỪ MỘT! Hãy để tôi giải thích tại sao điều này đã được thực hiện. Vấn đề là mỗi lần triển khai chỉ có thể xử lý phần của nó - với các biến và phương thức mà nó biết. Và ngay cả khi chúng ta kế thừa lớp C từ AB , thì phương thức processA , được kế thừa từ lớp A, chỉ có thể hoạt động với biến nội bộ a , bởi vì nó không biết gì về b , cũng như nó không biết gì về c và phương thức processC . Tương tự như vậy, phương thức processB chỉ có thể làm việc với biến b. Tức là về bản chất, các bộ phận được kế thừa đều bị cô lập. Lớp C chắc chắn có thể hoạt động với chúng, nhưng nó cũng có thể hoạt động tốt với những phần này nếu chúng chỉ được đưa vào như một phần của nó chứ không phải được kế thừa. Tuy nhiên, ở đây còn một điều phiền toái khác, đó là sự trùng lặp về tên gọi. Nếu các phương thức processAprocessB được đặt tên giống nhau, chẳng hạn như tiến trình, thì việc gọi phương thức tiến trình của lớp C sẽ có tác dụng gì ? Phương pháp nào trong hai phương pháp sẽ được gọi? Tất nhiên, C++ có các điều khiển trong tình huống này, nhưng điều này không tạo thêm sự hài hòa cho ngôn ngữ. Vì vậy, kế thừa triển khai không mang lại ưu điểm nhưng cũng có những nhược điểm. Vì lý do này, tính kế thừa triển khai này trong Java đã bị loại bỏ. Tuy nhiên, các nhà phát triển chỉ còn lại một tùy chọn như vậy cho nhiều kế thừa dưới dạng kế thừa từ một giao diện. Theo thuật ngữ Java, việc triển khai giao diện. Giao diện là gì? Một tập hợp các phương pháp. (Hiện tại chúng tôi không xem xét định nghĩa về hằng số trong giao diện; tìm hiểu thêm về điều đó tại đây .) Phương thức là gì? Và cốt lõi của một phương thức là xác định hành vi của một đối tượng. Không phải ngẫu nhiên mà tên của hầu hết mọi phương thức đều chứa một hành động - getXXX , drawXXX , countXXX , v.v. Và vì giao diện là một tập hợp các phương thức, nên trên thực tế, giao diệnyếu tố quyết định hành vi . Một tùy chọn khác để sử dụng giao diện là xác định vai trò của một đối tượng. Người quan sát, người nghe , v.v. Trong trường hợp này, phương pháp này thực sự là hiện thân của một phản ứng đối với một sự kiện bên ngoài nào đó. Đó là, một lần nữa, hành vi. Tất nhiên, một đối tượng có thể có nhiều hành vi khác nhau. Nếu nó cần được hiển thị, nó sẽ được hiển thị. Nếu anh ta cần được cứu, anh ta sẽ được cứu. Vâng, v.v. Theo đó, khả năng kế thừa từ các lớp xác định hành vi là rất rất hữu ích. Tương tự như vậy, một đối tượng có thể có nhiều vai trò khác nhau. Tuy nhiên, việc thực hiệnhành vi hoàn toàn phụ thuộc vào lương tâm của lớp trẻ. Kế thừa từ một giao diện (việc triển khai nó) nói rằng một đối tượng của lớp này sẽ có thể thực hiện điều này và điều kia. Và CÁCH thực hiện điều này được xác định độc lập bởi mỗi lớp triển khai giao diện. Hãy quay trở lại lỗi trong kế thừa. Kinh nghiệm của tôi trong việc phát triển các hệ thống khác nhau cho thấy rằng có tính kế thừa từ các giao diện, bạn có thể triển khai bất kỳ hệ thống nào mà không cần sử dụng nhiều tính kế thừa triển khai. Và do đó, khi tôi gặp phải những lời phàn nàn về việc thiếu tính đa kế thừa ở dạng nó tồn tại trong C++, đối với tôi đây là một dấu hiệu chắc chắn về thiết kế không chính xác. Lỗi phổ biến nhất mắc phải là lỗi tôi đã đề cập - kế thừa bị nhầm lẫn với tổng hợp. Đôi khi điều này xảy ra do những giả định không chính xác. Những thứ kia. Lấy đồng hồ tốc độ làm ví dụ, người ta cho rằng tốc độ chỉ có thể đo được bằng cách đo khoảng cách và thời gian, sau đó đồng hồ tốc độ được kế thừa thành công từ thước và đồng hồ, từ đó trở thành thước và đồng hồ, theo định nghĩa của di sản. (Yêu cầu đo thời gian bằng đồng hồ tốc độ của tôi thường được trả lời bằng những câu chuyện cười. Hoặc họ không trả lời gì cả.) Sai lầm ở đây là gì? Trong tiền đề. Thực tế là đồng hồ tốc độ không đo được thời gian. Và nhân tiện, cả khoảng cách nữa. Đồng hồ đo đường, được tìm thấy trong bất kỳ đồng hồ tốc độ nào, là một ví dụ cổ điển về thiết bị thứ hai trong cùng một vỏ, tức là. tổng hợp. Không cần thiết phải đo tốc độ. Nó có thể được loại bỏ hoàn toàn - điều này sẽ không ảnh hưởng đến việc đo tốc độ dưới bất kỳ hình thức nào. Đôi khi những sai lầm như vậy được thực hiện có chủ ý. Điều này còn tệ hơn nhiều. “Ừ, tôi biết là sai, nhưng như vậy sẽ thuận tiện hơn cho tôi.” Điều này có thể dẫn đến điều gì? Nhưng đây là điều: chúng ta sẽ kế thừa một chiếc xe tăng từ đại bác và súng máy. Cách này thuận tiện hơn. Kết quả là xe tăng trở thành đại bác và súng máy. Tiếp theo chúng ta sẽ trang bị cho máy bay hai súng máy và một khẩu pháo. Chúng ta nhận được gì? Một chiếc máy bay với vũ khí treo dưới dạng ba chiếc xe tăng! Bởi vì CHẮC CHẮN có một người sử dụng xe tăng làm súng máy mà không hiểu rõ. Độc quyền theo hệ thống phân cấp thừa kế. Và anh ấy sẽ hoàn toàn đúng, bởi vì người thiết kế hệ thống phân cấp như vậy đã mắc sai lầm.
Nói chung, tôi không thực sự hiểu cách tiếp cận “làm thế này sẽ thuận tiện hơn cho tôi ”. Thật thuận tiện khi viết như một người nghe, và những người nói ngữ pháp cơ bản đều là những người ngu ngốc. Tất nhiên, tôi đang phóng đại, nhưng ý chính vẫn là - ngoài sự tiện lợi nhất thời, còn có một thứ gọi là khả năng đọc viết. Khái niệm này được xác định dựa trên kinh nghiệm của rất nhiều người. Trên thực tế, đây là điều mà trong tiếng Anh gọi là “cách thực hành tốt nhất” - giải pháp tốt nhất. Và thường xuyên hơn không, những giải pháp có vẻ đơn giản hơn lại mang đến rất nhiều vấn đề trong tương lai.
Tất nhiên, ví dụ này đã được phóng đại quá mức và do đó vô lý. Tuy nhiên, có những trường hợp ít rõ ràng hơn nhưng vẫn dẫn đến hậu quả thảm khốc. Bằng cách kế thừa từ một đối tượng, thay vì tổng hợp nó, nhà phát triển cung cấp cho bất kỳ ai khả năng sử dụng trực tiếp chức năng của đối tượng gốc. Với tất cả những gì nó ngụ ý. Hãy tưởng tượng rằng bạn có một lớp làm việc với cơ sở dữ liệu, DBManager . Bạn tạo một lớp khác sẽ hoạt động với dữ liệu của mình bằng cách sử dụng DBManager - DataManager . Lớp này sẽ thực hiện kiểm soát dữ liệu, chuyển đổi, hành động bổ sung, v.v. Nói chung, là một lớp giữa lớp nghiệp vụ và lớp cơ sở. Nếu bạn kế thừa DataManager từ DBManager thì bất kỳ ai sử dụng nó sẽ có quyền truy cập trực tiếp vào cơ sở dữ liệu. Và do đó, anh ta sẽ có thể thực hiện bất kỳ hành động nào vượt qua sự kiểm soát, biến đổi, v.v. Được rồi, hãy giả sử rằng không ai muốn cố ý gây tổn hại và các hành động trực tiếp sẽ có thẩm quyền. Nhưng! Giả sử rằng cơ sở đã thay đổi. Ý tôi là, một số nguyên tắc kiểm soát hoặc chuyển hóa đã thay đổi. Trình quản lý dữ liệu đã được thay đổi. Tuy nhiên, mã trước đây đã làm việc trực tiếp với cơ sở dữ liệu sẽ tiếp tục hoạt động. Rất có thể họ sẽ không nhớ đến anh ấy. Kết quả sẽ là một lỗi của lớp mà những người tìm kiếm nó sẽ chuyển sang màu xám. Sẽ không bao giờ xảy ra với bất kỳ ai rằng họ làm việc với cơ sở dữ liệu bỏ qua Trình quản lý dữ liệu. Nhân tiện, một ví dụ từ cuộc sống thực. Phải mất một thời gian RẤT lâu để tìm ra lỗi. Cuối cùng, tôi sẽ nói lại. Kế thừa CHỈ nên được sử dụng khi có mối quan hệ "là". Bởi vì đây chính là bản chất của sự kế thừa - khả năng sử dụng các đối tượng của lớp con làm đối tượng của lớp cơ sở. Nếu không có mối quan hệ “is” giữa các lớp thì KHÔNG NÊN có sự kế thừa!!! Không bao giờ và trong mọi trường hợp. Và thậm chí còn hơn thế nữa – chỉ vì nó quá tiện lợi. Link nguồn gốc: http://www.skipy.ru/philosophy/inheritance.html
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION