OOP'de sıfır davranış nesnesi - tasarım ikilemim


94

OOP'un ardındaki temel fikir, veri ve davranışların (bu veri üzerine) ayrılmaz olması ve sınıfın bir nesnesi fikri ile birleştirilmeleridir. Nesnenin bununla (ve diğer verilerle) çalışan veri ve yöntemleri vardır. Açıkça OOP ilkelerine göre, sadece veri olan nesneler (C yapılarına benzer) bir anti-kalıp olarak kabul edilir.

Çok uzak çok iyi.

Sorun şu ki, kodumun son zamanlarda bu anti-patern doğrultusunda gittikçe daha fazla gittiğini farkettim. Bana göre, sınıflar ve gevşek eşleşmiş tasarımlar arasında gizlenmeye daha fazla bilgi edinmeye çalıştığımda, sınıflarım daha fazla davranışsız, davranışsız ve tüm davranışsız veri sınıfları olarak saf veri karışımı oluyor.

Genellikle sınıfları, diğer sınıfların varlığına ilişkin farkındalıklarını en aza indirecek ve diğer sınıfların arayüzleri hakkındaki bilgilerini en aza indirecek şekilde tasarlarım. Bunu özellikle yukarıdan aşağıya bir zorla uygularım, daha düşük seviyeli sınıflar daha üst seviyeli sınıfları bilmez. Örneğin:

Genel bir kart oyunu API'niz olduğunu varsayalım. Senin bir sınıfın var Card. Şimdi bu Cardsınıfın oyunculara görünürlüğü belirlemesi gerekiyor.

Tek yönlü sahip olmaktır boolean isVisible(Player p)üzerine Cardsınıfa.

Başka sahip olmaktır boolean isVisible(Card c)üzerine Playersınıfa.

Özellikle birinci Playersınıf sınıf hakkında daha düşük seviyeli bir Cardsınıfa bilgi verdiğinden ilk yaklaşımı beğenmedim .

Bunun yerine Viewport, Playerhangi kartların görülebildiğini belirleyen bir ve bir liste listesi verilen bir sınıfa sahip olduğumuz üçüncü seçeneği tercih ettim .

Ancak bu yaklaşım hem soyar Cardve Playerolası bir üye işlev sınıfları. Bunu, kartların görünürlüğünden başka şeyler için yaptığınızda, tüm işlevler çoğunlukla veri içermeyen sınıflar olan, yalnızca yöntemlerin olduğu gibi, yukarıdaki gibi yöntemlerle uygulandığından , geride bırakılırsınız Cardve Playertamamen veri içeren sınıflar kalırsınız Viewport.

Bu açıkça OOP'nin ana fikrine karşıdır.

Hangisi doğru yolu? Sınıf bağımlılıklarını en aza indirme, varsayılan bilgi ve eşleşmeyi en aza indirme, ancak tüm düşük seviyeli sınıfların sadece veri içerdiği ve yüksek seviyeli sınıfların tüm yöntemleri içerdiği garip tasarıma gerek duymadan nasıl devam etmeliyim? Herhangi bir problemi ortadan kaldıran sınıf tasarımında üçüncü bir çözümü veya perspektifi olan var mı?

PS İşte başka bir örnek:

Değişmeyen bir sınıfa DocumentIdsahip olduğunuzu, BigDecimal idbu üyeye sadece tek bir üye ve bir alıcı olduğunu varsayalım . Şimdi, bir yerde bu yönteme sahip olmalısınız, ki bu id için bir veri tabanından DocumentIdgeri döner Document.

Yapıyor musun:

  • DB'ye ve benzerlerine erişmek için kullanılan API, tek tek ısrarlılığınız ( ) hakkında bilgi Document getDocument(SqlSession)vererek, DocumentIdsınıfa yöntem ekleyin "we're using a database and this query is used to retrieve document by id". Ayrıca bu sınıf şimdi sadece derlemek için sebat JAR dosyasını gerektirir.
  • Yöntemi içeren başka bir sınıf ekleyin Document getDocument(DocumentId id), DocumentIdsınıfı ölü olarak bırakın, davranış yok, yapı benzeri sınıf.

21
Buradaki önerilerin bir kısmı tamamen yanlıştır, bu da altta yatan soruyu cevaplamayı çok zorlaştıracaktır. Sorularınızı olabildiğince öz ve fikirsiz tutun, daha iyi cevaplar alın.
pdr

31
"Bu açıkça OOP'nin ana fikrine karşıdır" - hayır, değil, ortak bir yanıltıcıdır.
Doktor Brown

5
Sanırım sorun, geçmişte “Nesne Oryantasyonu” için farklı okulların olması - aslında Alan Kay gibi insanlar tarafından kastedilmesi gerçeğinde yatmaktadır (bkz. Geekswithblogs.net/theArchitectsNapkin/archive/2013/09/08/). … ) Ve bu yol Rational’dan olan insanlar tarafından OOA / OOD bağlamında öğretildi ( en.wikipedia.org/wiki/Object-oriented_analysis_and_design ).
Doktor Brown

