Dairesel sınıf bağımlılığı


12

Birbirine ihtiyaç duyan 2 sınıfa sahip olmak kötü bir tasarım mıdır?

GameEngineBirkaç GameStatenesnesi olan bir dersim olan küçük bir oyun yazıyorum . Birkaç oluşturma yöntemine erişmek için bu GameStatenesnelerin GameEnginesınıfı da bilmeleri gerekir - bu dairesel bir bağımlılıktır.

Buna kötü tasarım denir mi? Sadece soruyorum, çünkü tam olarak emin değilim ve şu anda hala bunları yeniden düzenleyebiliyorum.


2
Cevap evet olsaydı çok şaşırırdım.
doppelgreener

Mantıksal olarak ayrık iki soru olduğu için .. cevap bunlardan birine evet. Neden gerçekten bir şey yapan bir durum tasarlamak istiyorsunuz? durumu denetlemek ve eylemi gerçekleştirmek için bir yöneticiniz / denetleyiciniz olmalıdır. Bir durum nesnesinin başka bir şeyin sorumluluklarını üstlenmesine izin vermek biraz kafa karıştırıcıdır. Bunu yapmak istiyorsan, C ++ senin arkadaşın .
teodron

GameState sınıflarım, giriş, gerçek oyun, menü vb. GameEngine bu durumları bir yığın halinde saklar, bu yüzden bir durumu duraklatabilir ve bir menü açabilir veya bir cutscene oynayabilirim, ... GameState sınıflarının GameEngine'i bilmesi gerekir, çünkü motor pencereyi oluşturur ve oluşturma içeriği vardır .
shad0w

5
Unutmayın, tüm bu kurallar hızlı olmayı hedefler. Çalıştırması hızlı, Oluşturması hızlı, bakımı hızlı. Bazen bu yönler birbiriyle çelişir ve nasıl devam edeceğinize karar vermek için bir maliyet-fayda analizi yapmanız gerekir. Eğer bu kadar düşünürseniz ve bir bağırsak çağrısı yaparsanız, hala geliştiricilerin% 90'ından daha iyisini yapıyorsunuz. Eğer yanlış yaparsan seni öldürecek gizli bir canavar yok, o daha çok gizli bir ücretli gişe operatörü.
DampeS8N

Yanıtlar:


0

Doğası gereği kötü bir tasarım değildir, ancak kolayca kontrolden çıkabilir. Aslında bu tasarımı günlük kodlarınızda kullanıyorsunuz. Örneğin, vektör bunun ilk yineleyiciyi ve yineleyicilerin kaplarına işaretçi olduğunu bilir.

Şimdi sizin durumunuzda, GameEnigne ve GameState için iki ayrı sınıfa sahip olmak çok daha iyi. Temelde bu ikisi farklı şeyler yapıyor ve daha sonra GameState'i devralan birçok sınıf tanımlayabilirsiniz (oyununuzdaki her sahne için bir GameState gibi). Ve hiç kimse birbirine erişme ihtiyacını inkar edemez. Temel olarak GameEngine oyun siteleri çalıştırıyor, bu yüzden onlara bir işaretçi olmalı. GameState, GameEngine'de tanımlanan kaynakları kullanıyor (işlenmiş, fizik yöneticisi vb. Gibi).

Bu iki sınıfı birbiriyle birleştiremezsiniz ve yapmamalısınız çünkü doğası gereği farklı şeyler yaparlar ve birleştirmek kimsenin sevmediği çok büyük bir sınıfla sonuçlanır.

Şimdiye kadar, tasarımda dairesel bağımlılığa ihtiyacımız olduğunu biliyoruz. Bunu güvenli bir şekilde oluşturmanın birkaç yolu vardır:

  1. Önerdiğiniz gibi, her birinin bir işaretçisini diğerine koyabiliriz, bu en basit çözümdür, ancak kolayca kontrolden çıkabilir.
  2. Singleton / multiton tasarımını da kullanabilirsiniz, bu yöntemle GameEngine sınıfınızı bir singleton ve her GameStates'i bir multiton olarak tanımlamanız gerekir. Birçok geliştirici hem singleton hem de multiton'un AntiPattern olduğunu düşünmesine rağmen, bu tür tasarımı tercih ediyorum.
  3. Global değişkenleri kullanabilirsiniz, temel olarak Singleton / multiton ile aynı şeydir, ancak istedikleri örnek oluşturmak için programcı no'yu sınırlamadığı yönündeki küçük bir farktır.

