Daire-elips problemi ilişkiyi tersine çevirerek çözülebilir mi?


13

Having CircleuzatmakEllipse kırar Liskov yerdeğiştirme İlkesi yani X ve Y bağımsız olarak bir elips çizmek için ayarlayabilirsiniz, ancak X daima çevreler için Y eşit olmalıdır: Bir sonşartı değiştirir çünkü.

Ama burada Circle'ın Elips'in alt tipi olması nedeniyle ortaya çıkan sorun değil mi? İlişkiyi tersine çeviremez miydik?

Yani, Daire süper tiptir - tek bir yöntemi vardır setRadius.

Ardından, Elips ekleyerek Çemberi uzanır setXve setY. Arayan setRadiussetRadius üzerinde hedefşart tutulur anlamı, ama şimdi uzun bir arayüz üzerinden bağımsız X ve Y ayarlayabilirsiniz - Elips X ve Y her ikisi kuracak.



1
evet -
soruma bağladım

6
Ve bu tam olarak bu makalede ele alınmıştır, bu yüzden ne istediğine dair net değilim?
Philip Kendall

6
"Bazı yazarlar, bir elipsin ek yetenekleri olan bir daire olduğu gerekçesiyle, daire ve elips arasındaki ilişkiyi tersine çevirmeyi önermişlerdir. Maalesef, elipsler dairelerin değişmezlerinin çoğunu tatmin edememektedir; Circle'ın bir yöntem yarıçapı varsa, Elips şimdi bunu da sağlamak. "
Philip Kendall

3
Bu sorunun neden kötü binalara sahip olduğuna dair en açık açıklama olarak bulduğum wikipedia makalesinin en altında yer alıyor: en.wikipedia.org/wiki/… . Durumun bağlı olarak, birkaç temiz tasarımlar var, ama bu iki sınıfları için ihtiyacınız ne bağlı yapmak değil, olması .
Arthur Havlicek

Yanıtlar:


38

Ama burada Circle'ın Elips'in alt tipi olması nedeniyle ortaya çıkan sorun değil mi? İlişkiyi tersine çeviremez miydik?

Bu sorun (ve kare / dikdörtgen sorunu) yanlış bir etki alanı (geometri) başka bir ilişki (davranış) tutar bir ilişki varsayar

Bir daire ve elips, onları geometrik teorinin prizmasından izliyorsanız ilişkilidir. Ancak bakabileceğiniz tek alan bu değil.

Nesneye yönelik tasarım davranışla ilgilidir .

Bir nesnenin tanımlayıcı özelliği, nesnenin sorumlu olduğu davranıştır. Ve davranış alanında, bir daire ve elips o kadar farklı bir davranışa sahiptir ki, muhtemelen onları hiç ilgili olarak düşünmemek daha iyidir. Bu alanda, bir elips ve bir dairenin anlamlı bir ilişkisi yoktur.

Buradaki ders, OOD için en anlamlı olan alanı seçmektir, sadece farklı bir alanda var olduğu için bir ilişkide ayakkabı çekmeye çalışmayın.

Bu hatanın en yaygın gerçek dünya örneği, nesnelerin ilişkili olduklarını (hatta aynı sınıfla) varsaymaktır, çünkü davranışları çok farklı olsa bile benzer verilere sahiptirler . Bu, verilerin nereye gittiğini tanımlayarak "önce veri" nesneleri oluşturmaya başladığınızda sık karşılaşılan bir sorundur. Tamamen farklı davranışlara sahip verilerle ilişkili bir sınıfla sonuçlanabilir. Örneğin, hem maaş bordrosu hem de çalışan nesnelerinin "brüt maaş" özelliği olabilir, ancak bir çalışan maaş bordrosu değildir ve maaş bordrosu bir çalışan türü değildir.


