Vấn đề hình tròn-elip có thể được giải quyết bằng cách đảo ngược mối quan hệ?


12

Việc Circlemở rộngEllipse phá vỡ Nguyên tắc thay thế Liskov , bởi vì nó sửa đổi một điều kiện hậu: cụ thể, bạn có thể đặt X và Y độc lập để vẽ một hình elip, nhưng X phải luôn bằng Y cho các vòng tròn.

Nhưng không phải vấn đề ở đây gây ra bởi việc Circle có phải là kiểu con của Ellipse không? Chúng ta không thể đảo ngược mối quan hệ?

Vì vậy, Circle là siêu kiểu - nó có một phương thức duy nhất setRadius.

Sau đó, Ellipse mở rộng Circle bằng cách thêm setXsetY. Gọi setRadiustrên Ellipse sẽ đặt cả X và Y - nghĩa là hậu điều kiện trên setRadius được duy trì, nhưng giờ đây bạn có thể đặt X và Y độc lập thông qua giao diện mở rộng.


1
Bạn đã tìm hiểu Wikipedia trước chưa ( en.wikipedia.org/wiki/Circle-ellipse_probols )?
Doc Brown

1
vâng - tôi thậm chí còn liên kết nó trong câu hỏi của mình ...
HorusKol

6
Và điểm chính xác này được đề cập trong bài viết đó, vì vậy tôi không rõ bạn đang hỏi gì?
Philip Kendall

6
"Một số tác giả đã đề nghị đảo ngược mối quan hệ giữa hình tròn và hình elip, với lý do hình elip là một hình tròn có khả năng bổ sung. Thật không may, hình elip không thể đáp ứng nhiều bất biến của hình tròn; nếu Circle có bán kính phương thức, thì Ellipse sẽ có để cung cấp cho nó là tốt. "
Philip Kendall

2
Những gì tôi thấy là lời giải thích rõ ràng nhất về lý do tại sao vấn đề này có tiền đề xấu nằm ở dưới cùng của bài viết trên wikipedia: en.wikipedia.org/wiki/ . Tùy thuộc vào tình huống, có một số thiết kế sạch, nhưng nó phụ thuộc vào những gì bạn cần từ hai lớp này để làm , không phải .
Arthur Havlicek

Câu trả lời:


36

Nhưng không phải vấn đề ở đây gây ra bởi việc Circle có phải là kiểu con của Ellipse không? Chúng ta không thể đảo ngược mối quan hệ?

Vấn đề với vấn đề này (và vấn đề hình vuông / hình chữ nhật) là giả định rằng một mối quan hệ trong một miền (hình học) giữ trong một (hành vi) khác

Một hình tròn và hình elip có liên quan nếu bạn đang xem chúng thông qua lăng kính của lý thuyết hình học. Nhưng đó không phải là tên miền duy nhất bạn có thể nhìn vào.

Đối tượng thiết kế định hướng đối phó với hành vi .

Đặc điểm xác định của một đối tượng là hành vi mà đối tượng chịu trách nhiệm. Và trong lĩnh vực hành vi, một hình tròn và hình elip có hành vi khác nhau đến mức có lẽ tốt hơn hết là đừng nghĩ chúng có liên quan gì cả. Trong miền này, hình elip và hình tròn không có mối quan hệ đáng kể.

Bài học ở đây là chọn tên miền có ý nghĩa nhất đối với OOD, chứ không phải thử và đánh giày trong một mối quan hệ đơn giản vì nó tồn tại ở một miền khác.

Ví dụ phổ biến nhất trong thế giới thực của lỗi này là giả sử các đối tượng có liên quan (hoặc thậm chí cùng một lớp) vì chúng có dữ liệu tương tự ngay cả khi hành vi của chúng rất khác nhau. Đây là một vấn đề phổ biến khi bạn bắt đầu xây dựng các đối tượng "dữ liệu trước" bằng cách xác định nơi dữ liệu đi. Bạn có thể kết thúc với một lớp có liên quan thông qua dữ liệu có hành vi hoàn toàn khác nhau. Ví dụ: cả đối tượng thanh toán và đối tượng nhân viên có thể có thuộc tính "tổng lương", nhưng nhân viên không phải là một loại phiếu lương và phiếu lương không phải là một loại nhân viên.