Cevabını sonuçlandırmak için bu üç yöntemden herhangi birini kullanabilirsiniz, ancak tüm bu tasarımların gerçekten tehlikeli olduğunu ve kolayca sürdürülemez bir kodla sonuçlanabileceğini söylediğim için ikisinden birini fazla kullanmamaya dikkat etmelisiniz.


6

Birbirine ihtiyaç duyan 2 sınıfa sahip olmak kötü bir tasarım mıdır?

Biraz Kod Kokusu , ama biri onunla ayrılabilir. Oyununuzu çalıştırıp çalıştırmanın daha kolay ve hızlı yolu buysa, bunun için gidin. Ancak bunu aklınızda bulundurun, çünkü bir noktada yeniden düzenleme yapmak için iyi bir şansınız var.

C ++ ile olan şey, dairesel bağımlılıkların bu kadar kolay derlenmeyeceğidir , bu yüzden derlemenizi düzeltmek için zaman harcamak yerine onlardan kurtulmak daha iyi bir fikir olabilir.

Birkaç fikir için SO ile ilgili bu soruya bakınız .

[Tasarımım] kötü tasarım diyebilir misiniz?

Hayır, hala her şeyi bir sınıfa koymaktan daha iyidir.

Çok iyi değil, ama aslında gördüğüm çoğu uygulamaya oldukça yakın. Genellikle, oyun durumları için bir yönetici sınıfınız ( dikkat! ) Ve bir oluşturucu sınıfınız olur ve bunların tekton olması oldukça yaygındır. Yani dairesel bağımlılık "gizlidir", ancak potansiyel olarak oradadır.

Ayrıca, yorumlarda belirtildiği gibi, oyun devlet sınıflarının bir çeşit render gerçekleştirmesi biraz garip. Yalnızca durum bilgilerini tutmalılar ve oluşturma işlemi bir oluşturucu veya oyun nesnelerinin bazı grafik bileşenleri tarafından ele alınmalıdır.

Şimdi nihai tasarım olabilir . Diğer cevapların güzel bir fikir getirip getirmediğini merak ediyorum. Yine de, muhtemelen oyununuz için en iyi tasarımı bulabileceksiniz.


Neden bir kod kokusu olsun ki? Ebeveyn-çocuk ilişkisi olan nesnelerin çoğunun birbirini görmesi gerekir.
Kikaimaru

4
Çünkü hangi sınıftan neyin sorumlu olduğunu tanımlamamanın en kolay yolu . Her ikisinde de yeni kod eklemenin yapılabileceği kadar derinden bağlı iki sınıfta kolayca biter, böylece artık kavramsal olarak ayrılmazlar. Aynı zamanda spagetti kodu için açık bir kapıdır: A sınıfı bunun için B sınıfını çağırır, ancak B'nin A, vb. Mümkünse, değilse daha iyi olur.
Laurent Couvidou

Bu kaygan bir eğim hatasıdır. Statik sınıflar da kötü şeylere yol açabilir, ancak util sınıfları kod kokusu değildir. Ve Scene'in SceneNodes ve SceneNode'un Scene'e başvurması gibi şeyler yapmanın "iyi" bir yolu olduğundan gerçekten şüpheliyim, bu yüzden iki farklı sahneye ekleyemezsiniz ... Ve nerede duracaksınız? A gerekli B, B C gerektirir ve C A kodu kokusu mu gerektirir?
Kikaimaru

Gerçekten, bu aynı statik sınıflar gibidir. Dikkatli taşıyın, sadece gerekiyorsa kullanın. Şimdi deneyimlerden bahsediyorum ve bu şekilde düşünen tek kişi ben değilim , ama gerçekten, bu bir görüş meselesi. Benim fikrime göre OP tarafından verilen bağlamda, bu gerçekten kötü kokulu.
Laurent Couvidou

Bu wikipedia makalesinde bu durumdan bahsedilmiyor. Ve bu sınıfları görene kadar "Uygunsuz mahremiyet" olgusu diyemezsiniz.
Kikaimaru