21
Bu çok iyi bir sorudur ve iyi bir şekilde yayınlanmıştır - diğer bazı yorumların aksine, söyleyebilirim. Programın nasıl yapılandırılacağına dair önerilerin ne kadar saf veya eksik olduğunu ve bunun ne kadar zor olduğunu ve ne kadar doğru yapmak için çaba sarfettiğine bakılmaksızın uygun bir tasarımın ne kadar ulaşılmaz olduğunu açıkça göstermektedir. Her ne kadar belirli bir soruya verilen açık cevap çoklu yöntemler olsa da, tasarımın altında yatan sorun devam etmektedir.
Thiago Silva

5
Kimin soygunsuz sınıfların anti-patern olduğunu söylüyor?
James Anderson

Yanıtlar:


42

Tanımladığınız şey anemik bir etki alanı modeli olarak bilinir . Birçok OOP tasarım ilkesinde olduğu gibi (Demeter Kanunu gibi), sadece bir kuralı yerine getirmek için geriye doğru eğilmeye değmez.

Tüm manzarayı bozmadığı ve kendileri için yapabilecekleri temizlik işini yapmak için diğer nesnelere güvenmedikleri sürece, değer torbalarına sahip olmak konusunda yanlış bir şey yoktur .

Sadece özelliklerini değiştirmek için ayrı bir sınıfınız olsaydı kesinlikle bir kod kokusu olurdu Card- eğer kendi başlarına ilgilenmeleri makul bir şekilde beklenebilirse.

Ama gerçekte kimin görebildiğini Cardbilmek bir iş Playermi?

Ve neden uygulamak Card.isVisibleTo(Player p), ancak değil Player.isVisibleTo(Card c)? Ya da tam tersi?

Evet, yaptığınız gibi Playerbunun için bir tür kural bulmaya çalışabilirsiniz - Card(?) ' Den daha yüksek bir seviyede olmak gibi - ama tahmin etmek o kadar kolay değil ve birden fazla yere bakmak zorunda kalacağım yöntemi bulmak için.

O uygulama çürümüş bir tasarım virüsün yayılmasına yol açabilecek Zamanla isVisibleToüzerinde hem Card ve Playerinanıyorum ki sınıfta, bir hayır-hayır olduğunu. Neden öyle? Çünkü zaten utanç verici bir günü, düşündüğümden player1.isVisibleTo(card1)farklı bir değere döndüğünü hayal card1.isVisibleTo(player1).ediyorum - özneldir - bu tasarım tarafından imkansızlaştırılmalıdır .

Kartlar ve oyuncuların Karşılıklı görünürlük daha iyi bir bağlam nesnenin çeşit tarafından yönetilmelidir - olsun Viewport, Dealya da Game.

Global fonksiyonlara sahip olmakla eşit değil. Sonuçta, birçok eşzamanlı oyun olabilir. Aynı kartın birçok masada aynı anda kullanılabileceğini unutmayın. CardHer kürek ası için birçok örnek oluşturalım mı?

Hala uygulamak belki isVisibleToüzerinde Card, ama buna bir bağlam nesnesi geçmek ve yapmak CardTemsilciye sorgusu. Yüksek bağlanmayı önlemek için arayüz programlayın.

İkinci örneğinize gelince - belge kimliği yalnızca BigDecimalbir şey içeriyorsa, neden bunun için bir sarmalayıcı sınıfı oluşturmalısınız?

İhtiyacın olan tek şey bir diyelim DocumentRepository.getDocument(BigDecimal documentID);

Bu arada, Java'dan yokken structC # de var.

Görmek

referans için. Nesne yönelimli bir dildir, ancak hiç kimse ondan önemli bir şey çıkarmaz.


1
C # 'daki yapılar hakkında sadece bir not: Bunlar, C' de bildiğiniz gibi tipik yapılar değildir. Aslında, aynı zamanda kalıtım, enkapsülasyon ve polimorfizm ile OOP'yi desteklerler. Bazı özelliklerin yanı sıra, temel fark, çalışma zamanının diğer nesnelere geçirildiğinde örnekleri nasıl ele aldıklarıdır: yapılar değer türleridir ve sınıflar referans türleridir!
Aschratt

3
@Aschratt: Yapılar mirası desteklemiyor. Yapılar, arabirimleri uygulayabilir, ancak arabirimleri uygulayan yapılar, aynı şekilde sınıf nesnelerinden farklı davranır. Yapıların bir nevi nesneler gibi davranmasını sağlamak mümkün olsa da, biri bir C yapısı gibi davranan bir şey istediğinde ve onu içine alan şeyler ilkel veya değişmez sınıf türleri olduğunda yapılar için en iyi kullanım örneği.
supercat

1
"Küresel işlevlere sahip olmakla eşit değil" nedeniyle + 1. Bu başkaları tarafından çok fazla ele alınmamıştır. (Birkaç desteğiniz olsa da, global bir işlev aynı kartın farklı örnekleri için farklı değerler döndürür).
alexis