Việc tách biệt mối quan tâm của miền (ứng dụng) so với khả năng hành vi và trách nhiệm của OOD là một điểm rất quan trọng. Ví dụ, trong một ứng dụng vẽ, có lẽ bạn phải biến hình tròn thành hình vuông, nhưng điều này không dễ dàng được mô hình hóa bằng cách sử dụng các lớp / đối tượng trong hầu hết các ngôn ngữ (vì các đối tượng thường không thể thay đổi lớp). Vì vậy, miền ứng dụng không phải luôn luôn ánh xạ tới cấu trúc phân cấp kế thừa của ngôn ngữ OOP nhất định và chúng ta không nên cố gắng ép buộc nó; trong nhiều trường hợp, thành phần là tốt hơn.
Erik Eidt

3
Câu trả lời này cho đến nay là điều tốt nhất tôi từng thấy về toàn bộ vấn đề, và khả năng xảy ra lỗi thiết kế có thể phát sinh như thế nào trong các trường hợp tổng quát hơn. Cảm ơn
HorusKol

1
@ErikEidt Vấn đề về hành vi thay đổi đối tượng có thể được giải quyết trong OOD thông qua phân tách. Ví dụ: nếu hình dạng biến đổi thành một vòng tròn, bạn không phải thay đổi lớp. Thay vào đó, lớp lấy một đối tượng hành vi hình học hiện tại mà bạn có thể trao đổi cho hành vi khác khi bạn biến hình. Lớp khác này chứa các quy tắc của hình dạng hình học hiện đang được mô hình hóa, và lớp hình dạng có thể biến đổi thành lớp này cho hành vi hình học. Nếu đối tượng biến thành một lớp khác, bạn thay đổi lớp hành vi thành một thứ khác.
Cormac Mulhall

2
@Cormac, đúng rồi! Nói chung, tôi gọi đó là một hình thức sáng tác, như tôi đã đề cập, mặc dù bạn có thể xác định, cụ thể hơn, một mẫu chiến lược hoặc một cái gì đó. Về bản chất, bạn có một danh tính không biến hình và những thứ khác có thể thay đổi. Tất cả đều làm nổi bật sự khác biệt giữa các khái niệm miền ứng dụng và các chi tiết của OOP của một ngôn ngữ nhất định và nhu cầu ánh xạ giữa chúng (ví dụ như kiến ​​trúc, thiết kế và lập trình).
Erik Eidt

1
Nhưng một công việc có thể là một mức lương.

8

Các vòng tròn là một trường hợp đặc biệt của các hình elip, cụ thể là cả hai trục của dấu chấm lửng đều giống nhau. Về cơ bản là sai trong miền vấn đề (hình học) để nói rằng các hình elip có thể là một loại hình tròn. Sử dụng mô hình thiếu sót này sẽ vi phạm nhiều đảm bảo của một vòng tròn, ví dụ, tất cả các điểm trên vòng tròn có cùng khoảng cách đến trung tâm. Điều đó cũng sẽ là một vi phạm Nguyên tắc thay thế Liskov. Làm thế nào một hình elip có bán kính duy nhất? (Không setRadius()nhưng quan trọng hơn getRadius())

Mặc dù việc mô hình hóa các vòng tròn dưới dạng một kiểu con của các hình elip không sai về cơ bản, nhưng chính sự ra đời của tính đột biến đã phá vỡ mô hình này. Không có setX()setY()phương pháp, không có vi phạm LSP. Nếu cần phải có một đối tượng với các kích thước khác nhau, tạo một thể hiện mới là một giải pháp tốt hơn:

class Ellipse {
  final double x;
  final double y;
  ...
  Ellipse withX(double newX) {
    return new Ellipse(x: newX, y: y);
  }
}

1
được rồi - vì vậy, nếu có một số giao diện chung giữa EllipseCircle(chẳng hạn như getArea) sẽ được trừu tượng hóa thành một loại Shape- có thể EllipseCirclephân loại phụ riêng biệt Shapevà đáp ứng LSP không?
HorusKol

1
@HorusKol Vâng. Hai lớp kế thừa một giao diện mà cả hai thực sự thực hiện chính xác là hoàn toàn tốt.
Ixrec

6