6

Doğrudan birbirine atıfta bulunan 2 sınıfa sahip olmak genellikle kötü tasarım olarak kabul edilir, evet. Pratik terimlerle, kod üzerinden kontrol akışını takip etmek daha zor olabilir, nesnelerin mülkiyeti ve ömürleri karmaşık olabilir, bu da hiçbir sınıfın diğeri olmadan tekrar kullanılamayacağı anlamına gelir, kontrol akışının her ikisinin de dışında yaşaması gerektiği anlamına gelebilir bu sınıfların üçüncü bir 'arabulucu' sınıfında vb.

Bununla birlikte, 2 nesnenin birbirine başvurması çok yaygındır ve buradaki fark, genellikle bir yöndeki ilişkinin daha soyut olmasıdır. Örneğin, bir Model-Görünüm-Denetleyici sisteminde, View nesnesi Model nesnesine bir referans tutabilir ve Görünüm'ün kendisini ilgili verilerle doldurabilmesi için tüm yöntemlerine ve özelliklerine erişebilmesiyle ilgili tüm bilgileri bilir. . Model nesnesi, kendi verileri değiştiğinde Görünüm güncellemesini yapabilmesi için Görünüm'e bir referans tutabilir. Ancak, Modelin Görünüm'e bağımlı olmasını sağlayacak olan bir View referansına sahip olmak yerine, genellikle View genellikle 1 ile Gözlemlenebilir bir arayüz uygularUpdate()işlevini gösterir ve Model, bir Görünüm olabilecek Gözlenebilir bir nesneye başvuru içerir. Model değiştiğinde, Update()tüm Gözlemlenebilirlerini çağırır ve Görünüm Update(), Modeli geri arayarak ve güncellenmiş bilgileri alarak uygular . Buradaki fayda, Modelin Görünümler hakkında hiçbir şey bilmemesi (ve neden yapması gerekir?) Ve Görüşleri olmayanlar bile diğer durumlarda yeniden kullanılabilmesidir.

Oyunda da benzer bir durum var. GameEngine normalde GameStates hakkında bilgi sahibi olur. Ancak GameState'in GameEngine hakkında her şeyi bilmesi gerekmez - sadece GameEngine'deki belirli oluşturma yöntemlerine erişmesi gerekir. Bu, kafanızda (a) GameEngine'ın bir sınıf içinde çok fazla şey yapmaya çalıştığını ve / veya (b) GameState'in tüm oyun motoruna, sadece yenilenebilir bölüme ihtiyacı olmadığını söyleyen küçük bir alarm vermelidir.

Bunu çözmek için üç yaklaşım şunları içerir:

  • GameEngine'ı Yenilenebilir bir arabirimden türettirin ve bu başvuruyu GameState'e aktarın. Oluşturma parçasını yeniden düzenlediyseniz, sadece aynı arayüzü koruduğundan emin olmanız gerekir.
  • Oluşturma bölümünü yeni bir Renderer nesnesine katlayın ve bunun yerine GameStates'e iletin.
  • Olduğu gibi bırakın. Belki bir noktada GameState'in tüm GameEngine işlevlerine erişmek istersiniz. Ancak, bakımı kolay ve genişletilmesi kolay yazılımların genellikle her sınıfın dışarıdan olabildiğince az ifade etmesini gerektirdiğini unutmayın, bu yüzden şeyleri tek ve iyi performans gösteren alt nesnelere ve arabirimlere ayırın. tanımlı görev tercih edilir.

0

Genellikle düşük kuplaj ile yüksek kohezyona sahip olmak iyi bir uygulama olarak algılanmaktadır. İşte birleştirme ve uyum ile ilgili birkaç bağlantı.

wikipedia bağlantısı

Wikipedia uyumu

düşük bağlantı, yüksek kohezyon

en iyi uygulamada stackoverflow

Birbirini referans alan iki sınıfın olması yüksek bağlantıya sahiptir. Hakkında guice çerçeve amaçları bağımlılık enjeksiyon vasıtası ile düşük bağlama yüksek yapışkanlık seviyesine gelmesi için. Konuyu biraz daha okumanızı ve bağlamınız göz önüne alındığında kendi aramanızı yapmanızı öneririm.

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.