@supercat Bu, ayrı bir soruya veya sohbet oturumuna layıktır, ancak şu anda ikisiyle de ilgilenmiyorum :-( Siz (C #) "arayüzleri uygulayan yapıların, aynı şekilde sınıf derslerinden farklı davrandıklarını" söylüyorsunuz. " Dikkate alınması gereken başka davranışsal farklılıklar var, ancak AFAIK kodundaki Interface iObj = (Interface)obj;davranışının davranışından veya durumundan iObjetkilenmiyor ( a . eğer bu ödevde kutulu bir kopya olacaksa hariç )structclassobjstruct
Mark Hurd

150

OOP'un ardındaki temel fikir, veri ve davranışların (bu veri üzerine) ayrılmaz olması ve sınıfın bir nesnesi fikri ile birleştirilmeleridir.

Sınıfların OOP'de temel bir kavram olduğunu varsaymanın ortak hatasını yapıyorsunuz . Sınıflar, enkapsülasyona ulaşmanın özellikle popüler bir yoludur. Fakat bunun kaymasına izin verebiliriz.

Genel bir kart oyunu API'niz olduğunu varsayalım. Sınıf kartınız var. Şimdi bu Card sınıfının oyunculara görünürlüğü belirlemesi gerekiyor.

İYİ HEAVENS NO. Köprü oynarken , yedinin yedisine , kukla elini sadece kukla tarafından herkes tarafından bilinen bir sır olarak değiştirmenin zamanı geldiğini mi sorarsınız ? Tabii ki değil. Bu kartın hiç bir endişesi değil.

Bir yol, Card sınıfında boolean isVisible (Player p) sahibi olmaktır. Diğeri ise Player sınıfında boolean isVisible (Card c) olmalıdır.

Her ikisi de korkunç; ikisini de yapma. Ne oyuncu ne de kart Köprü kurallarını uygulamaktan sorumlu değildir !

Bunun yerine, bir Player ve bir kart listesi verildiğinde hangi kartların görünür olduğunu belirleyen bir Viewport sınıfına sahip olduğumuz üçüncü seçeneği tercih ettim.

Daha önce hiç "viewport" olan kartlar oynamamıştım, bu yüzden bu sınıfın ne yapması gerektiği hakkında hiçbir fikrim yoktu. Ben var kartların bir çift deste, bazı oyuncuların, bir masa ve Hoyle bir kopyasını kartlarını oynadı. Viewport bunlardan hangisini temsil ediyor?

Ancak bu yaklaşım, olası bir üye işlevinin hem Kart hem de Oyuncu sınıflarını soyar.

İyi!

Bunu, kartların görünürlüğünden başka şeyler için yaptığınızda, tüm işlevler çoğunlukla veri içermeyen sınıflar olan, yalnızca yukarıdaki Viewport gibi yöntemler olan diğer sınıflarda uygulandığından, tamamen veri içeren Card ve Player sınıfları kalır. Bu açıkça OOP'nin ana fikrine karşıdır.

Hayır; OOP'nin temel fikri, nesnelerin kaygılarını içine almasıdır . Sisteminizde bir kart çok fazla endişe duymaz. Bir oyuncu da değil. Bunun nedeni, dünyayı doğru bir şekilde modellemenizdir . Gerçek dünyada, özellikler bir oyunla ilgili kartlardır ve son derece basittir. Oyundaki oyunda fazla değişiklik yapmadan kartlardaki resimleri 1'den 52'ye kadar olan sayılarla değiştirebiliriz. Dört kişiyi, oyunun oyununu değiştirmeden Kuzey, Güney, Doğu ve Batı etiketli mankenlerle değiştirebiliriz. Oyuncular ve kartlar, kart oyunları dünyasındaki en basit şeylerdir. Kurallar karmaşık olandır, bu yüzden kuralları temsil eden sınıf komplikasyonun nerede olması gerektiğidir.

Şimdi, eğer oyuncularınızdan biri bir AI ise, o zaman iç durumu son derece karmaşık olabilir. Ancak bu AI bir kartı görüp göremeyeceğini belirlemez. Kurallar bunu belirler .

İşte sisteminizi nasıl tasarlarım.

İlk olarak, birden fazla desteye sahip oyunlar varsa, kartlar şaşırtıcı şekilde karmaşıktır. Soruyu göz önünde bulundurmanız gerekir: oyuncular aynı sıralamadaki iki kart arasında ayrım yapabilir mi? Bir oyuncu kalpten yedi taneden birini oynarsa ve sonra bazı şeyler olur ve sonra iki oyuncu kalpten yedi taneden birini oynarsa, üçüncü oyuncu da kalplerin yedide aynı olduğunu belirleyebilir mi? Bunu dikkatlice düşünün. Ancak bu kaygı dışında, kartlar çok basit olmalı; onlar sadece veri.

Sonra, oyuncunun doğası nedir? Bir oyuncu tüketir dizisi görünür eylemler ve üreten bir eylem .

Kural nesnesi tüm bunları koordine eden şeydir. Kurallar görünür eylemler dizisi oluşturur ve oyuncuları bilgilendirir:

  • Birincisi, kupaların on'u sana üçüncü oyuncu tarafından verildi.
  • İkinci oyuncu, bir oyuncu üçüncü oyuncuya bir oyuncuya verilir.

Sonra oyuncudan bir aksiyon almasını ister.

  • Birinci oyuncu, ne yapmak istersin?
  • Bir oyuncu diyor ki: fromp tiz.
  • Birincisi, bu yasadışı bir eylem çünkü tp'lerden tp yenilmez bir kumar oynuyor.
  • Birinci oyuncu, ne yapmak istersin?
  • Bir oyuncu diyor ki: maça kraliçesini at.
  • İkinci oyuncu, birinci oyuncu maça kraliçesini attı.

Ve bunun gibi.

Mekanizmalarınızı politikalarınızdan ayırın . Oyunun politikaları , kartların içinde değil, bir politika nesnesine alınmalıdır . Kartlar sadece bir mekanizmadır.


41
@gnat: Alan Kay'dan zıt bir düşünce "Aslında" nesne yönelimli "terimini oluşturdum ve aklımda C ++ olmadığını düşünüyorum." Sınıfsız OO dilleri var; JavaScript akla geliyor.
Eric Lippert

19
@gnat: JS'nin bugünkü haliyle OOP dilinin harika bir örneği olmadığı konusunda hemfikir olurum, ancak birinin sınıfsız bir OO dilini kolayca oluşturabildiğini gösteriyor. Hem Eyfel hem de C ++ 'daki temel OOnesite biriminin sınıf olduğu konusunda hemfikirim. katılmıyorum burada sınıfların OO'nun olmazsa olmazı olduğu fikri . Olmazsa olmaz OO davranışı kapsayan ve iyi tanımlanmış bir halka arayüz üzerinden birbirleriyle iletişim nesneler.
Eric Lippert

16
W / @EricLippert, sınıflar OO için temel değil, miras, ana akım ne söylerse söylesin kabul ediyorum. Bununla birlikte, verilerin kapsüllenmesi davranış ve sorumluluktur. Orada prototip tabanlı diller OO ama sınıfsız olan JavaScript ötesinde. Bu kavramlar üzerindeki kalıtım üzerine odaklanmak özellikle bir hatadır. Bununla birlikte, sınıflar davranış enkapsülasyonunu organize etmek için çok faydalıdır. Sınıfları nesne olarak kabul edebileceğiniz (ve prototip dillerinde, tersi) çizgiyi bulanıklaştırır.
Schwern

6
Bunu şu şekilde düşünün: Gerçek dünyada, gerçek kart hangi davranışları kendi kendine sergiliyor? Bence cevabı "yok". Kartta başka şeyler de var. Kartın kendisi, gerçek dünyada, kelimenin tam anlamıyla , hiçbir şekilde içsel davranış göstermeyen , yalnızca bilgidir (4'lü kulüp). Bu bilginin (aka "kart") nasıl kullanıldığı bir başkasına, yani "kurallara" ve "oyunculara" kadar% 100'dür. Aynı kartlar, sonsuz sayıda (belki de tam olmayan) farklı oyunlar için, herhangi bir sayıda farklı oyuncu tarafından kullanılabilir. Bir kart sadece bir karttır ve sahip olduğu özelliklerin tümüdür.
Craig

5
@Montagist: O zaman işleri biraz netleştireyim. C'yi düşünün. Bence C'nin sınıfları olmadığı konusunda hemfikirsiniz. Bununla birlikte, yapıların "sınıf" olduğunu söyleyebilir, işlev işaretçi türünde alanlar oluşturabilir, vtables oluşturabilir, vtables'ı kuran "yapı" olarak adlandırılan yöntemler yapabilir, böylece bazı yapılar birbirinden "miras alınır", ve bunun gibi. Sen edebilirsiniz taklit C. sınıf tabanlı miras Ve yapabilirsiniz taklit JS bunu. Ancak bunu yapmak, dilin üstünde zaten bulunmayan bir şey inşa etmek anlamına gelir.
Eric Lippert

29

Verilerin ve davranışların birleştirilmesinin OOP'nin ana fikri olduğu konusunda haklısın, ama dahası da var. Örneğin, kapsülleme : OOP / modüler programlama, bir kamu arayüzünü uygulama detaylarından ayırmamızı sağlar. OOP'de bu, verilerin hiçbir zaman halka açık olmaması ve sadece erişimciler tarafından kullanılması gerektiği anlamına gelir. Bu tanım gereği, yöntemi olmayan bir nesne gerçekten işe yaramaz.

Erişimcilerin ötesinde yöntem sunmayan bir sınıf, esasen aşırı karmaşık bir yapıdır. Ancak bu fena değil, çünkü OOP size bir yapının yapmadığı iç detayları değiştirme esnekliği verir. Örneğin, bir üye alanında bir değer depolamak yerine, her seferinde yeniden hesaplanabilir. Ya da bir destek algoritması değiştirilir ve onunla birlikte takip edilmesi gereken durum.

OOP bazı belirgin avantajlara sahip olsa da (özellikle düz prosedürel programlamaya göre), “saf” OOP için çaba göstermek saflıkta değildir. Bazı problemler nesne yönelimli bir yaklaşımla iyi eşleşmez ve diğer paradigmalar tarafından daha kolay çözülür. Böyle bir sorunla karşılaştığınızda, aşağılık bir yaklaşım konusunda ısrar etmeyin.

  • Fibonacci dizisini nesne yönelimli şekilde hesaplamayı düşünün . Bunu yapmanın akıllıca bir yolunu düşünemiyorum; basit yapılandırılmış programlama bu soruna en iyi çözümü sunar.

  • Kişisel isVisibleilişki hem sınıflara ait olduğunda veya fiilen için ne ya: hiç bağlamda . Davranışsız kayıtlar, probleminize en uygun gibi görünen işlevsel veya prosedürel bir programlama yaklaşımına tipiktir. Yanlış bir şey yok

    static boolean isVisible(Card c, Player p);
    

    ve Cardötesinde hiçbir yönteme rankve suiterişimcilere sahip olmanın yanlış bir tarafı yoktur .


11
@UMad evet, bu tam olarak benim açımdan ve bunda yanlış bir şey yok. Eldeki iş için doğru <del> dil </del> paradigmasını kullanın . (Bu arada, Smalltalk dışındaki çoğu dil saf nesne yönelimli değildir. Örneğin, Java, C # ve C ++ zorunlu, yapılandırılmış, yordamsal, modüler, fonksiyonel ve nesne yönelimli programlamayı destekler. : onları kullanabilmeniz için)
amon

1
Fibonacci'yi yapmanın mantıklı bir OO yolu var, fibonacciörneği bir yöntemle çağır integer. OO'nun görünüşte küçük yerlerde bile kapsülleme ile ilgili olduğunu vurgulamanızı diliyorum. Tamsayı işin nasıl yapılacağını çözelim. Daha sonra uygulamayı geliştirebilir, performansı iyileştirmek için önbellekleme özelliğini ekleyebilirsiniz. İşlevlerin aksine, yöntemler verileri izler, böylece tüm arayanlar gelişmiş bir uygulamadan yararlanır. Belki daha sonra isteğe bağlı hassas tam sayılar daha sonra eklenir, normal tam sayılar gibi saydam bir şekilde ele alınabilir ve kendi performans ayarlarında ince ayarlı bir fibonacciyöntem olabilir.
Schwern

2
FibonacciBir şey soyut sınıfın bir alt sınıfıysa Sequence, dizi, herhangi bir sayı kümesi tarafından kullanılır ve tohumların, durumların, önbelleklerin ve bir yineleyicinin depolanmasından sorumludur.
George Reith

2
Ben bu kadar etkili olduğu “saf NYP Fibonacci'yi” beklemiyorduk inek-ateş açılması . Lütfen, bu yorumlarda belirli bir eğlence değerine sahip olmasına rağmen herhangi bir döngüsel tartışmayı durdurun. Şimdi hepimiz değişim için yapıcı bir şey yapalım!
amon

3
fibonacci'yi bir tamsayı yöntemi yapmak aptalca olur, sadece OOP olduğunu söyleyebilirsin. Bu bir fonksiyondur ve bir fonksiyon gibi ele alınmalıdır.
immibis

19

OOP'un ardındaki temel fikir, veri ve davranışların (bu veri üzerine) ayrılmaz olması ve sınıfın bir nesnesi fikri ile birleştirilmeleridir. Nesnenin bununla (ve diğer verilerle) çalışan veri ve yöntemleri vardır. Açıkça OOP ilkelerine göre, sadece veri olan nesneler (C yapılarına benzer) bir anti-kalıp olarak kabul edilir. (...) Bu açıkça OOP'un ana fikrine karşıdır.

Bu zor bir soru çünkü birkaç hatalı binaya dayanıyor:

  1. OOP'nin kod yazmanın tek geçerli yolu olduğu fikri.
  2. OOP'nin iyi tanımlanmış bir kavram olduğu fikri. OOP kelimesi, OOP'nin ne olduğu konusunda hemfikir olabilecek iki kişi bulmak zor oldu.
  3. OOP'nin veri toplama ve davranışla ilgili olduğu fikri.
  4. Her şeyin bir soyutlama / olması gerektiği düşüncesi.

# 1-3'e çok fazla dokunmam, çünkü her biri kendi cevabını doğurabilir ve birçok görüş temelli tartışmaya davet eder. Ancak “OOP veri ve davranışları birleştirmekle ilgili” fikrini özellikle rahatsız edici buluyorum. Sadece 4 numaraya gelmiyor, aynı zamanda her şeyin bir yöntem olması gerektiği fikrine de yol açıyor.

Bir türü tanımlayan işlemler ile bu türü kullanma biçimleriniz arasında bir fark vardır. iTh elemanını alabilmek bir dizi kavramı için esastır, ancak sıralama bir taneyle yapmayı seçebileceğim birçok şeyden sadece biri. Sıralama, "sadece eşit elemanları içeren yeni bir dizi yarat" dan daha fazla bir yöntem olmak zorunda değildir.

OOP nesneleri kullanmakla ilgilidir. Nesneler, soyutlamayı başarmanın sadece bir yoludur . Soyutlama, kodunuzdaki gereksiz eşleştirmelerden kaçınmanın bir yoludur, başlı başına bir amaç değildir. Bir kart konusundaki fikriniz yalnızca süitinin ve rütbesinin değeri ile tanımlanmışsa, basit kayıt veya kayıt olarak uygulamak doğru olacaktır. Kodun başka bir kısmına bağımlılık yaratabilecek önemli olmayan detaylar yoktur. Bazen sadece saklayacak bir şeyin olmaz.

Bu tür isVisiblebir yöntem Cardyapmazsınız, çünkü görünür olmak muhtemelen bir kart fikriniz için gerekli değildir (yarı saydam veya opak olabilen çok özel kartlarınız yoksa). Bu tür bir yöntem Playermi olmalı ? Muhtemelen bu, oyuncuların tanımlayıcı bir niteliği de değildir. Bir Viewporttürün parçası mı olmalı ? Bir kez daha bu, bir görünüm alanını ne olarak tanımladığınıza ve kartların görünürlüğünü kontrol etme nosyonunun bir görünüm alanını tanımlamanın ayrılmaz bir parçası olup olmadığına bağlıdır.

Sadece isVisibleserbest bir fonksiyon olması çok mümkün .


1
Akılsız uçağı yerine sağduyu için +1.
sağa doğru

Okuduğum satırlardan, bağladığın makale, bir süredir sahip olmadığım tek katı okumaya benziyor.
Arthur Havlicek

@ArthurHavlicek Kod örneğinde kullanılan dilleri anlamıyorsanız takip etmek daha zordur, ancak bunu oldukça aydınlatıcı buldum.
Doval

9

Açıkça OOP ilkelerine göre, sadece veri olan nesneler (C yapılarına benzer) bir anti-kalıp olarak kabul edilir.

Hayır değiller. Düz-Eski-Veri nesneleri mükemmel geçerli bir kalıptır ve programınızın farklı alanları arasında kalıcı olması veya iletilmesi gereken verilerle ilgilenen herhangi bir programda beklerdim.

Veri katmanlı iken olabilir tam spool up Playerbunun dan okuduğunda sınıf Playerstabloda, bunun yerine sadece dönüştürür size programın başka bir bölgeye geçer tablodan alanları ile bir POD döndüren bir genel veri kütüphanesi olabilir Beton Playersınıfınıza bir oyuncu POD .

Yazılmış veya yazılmamış veri nesnelerinin kullanılması programınızda anlam ifade etmeyebilir, ancak bu onları bir kalıp karşıtı yapmaz. Mantıklı geliyorsa, onları kullanın ve eğer istemiyorsa kullanmayın.


5
Söylediğiniz hiçbir şeye katılmayın, ancak bu soruya hiçbir şekilde cevap vermiyor. Bununla birlikte, soruyu cevabından daha fazla suçluyorum.
pdr

2
Aynen, kartlar ve belgeler gerçek dünyada bile sadece bir bilgi kabıdır ve bununla başa çıkamayan herhangi bir "kalıp" ihmal edilmeli.
JeffO

1
Plain-Old-Data objects are a perfectly valid pattern Öyle olmadıklarını söylemedim, başvurunun tüm alt yarısını doldurduklarında yanlış olduğunu söylüyorum.
RokL

8

Şahsen, Domain Driven Design'ın bu konuya açıklık getirilmesine yardımcı olduğunu düşünüyorum. Benim sorduğum soru, kart oyununu insanlara nasıl tarif ederim? Başka bir deyişle, ne model yapıyorum? Modelleme yaptığım şey gerçekten "viewport" kelimesini ve davranışına uyan bir kavramı içeriyorsa, viewport nesnesini yaratacağım ve mantıksal olarak yapması gerekeni yapmasını istiyorum.

Bununla birlikte, oyunumda bakış açısı kavramı yoksa ve ihtiyacım olan bir şey çünkü aksi halde "yanlış hissettiriyor" kodu. Etki alanı modelimi eklemeyi iki kez düşünüyorum.

Model kelimesi, bir şeyin temsilini inşa ettiğiniz anlamına gelir. Temsil ettiğiniz şeyin ötesinde soyut bir şeyi temsil eden bir sınıfa koymaya karşı dikkatli olun.

Bir ekranla arayüz kurmanız gerekirse, kodunuzun başka bir bölümünde bir Viewport konseptine ihtiyacınız olabileceğini eklemek için düzenleme yapacağım. Ancak DDD açısından bu bir altyapı endişesi olacak ve etki alanı modelinin dışında olacaktı.


Lippert'in yukarıdaki cevabı bu kavramın daha iyi bir örneğidir.
RibaldEddie

5

Genellikle kendi kendine promosyon yapmıyoruz ama aslında ben cepten tasarım sorunları hakkında çok şey yazdım olduğunu bloguma . Birkaç sayfa özetlemek için: tasarıma sınıflarla başlamamalısınız. Arabirimler veya API'ler ile başlamak ve buradaki şekil kodu, anlamlı soyutlamalar sağlamak, özelliklere uymak ve somut sınıfları tekrar kullanılamaz kodla şişirmekten kaçınmak için daha yüksek şanslara sahiptir.

Bu nasıl uygulanacağı Card- Playerproblem: Bir Oluşturma ViewPortaklınıza eğer soyutlama mantıklı Cardve Playeriki bağımsız kütüphaneler (ima hangi varlık olarak Playerbazen olmadan kullanılır Card). Ancak, Playerbekletmeyi düşünmeye meyilliyim Cardsve Collection<Card> getVisibleCards ()onlara erişimci sağlamalıyım. Her ikisi de bu çözümler ( ViewPortve benimki), anlaşılabilir kod ilişkileri oluşturmak için veya isVisiblebir yöntem olarak sunmaktan daha iyidir .CardPlayer

Sınıf dışı bir çözüm için çok, çok daha iyidir DocumentId. Karmaşık bir veritabanı kitaplığına bağlı (temelde bir tamsayı) yapmak için çok az motivasyon vardır.


Blogunu beğendim.
RokL

3

Eldeki sorunun doğru düzeyde yanıtlandığından emin değilim. Forumdaki bilge, buradaki sorunun özünü aktif olarak düşünmeye teşvik ettim.

U Mad, OOP anlayışı gereğince programlamanın genellikle çok sayıda yaprak düğümünün veri sahibi olmasına yol açacağına inanırken, üst düzey API'si davranışların çoğunu oluşturduğuna inandığı bir durum ortaya koyuyor.

Ben konunun isVisible'ın Card vs Player'da tanımlanıp tanımlanmayacağı konusunda biraz teğet olduğunu düşünüyorum; naif de olsa, sadece örnekle gösterilmiştir.

Buradaki tecrübeyi eldeki probleme bakmak için zorladım. U Mad'in zorladığı iyi bir soru olduğunu düşünüyorum. Anladığım kadarıyla kuralları ve ilgili mantığı kendine ait bir nesneye iteceksin; ama anladığım kadarıyla soru

  1. Gerçekten fazla işlevsellik sunmayan basit veri taşıyıcı yapıları (sınıflar / yapılar; bu soru için neyin modellendiğini umursamıyorum) sorun yok mu?
  2. Evet ise, onları modellemenin en iyi veya tercih edilen yolu nedir?
  3. Hayır ise, bu veri sayacı parçalarını daha yüksek API sınıflarına nasıl dahil ederiz (davranış dahil)

Benim görüşüm:

Sanırım nesne yönelimli programlamada doğru elde edilmesi zor olan bir ayrıntı düzeyi sorusu soruyorsunuz. Benim küçük tecrübeme göre, modelime kendi başına hiçbir davranış içermeyen bir varlık dahil etmem. Gerekirse, muhtemelen veri ve davranışları içine alma fikrine sahip bir sınıfın aksine böyle bir soyutlamayı tutacak şekilde tasarlanmış bir yapı kullanmak zorunda kaldım.


3
Sorun şu ki, sorunun (ve cevabınız) işlerin "genel olarak" nasıl yapılacağı ile ilgili. Gerçek şu ki, "genel olarak" hiçbir zaman bir şey yapmayız. Biz her zaman belirli şeyler yaparız. Özel şeylerimizin durum için doğru şeyler olup olmadığını belirlemek için özel şeylerimizi incelemek ve bunları gereksinimlerimize göre ölçmek gerekir.
John Saunders

@JohnSaunders Burada bilgeliğinizi algılıyorum ve bir dereceye kadar katılıyorum, ancak bir sorunu çözmeden önce gerekli olan kavramsal bir yaklaşım da var. Sonuçta, buradaki soru göründüğü kadar açık uçlu değil. Herhangi bir OO tasarımcısının OOP'nin ilk kullanımlarında karşılaştığı geçerli bir OOD sorusu olduğunu düşünüyorum. Senin sıran ne? Bir somutluk yardımcı olursa, seçtiğiniz bir örnek oluşturmayı tartışabiliriz.
Harsha

35 yıldan beri okul dışındayım. Gerçek dünyada, "kavramsal yaklaşımlarda" çok az değer buluyorum. Bu durumda Meyers'den daha iyi bir öğretmen olmak için deneyim buluyorum.
John Saunders

Davranış ayrımı için veri sınıfına karşı sınıf anlamıyorum. Nesnelerinizi doğru soyutlarsanız, hiçbir ayrım yoktur. Bir düşünün Pointile getX()fonksiyonu. Özniteliklerinden birini aldığını hayal edebiliyordunuz, ancak bunu diskten ya da internetten okuyabilirdi. Alma ve ayarlama davranış ve bunu yapan sınıflara sahip olmak tamamen iyi. Veritabanları sadece veri almak ve ayarlamak fwiw
Arthur Havlicek

@ArthurHavlicek: Bir sınıf bilerek olmaz do sık sık ne yapacağını bilmeden kadar yararlıdır. Sözleşmesinde, paylaşılabilir bir değişmez veri tutucusundan başka bir şey olarak veya paylaşılamaz bir değişebilir veri tutucusundan başka bir şey olarak davranmayacağını belirtmek yararlıdır.
supercat,

2

OOP'deki ortak bir karışıklık kaynağı birçok nesnenin devletin iki yönünü kapsıyor olmasından kaynaklanmaktadır: bildikleri şeyler ve onlar hakkında bilenler. Nesnelerin durumuyla ilgili tartışmalar sıklıkla ikinci yönü görmezden gelir, çünkü nesne referanslarının aldatıcı olduğu çerçevelerde, referansları dış dünyaya maruz kalan herhangi bir nesne hakkında ne bildiklerini belirlemenin genel bir yolu yoktur.

CardEntityKartın bu yönlerini ayrı ayrı bileşenlere yerleştiren bir nesneye sahip olmanın muhtemelen yararlı olacağını düşünüyorum . Bir bileşen kart üzerindeki işaretlerle ilgili olacaktır (Örn. "Diamond King" veya "Lava Blast; oyuncuların AC-3 atlatması veya 2D6 hasar alması şansı var"). Kişi pozisyon gibi benzersiz bir durumla ilgili olabilir (örneğin, güvertede ya da Joe'nun elinde ya da Larry'nin önünde bulunan masa). Üçüncüsü bunu görmekle ilgili olabilir (belki hiç kimse, belki bir oyuncu veya belki de pek çok oyuncu). Her şeyin senkronize tutulmasını sağlamak için, bir kartın olabileceği yerler basit alanlar olarak değil, CardSpacenesneler olarak kapsüllenebilir ; Bir kartı bir yere taşımak için, uygun olana bir referans verilmelidir.CardSpacenesne, cisim; daha sonra kendisini eski uzaydan kaldırır ve yeni uzaya koyardı).

Açıkça, "X'i bilen" ifadesini, "X'in bildiğinden" ayrı olarak kapsüllemek, çok fazla kafa karışıklığını önlemeye yardımcı olmalıdır. Özellikle çok sayıda ilişkide olduğu gibi bellek sızıntılarını önlemek için özen gösterilmesi gerekir (örneğin, yeni kartlar ortaya çıkabilir ve eski kartlar kaybolabilirse, terk edilmesi gereken kartların kalıcı olarak uzun ömürlü nesnelere bırakılmadığından emin olunmalıdır) ) fakat eğer bir nesneye yapılan referansların varlığı, durumunun ilgili bir parçasını oluşturacaksa, nesnenin kendisinin bu bilgiyi açıkça kapsüllenmesi tamamen uygundur (başka bir sınıfa temsili olsa bile, onu gerçekten yönetme çalışmasını deleştirmiş olsa da).


0

Ancak bu yaklaşım, olası bir üye işlevinin hem Kart hem de Oyuncu sınıflarını soyar.

Ve bu nasıl kötü / kötü tavsiye edilir?

Kartlar örneğinize benzer bir benzetme kullanmak için, a Car, a düşünün Driverve aracın kullanıp Driverkullanamayacağını belirlemeniz gerekir Car.

Tamam, yani doğru araba anahtarına sahip Carolup olmadığını bilmek istemediğinize Driverkarar verdiniz ve bazı nedenlerden dolayı sınıf Driverhakkında bilgi sahibi olmamaya karar verdiniz Car(tam olarak etmediniz). Bu da orjinal sorunuzda. Bu nedenle, yukarıdaki soruya bir değer döndürmek için iş kurallarınıUtils içeren yöntemi içeren bir sınıf çizgisi boyunca bir ara sınıfa sahipsiniz .boolean

Bence bu iyi. Aracı sınıfın şu anda sadece araba anahtarlarını kontrol etmesi gerekebilir, ancak sürücünün geçerli bir sürücü ehliyeti olup olmadığını, alkolün etkisi altında veya distopyacı bir gelecekte DNA biyometrilerini kontrol edip etmediğini düşünmek için yeniden kontrol edilebilir. Kapsülleme ile, bu üç sınıfın bir arada var olması gerçekten büyük bir sorun değil.

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.