Đó là một sai lầm ngay từ đầu khi khăng khăng muốn có một lớp "Ellipse" và một lớp "Circle" trong đó một lớp là lớp con của lớp kia. Bạn có hai lựa chọn thực tế: Một là có các lớp riêng biệt. Họ có thể có một siêu lớp phổ biến, cho những thứ như màu sắc, cho dù đối tượng được lấp đầy, chiều rộng đường để vẽ, v.v.

Lớp khác là chỉ có một lớp có tên là "Ellipse". Nếu bạn có lớp đó, bạn có thể dễ dàng sử dụng nó để biểu diễn các vòng tròn (có thể có bẫy tùy thuộc vào chi tiết thực hiện; Ellipse sẽ có một số góc và việc tính toán góc đó không gặp rắc rối đối với hình elip hình tròn). Bạn thậm chí có thể có các phương pháp chuyên dụng cho các hình elip tròn, nhưng các "hình elip tròn" này vẫn sẽ là các đối tượng "Ellipse" đầy đủ.


Có thể có một phương thức IsCircle sẽ kiểm tra xem liệu một đối tượng cụ thể của lớp Ellipse trên thực tế có cả hai trục giống nhau không. Bạn cũng chỉ ra vấn đề góc. Vòng kết nối không thể được "xoay".

6

Cormac có một câu trả lời thực sự tuyệt vời, nhưng tôi chỉ muốn giải thích một chút về lý do cho sự nhầm lẫn ở nơi đầu tiên.

Kế thừa trong OO thường được dạy bằng cách sử dụng các phép ẩn dụ trong thế giới thực, như "táo và cam là cả hai loại trái cây". Thật không may, điều này dẫn đến niềm tin nhầm lẫn rằng các loại trong OO nên được mô hình hóa theo một số phân cấp phân loại hiện có độc lập với chương trình.

Nhưng trong thiết kế phần mềm, các loại nên được mô hình hóa theo yêu cầu của ứng dụng. Phân loại trong các lĩnh vực khác thường không liên quan. Trong một ứng dụng thực tế với các đối tượng "Apple" và "Orange" - giả sử một hệ thống quản lý hàng tồn kho cho siêu thị - chúng có thể sẽ không phải là các lớp riêng biệt, và các danh mục như "Trái cây" sẽ là các thuộc tính chứ không phải là siêu kiểu.

Vấn đề hình tròn-elip là cá trích đỏ. Trong hình học, một vòng tròn là một chuyên môn của hình elip, nhưng các lớp trong ví dụ của bạn không phải là hình hình học. Điều quan trọng, các hình hình học không thể thay đổi. Chúng có thể được chuyển đổi , mặc dù, nhưng sau đó một vòng tròn có thể được chuyển đổi thành một dấu chấm lửng. Vì vậy, một mô hình trong đó các vòng tròn có thể thay đổi bán kính nhưng không thay đổi thành dấu chấm lửng không tương ứng với hình học. Một mô hình như vậy có thể có ý nghĩa trong một ứng dụng cụ thể (giả sử là một công cụ vẽ) nhưng phân loại hình học không liên quan đến cách bạn thiết kế hệ thống phân cấp lớp.

Vậy Circle nên là một lớp con của Ellipse hay ngược lại? Nó hoàn toàn phụ thuộc vào các yêu cầu của ứng dụng cụ thể sử dụng các đối tượng này. Một ứng dụng vẽ có thể có các lựa chọn khác nhau trong cách xử lý hình tròn và hình elip:

  1. Coi các hình tròn và hình elip là các loại hình dạng khác nhau với giao diện người dùng khác nhau (ví dụ: hai tay cầm thay đổi kích thước trên hình elip, một tay cầm trên hình tròn). Điều này có nghĩa là bạn có thể có hình elip là hình tròn nhưng không phải hình tròn theo quan điểm của ứng dụng.

  2. Đối xử với tất cả các hình elip bao gồm các vòng tròn giống nhau, nhưng có tùy chọn "khóa" x và y về cùng một giá trị.

  3. Dấu chấm lửng chỉ là các vòng tròn trong đó một phép biến đổi tỷ lệ đã được áp dụng.

Mỗi thiết kế có thể sẽ dẫn đến mô hình đối tượng khác nhau -

Trong trường hợp đầu tiên, Circle và Ellipses sẽ là các lớp anh chị em

Trong phần 2, sẽ không có lớp Circle riêng biệt nào cả

Trong phần 3, sẽ không có lớp Ellipse riêng biệt. Vì vậy, vấn đề được gọi là hình tròn-elip không nhập hình ảnh vào bất kỳ trong số này.