(Uygulama) alanının endişelerini OOD'nin davranış ve sorumluluk yetenekleri ile ayırmak çok önemli bir noktadır. Örneğin, bir çizim uygulamasında, bir daireyi bir kareye dönüştürebilmeniz gerekir, ancak bu çoğu dilde sınıflar / nesneler kullanılarak kolayca modellenmez (nesneler genellikle sınıfı değiştiremez). Bu nedenle, uygulama etki alanı her zaman belirli bir OOP dilinin miras hiyerarşisiyle eşleşmez ve bunu zorlamaya çalışmamalıyız; birçok durumda, kompozisyon daha iyidir.
Erik Eidt

3
Bu cevap, tüm konu hakkında gördüğüm en iyi şey ve daha genel durumlarda tasarım hataları potansiyelinin nasıl ortaya çıkabileceği. Teşekkürler
HorusKol

1
@ErikEidt Bir nesne değiştirme davranışı sorunu, ayrışma yoluyla OOD'de çözülebilir. Örneğin, şekil verilebilir bir şekil bir daireye dönüşürse, sınıfı değiştirmeniz gerekmez. Bunun yerine sınıf, dönüştürdüğünüzde başka bir davranış için değiştirebileceğiniz geçerli bir geometrik davranış nesnesini alır. Bu diğer sınıf, halihazırda modellenen geometrik şeklin kurallarını içerir ve morphable shape sınıfı, geometrik davranış için bu sınıfa ayrılır. Nesne farklı bir sınıfa dönüşürse, davranış sınıfını başka bir sınıfa dönüştürürsünüz.
Cormac Mulhall

2
@Cormac, doğru! Genel olarak, daha önce de belirttiğim gibi, daha özel olarak bir strateji modelini veya başka bir şeyi tanımlayabilmenize rağmen, bahsettiğim gibi bir kompozisyon biçimi diyebilirim. Özünde, değişmeyen bir kimliğiniz ve daha sonra değiştirilebilecek diğer şeyleriniz var. Sonuç olarak, uygulama etki alanı kavramları ve belirli bir dilin OOP'sinin ayrıntıları ve bunlar arasında eşleme ihtiyacı (mimari, tasarım ve programlama) arasındaki farkın iyi bir vurgulanması.
Erik Eidt

1
Ancak bir iş maaş çeki olabilir.

8

Daireler elipslerin özel bir halidir, yani elipslerin her iki ekseni de aynıdır. Elipslerin bir tür daire olabileceğini belirtmek problem alanında (geometri) temelde yanlıştır. Bu kusurlu modelin kullanılması bir çemberin birçok garantisini ihlal edecektir, örneğin “çemberdeki tüm noktalar merkeze aynı mesafeye sahiptir”. Bu da bir Liskov İkame İlkesi ihlali olacaktır. Elipsin tek bir yarıçapı nasıl olurdu? ( setRadius()Daha da önemlisi getRadius())

Elipslerin bir alt tipi olarak dairelerin modellenmesi temelde yanlış olmasa da, bu modeli kıran mutasyona geçişin getirilmesidir. setX()Ve setY()yöntemleri olmadan, LSP ihlali yoktur. Farklı boyutlara sahip bir nesneye sahip olmanız gerekiyorsa, yeni bir örnek oluşturmak daha iyi bir çözümdür:

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

1
tamam - yani, Ellipseve arasında Circle(örneğin getArea) bir türe soyutlanabilecek bazı ortak arabirimler olsaydı Shape- LSP'den ayrı olarak alt tip olabilir Ellipseve onu tatmin edebilir mi? CircleShape
HorusKol

1
@HorusKol Evet. Her ikisinin de doğru şekilde uyguladığı bir arabirimi miras alan iki sınıf tamamen iyidir.
Ixrec

7

Cormac'ın gerçekten harika bir cevabı var, ama ilk başta kafa karışıklığı nedeni hakkında biraz ayrıntı vermek istiyorum.