Vì vậy, để trả lời câu hỏi như đặt ra: Có nên mở rộng hình elip? Câu trả lời là: Nó phụ thuộc vào những gì bạn muốn làm với nó. Nhưng có lẽ là không.


1
Một câu trả lời rất hay!
Utsav T

3

Theo các điểm LSP, một giải pháp 'phù hợp' cho vấn đề này là @HorusKol và @Ixrec xuất hiện - xuất phát cả hai loại từ Hình dạng. Nhưng nó phụ thuộc vào mô hình bạn đang làm việc, vì vậy bạn nên luôn luôn quay lại đó.

Điều tôi được dạy là:

Nếu loại phụ không thể thực hiện hành vi tương tự như siêu loại, mối quan hệ không giữ được tiền đề IS-A - nó cần được thay đổi.

  • Một loại phụ là một SUPERSET của siêu loại.
  • Một siêu loại là một SUBSET của loại phụ.

Bằng tiếng Anh:

  • Một loại dẫn xuất là một SUPERSET của loại cơ sở.
  • Loại cơ sở là một SUBSET của loại dẫn xuất.

(Thí dụ:

  • Một chiếc xe với ống xả xấu trai vẫn là một chiếc xe (theo một số người).
  • Một chiếc xe không có động cơ, bánh xe, giá lái, hệ thống truyền động và chỉ còn lại vỏ, không phải là 'xe hơi', nó chỉ là vỏ.)

Đó là cách phân loại hoạt động (nghĩa là trong thế giới động vật) và hiệu trưởng, trong OO.

Sử dụng điều này như định nghĩa của thừa kế và đa hình (luôn luôn được viết cùng nhau), nếu nguyên tắc này bị phá vỡ, bạn nên cố gắng nghĩ lại các loại bạn đang cố gắng mô hình hóa.

Như được đề cập bởi @HorusKul và @Ixrec, trong toán học, bạn có các loại được xác định rõ ràng. Nhưng trong toán học, một vòng tròn là một hình elip vì nó là một hình con của hình elip. Nhưng trong OOP đây không phải là cách kế thừa hoạt động. Một lớp chỉ nên kế thừa nếu nó là SUPERSET (phần mở rộng) của một lớp hiện có - nghĩa là nó vẫn là lớp cơ sở trong tất cả các ngữ cảnh.

Dựa vào đó, tôi nghĩ rằng giải pháp nên được điều chỉnh lại một chút.

Có loại cơ sở Hình dạng, sau đó là RoundedShape (có hiệu quả là một vòng tròn nhưng tôi đã sử dụng một tên khác ở đây DELIBERATELY ...)

... Rồi hình elip.

Theo cách đó:

  • RoundedShape là một hình dạng.
  • Ellipse là một RoundedShape.

(Điều này bây giờ có ý nghĩa với mọi người trong ngôn ngữ. Chúng tôi đã có một khái niệm được xác định rõ ràng về "vòng tròn" trong tâm trí của chúng tôi và những gì chúng tôi đang cố gắng thực hiện ở đây bằng cách khái quát hóa (tổng hợp) phá vỡ khái niệm đó.)


Các khái niệm được xác định rõ ràng của chúng tôi không phải lúc nào cũng hoạt động trong thực tế.

-1

Từ hình elip phối cảnh OO mở rộng vòng tròn, nó chuyên về nó bằng cách thêm một số thuộc tính. Các thuộc tính hiện có của vòng tròn vẫn giữ trong hình elip, nó chỉ phức tạp hơn và cụ thể hơn. Tôi không thấy bất kỳ vấn đề nào với hành vi trong trường hợp này như Cormac, hình dạng không có hành vi. Rắc rối duy nhất là theo nghĩa liguistic hoặc toán học, cảm thấy không đúng khi nói "hình elip là hình tròn". Bởi vì toàn bộ vấn đề của bài tập không được đề cập nhưng dù sao cũng là ẩn ý, ​​là phân loại các hình dạng hình học. Đó có thể là một lý do chính đáng để coi hình tròn và hình elip là đồng đẳng, không liên kết chúng bằng sự kế thừa và chấp nhận rằng chúng chỉ tình cờ có một số tính chất giống nhau và KHÔNG để tâm trí OO bị xoắn của bạn theo cách đó.

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.