OO'da kalıtım genellikle "elma ve portakal her ikisi de meyvelerin alt sınıflarıdır" gibi gerçek dünya metaforları kullanılarak öğretilir. Ne yazık ki bu, OO'daki türlerin, programdan bağımsız olarak mevcut bazı taksonomik hiyerarşilere göre modellenmesi gerektiğine dair yanlış inanca yol açmaktadır.

Ancak yazılım tasarımında, türler uygulamanın gereksinimlerine göre modellenmelidir. Diğer alanlardaki sınıflandırmalar genellikle önemsizdir. "Elma" ve "Portakal" nesneleri ile gerçek bir uygulamada - bir süpermarket için bir envanter yönetim sistemi - muhtemelen hiç farklı sınıflar olmayacak ve "Meyve" gibi kategoriler süpertipler yerine özellikler olacaktır.

Daire-elips problemi kırmızı bir ringa balığıdır. Geometride, bir daire bir elipsin uzmanlaşmasıdır, ancak örneğinizdeki sınıflar geometrik şekiller değildir. En önemlisi, geometrik figürler değişmez. Bunlar edilebilir dönüştürülmüş olsa da, ancak daha sonra bir çember olabilir , bir üç nokta dönüştürülecektir. Dolayısıyla, dairelerin yarıçapı değiştirebileceği ancak üç noktaya geçemediği bir model geometriye karşılık gelmez. Böyle bir model belirli bir uygulamada mantıklı olabilir (örneğin bir çizim aracı), ancak sınıflandırma hiyerarşisini nasıl tasarladığınız için geometrik sınıflandırma önemsizdir.

Circle, Elips'in bir alt sınıfı mı yoksa tam tersi mi olmalı? Tamamen bu nesneleri kullanan belirli bir uygulamanın gereksinimlerine bağlıdır. Bir çizim uygulamasının, çemberleri ve elipsleri tedavi etme konusunda farklı seçenekleri olabilir:

  1. Dairelere ve elipslere, farklı kullanıcı arayüzüne sahip farklı şekil türleri olarak davranın (örneğin, bir üç nokta üzerinde iki yeniden boyutlandırma tutamacı, bir daire üzerinde bir tutamaç). Bu, uygulama açısından geometrik olarak bir daire olan ancak bir Daire olmayan bir elips olabileceğiniz anlamına gelir.

  2. Daireler dahil tüm elipslere aynı şekilde davranın, ancak x ve y'yi aynı değere "kilitleme" seçeneğine sahiptir.

  3. Üç nokta yalnızca ölçekleme dönüşümünün uygulandığı dairelerdir.

Her olası tasarım farklı nesne modeline yol açacaktır -

İlk durumda, Circle ve Elips kardeş sınıfları olacak

İkincisinde, ayrı bir Circle sınıfı olmayacak

3. sınıfta ayrı bir Elips sınıfı olmayacak. Dolayısıyla, daire-elips sorunu denilen sorun, bunların hiçbirine resme girmez.

Soruyu olduğu gibi cevaplamak için: Daire elips uzatmalı mı? Cevap: Onunla ne yapmak istediğinize bağlı. Ama muhtemelen hayır.


1
Çok iyi bir cevap!
Utsav T

6

Başından beri, bir elipsin diğerinin bir alt sınıfı olduğu bir "Elips" ve "Circle" sınıfına sahip olmakta ısrar etmek bir hatadır. İki gerçekçi seçeneğiniz var: Biri ayrı sınıflara sahip olmak. Renk, nesnenin dolu olup olmadığı, çizim için çizgi genişliği vb. İçin ortak bir üst sınıfa sahip olabilirler.

Diğeri ise sadece "Elips" adlı bir sınıfa sahip olmaktır. Bu sınıfa sahipseniz, daireleri temsil etmek için kullanmak yeterince kolaydır (uygulama ayrıntılarına bağlı olarak tuzaklar olabilir; Elips bir açıya sahip olacaktır ve bu açının hesaplanması daire şeklindeki bir elips için sorun yaratmamalıdır). Dairesel elipsler için özel yöntemleriniz bile olabilir, ancak bu "dairesel elipsler" yine de tam "Elips" nesneler olacaktır.


Ellipse sınıfının belirli bir nesnesinin aslında her iki eksenin de aynı olup olmadığını kontrol edecek bir IsCircle yöntemi olabilir. Açı sorununa da dikkat çektiniz. Çevreler 'döndürülemez'.

3

LSP noktalarını takiben, bu soruna 'uygun' bir çözüm @HorusKol ve @Ixrec'in ortaya çıkmasıdır - her iki türü de Shape'dan türetmek. Ama bu, çalıştığınız modele bağlıdır, bu yüzden her zaman buna geri dönmelisiniz.

Bana öğretilen şey:

Alt tür, süper türle aynı davranışı gerçekleştiremezse, ilişki IS-A öncülünde bulunmaz - değiştirilmelidir.

  • Alt tür, süper türün bir SUPERSET'idir.
  • Süper tür, alt türün bir SUBSETidir.

İngilizcede:

  • Türetilmiş tip, baz tipinin bir SUPERSETidir.
  • Baz tipi, türetilmiş tipin bir SUBSETidir.

(Misal:

  • Kötü çocuk egzozuna sahip bir araba hala bir araba (bazılarına göre).
  • Motoru, tekerlekleri, direksiyon rafı, aktarma organları ve sadece mermisi olmayan bir araba bir 'araba' değil, sadece bir kabuktur.)

Sınıflandırma (hayvan dünyasında) ve prensip olarak OO'da böyle çalışır.

Bunu kalıtım ve polimorfizmin tanımı olarak kullanmak (her zaman birlikte yazılır), eğer bu ilke kırılırsa, modellemeye çalıştığınız türleri yeniden düşünmeye çalışmalısınız.

@HorusKul ve @Ixrec tarafından belirtildiği gibi, matematikte açıkça tanımlanmış tipleriniz vardır. Ancak matematikte, bir daire bir elipstir çünkü elipsin bir alt kümesidir. Fakat OOP'de kalıtım böyle çalışmaz. Bir sınıf yalnızca mevcut bir sınıfın SUPERSET (uzantısı) ise miras almalıdır - yani, tüm bağlamlarda hala temel sınıftır.

Buna dayanarak, çözümün biraz yeniden ele alınması gerektiğini düşünüyorum.

Bir Shape taban türüne, sonra RoundedShape'a sahip olun (etkili bir daire ama burada DELIBERATELY ...

... sonra Elips.

Bu şekilde:

  • RoundedShape bir Şekildir.
  • Elips bir RoundedShape'dir.

(Bu artık dilde insanlara mantıklı geliyor. Zaten zihnimizde açıkça tanımlanmış bir 'daire' kavramımız var ve burada genelleştirerek (toplama) bu kavramı bozarak yapmaya çalıştığımız şey.)


Açıkça tanımlanmış konseptlerimiz pratikte her zaman işe yaramaz.

-1

OO perspektifinden elips daireyi genişletir, bazı özellikler ekleyerek üzerinde uzmanlaşır. Çemberin mevcut özellikleri elips içinde hala tutulur, sadece daha karmaşık ve daha spesifik hale gelir. Bu durumda Cormac'ın yaptığı gibi davranışla ilgili herhangi bir sorun görmüyorum, şekillerin davranışları yok. Tek sorun, liguistik veya matematiksel anlamda "elips IS A çemberi" demenin doğru hissetmemesidir. Çünkü egzersizin sözü edilmeyen fakat yine de örtük olan tüm noktası geometrik şekilleri sınıflandırmaktı. Bu, çemberi ve elipsi akranları olarak kabul etmek, onları kalıtımla bağlamak ve aynı özelliklerden bazılarına sahip olduklarını kabul etmek ve bükülmüş OO zihninizin bu gözlemle yola çıkmasına izin VERMEMEK için iyi bir neden olabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.