Basit etki alanı nesnesini temsil etmek için ilkel mi sınıf mı?


14

Etki alanına özgü bir nesnenin düz bir Dize veya sayıya karşı ne zaman kullanılacağına ilişkin genel kurallar veya genel kurallar nelerdir?

Örnekler:

  • Yaş sınıfı vs Tamsayı?
  • FirstName sınıfı vs String?
  • UniqueID ve Dize
  • PhoneNumber sınıfı vs String vs Long?
  • DomainName sınıfı vs String?

Bence çoğu OOP uygulayıcısı kesinlikle PhoneNumber ve DomainName için belirli sınıflar söyleyebilirdi. Onları neyin geçerli kıldığı ve nasıl karşılaştırılacağı ile ilgili daha fazla kural, basit sınıfları ele almayı daha kolay ve daha güvenli hale getirir. Ancak ilk üçü için daha fazla tartışma var.

Asla bir "Yaş" sınıfına rastlamadım ama biri olumsuz olması gerektiği göz önüne alındığında mantıklı olabilir (tamam, negatif yaşları tartışabileceğinizi biliyorum ama neredeyse ilkel bir tam sayıya eşdeğer olduğu iyi bir örnek).

Dize, "Adı" temsil etmek için yaygındır, ancak boş bir Dize geçerli bir Dize olduğu için geçerli bir ad olmadığı için mükemmel değildir. Karşılaştırma genellikle vaka göz ardı edilerek yapılır. Tabii ki boş, büyük / küçük harfe duyarlı olmayan karşılaştırmalar yapmak için yöntemler vardır, ancak tüketicinin bunu yapmasını gerektirir.

Cevap çevreye bağlı mı? Öncelikle on yıldan fazla bir süre boyunca yaşayacak ve sürdürülecek kurumsal / yüksek değerli yazılımlarla ilgileniyorum.

Belki de bunu düşünüyorum ama kimsenin ne zaman ilkel vs sınıf seçeceğine dair kuralları olup olmadığını bilmek istiyorum.


2
Yaş sınıfı, bir tamsayı ile değiştirilebiliyorsa iyi bir fikir değildir. Yapıcıda bir doğum tarihi alırsa ve getCurrentAge () ve getAgeAt (Tarih tarihi) yöntemlerine sahipse, sınıfın anlamı vardır. Ancak, bir tamsayı ile değiştirilemez.
kiviron

5
Msgstr "Bir dize bazen bir dize değildir. Buna göre modelleyin." Mark Seemann tarafından 'ilkel takıntı' hakkında iyi bir yazı var: blog.ploeh.dk/2015/01/19/…
Serhii Shushliapin

4
Aslında bir Age sınıfı garip bir şekilde bir anlam ifade ediyor. Batı'nın ortak yaş tanımı doğumda sıfırdır ve bir sonraki doğum gününüzde 1 ekleyin. Doğu Asya metodolojisi, doğumda 1 olduğunuz ve sonraki Yeni Yıl gününde 1 eklendiğinizdir. Böylece, bulunduğunuz yere bağlı olarak, yaşınız farklı hesaplanabilir. EG en.wikipedia.org/wiki/East_Asian_age_reckoning people may be one or two years older in Asian reckoning than in the western age system
Peter M

@PeterM, bu iyi bir bilgi! Tarih / Saat ve şimdi yaş. Basit kavramlar olmalılar, ancak bu dünyada ele almak için kullandığımızdan çok daha karmaşık olduğunu kanıtlıyor.
Machado

6
Bu sorunun altında çok fazla yazı görüyorum, ancak cevap gerçekten bu kadar karmaşık değil. AgeBöyle bir sınıfın sağlayacağı ek davranışa ihtiyacınız olduğunda tamsayı yerine bir sınıf kullanırsınız.
Robert Harvey

Yanıtlar:


20

Etki alanına özgü bir nesnenin düz bir Dize veya sayıya karşı ne zaman kullanılacağına ilişkin genel kurallar veya genel kurallar nelerdir?

Genel kılavuz, alan adınızı alan adına özgü bir dilde modellemek istediğinizdir.

Şunu düşünün: Neden tamsayı kullanıyoruz? Tüm tamsayıları dizelerle kolayca temsil edebiliriz. Veya bayt ile.

Tamsayı ve yaş için ilkel türler içeren bir etki alanı agnostik dilinde programlama yapsaydık hangisini seçerdiniz?

Gerçekte ortaya çıkan şey, belirli türlerin "ilkel" doğası, uygulamamız için dil seçiminin bir kazasıdır.

Özellikle sayılar genellikle ek bağlam gerektirir. Yaş sadece bir sayı değil, aynı zamanda boyut (zaman), birimler (yıl?), Yuvarlama kuralları da var! Yaşları birlikte eklemek, paraya yaş eklemenin mantıklı olmadığı bir şekilde mantıklıdır.

Türleri farklı yapmak, doğrulanmamış bir e-posta adresi ile doğrulanmış bir e-posta adresi arasındaki farkları modellememizi sağlar .

Bu değerlerin bellekte nasıl temsil edildiğinin kazası en az ilginç kısımlardan biridir. Etki alanı modeli CustomerId, bir int veya bir String veya bir UUID / GUID veya bir JSON düğümü ile ilgilenmez . Sadece maddi gücü istiyor.

Tam sayıların büyük endian mı yoksa küçük endian mı olduğunu gerçekten önemsiyor muyuz? ListGeçtiğimiz bir dizi veya grafik üzerinde bir soyutlama olup olmadığı umurunda mıyız? Çift kesinlikli aritmetiğin verimsiz olduğunu ve bir kayan nokta gösterimine geçmemiz gerektiğini keşfettiğimizde , alan modeli dikkat etmeli mi?

Parnas, 1972'de yazdı

Bunun yerine, zor tasarım kararlarının veya değişmesi muhtemel tasarım kararlarının bir listesiyle başlamasını öneriyoruz. Her modül daha sonra böyle bir kararı diğerlerinden gizlemek için tasarlanmıştır.

Bir anlamda, tanıtacağımız alana özgü değer türleri, verilerin temelde temsili ne olacağına dair kararımızı izole eden modüllerdir.

Sonuç olarak modülerlik - bir değişikliğin kapsamını yönetmenin daha kolay olduğu bir tasarım elde ediyoruz. Dezavantajı maliyettir - ihtiyacınız olan ısmarlama türlerin oluşturulması daha fazla iştir , doğru türlerin seçilmesi etki alanının daha iyi anlaşılmasını gerektirir. Değer modülünü oluşturmak için gereken iş miktarı yerel Blub lehçenize bağlı olacaktır .

Denklemdeki diğer terimler, çözümün beklenen ömrünü (bir kez çalıştırılacak senaryo gereçleri için dikkatli modelleme, yatırımın berbat bir getirisine sahiptir), alan adının işletmenin temel yeterliliğine ne kadar yakın olduğunu içerebilir.

Düşünebileceğimiz özel bir durum, bir sınır ötesi iletişimdir . Bir konuşlandırılabilir birimde yapılan değişikliklerin diğer konuşlandırılabilir birimlerle koordineli değişiklikler gerektirdiği bir durumda olmak istemiyoruz. Bu nedenle mesajlar , değişmezler veya alana özgü davranışlar dikkate alınmadan temsillere daha fazla odaklanma eğilimindedir. İleti biçiminde "bu değer kesinlikle pozitif olmalı" iletmeye değil, tel üzerinden temsilini iletmeye ve etki alanı sınırındaki bu gösterime doğrulama uygulamaya çalışacağız.


Zor ve değiştirilmesi muhtemel şeyleri gizlemek (veya soyutlamak) için mükemmel bir nokta.
user949300

4

Soyutlamayı düşünüyorsunuz ve bu harika. Ancak, girişiniz bana daha büyük bir şeyin bireysel nitelikleri gibi görünüyor (elbette aynı şeyin hepsi değil).

Daha önemli olduğunu düşündüğüm şey, öznitelikleri bir araya toplayan ve onlara bir Person sınıfı gibi uygun bir ev veren soyutlama sağlamaktır, böylece istemci programcı birden çok öznitelikle değil, daha üst düzey bir soyutlama ile uğraşmak zorunda kalır.

Bu soyutlamaya sahip olduğunuzda, sadece değerler olan özellikler için ek soyutlamalara ihtiyacınız yoktur. Person sınıfı, (ilkel) dizeler listesi olarak PhoneNumbers ve (ilkel) dize olarak Ad ile birlikte BirthDate'i (ilkel) Tarih olarak tutabilir.

Biçimlendirme koduna sahip bir telefon numaranız varsa, şimdi bu iki özelliktir ve telefon numarası için bir sınıfı hak eder (büyük olasılıkla bazı numaralar da olabilir, örneğin yalnızca numaralar ve biçimlendirilmiş telefon numarası almak gibi).

Bir sınıfı hak eden birden fazla nitelik (örneğin önek / başlık, ilk, son, sonek) olarak bir Adınız varsa (şimdi tam adı dize olarak almak gibi daha küçük parçalar, başlık vb. Gibi bazı davranışlarınız var. .).


1

İhtiyacınız olan şekilde modellemelisiniz, sadece bazı veriler istiyorsanız ilkel türleri kullanabilirsiniz, ancak bu veriler üzerinde daha fazla işlem yapmak istiyorsanız, belirli sınıflarda modellenmelidir, örneğin (yorumunda @ kiwiron ile katılıyorum) bir Yaş sınıfının bir anlamı yoktur, ancak bir Doğum tarihi sınıfıyla değiştirmek daha iyidir ve bu yöntemleri oraya koyarsınız.

Bu kavramları sınıflar içinde modellemenin yararı, modelinizin zenginliğini güçlendirmenizdir, örneğin, herhangi bir Tarihi kabul eden bir yönteminiz var, bir kullanıcı herhangi bir tarih, WW II başlangıç ​​tarihi veya Soğuk savaş bitiş tarihi ile doldurabilir, bu tarihlerden hala geçerli bir doğum tarihi. bunu Doğum tarihi ile değiştirebilirsiniz, bu durumda, Doğum Tarihini kabul etme yönteminizi herhangi bir Tarih kabul etmemek için uygularsınız.

Firstname için çok fazla yarar görmüyorum, UniqueID de, PhoneNumber biraz, size ülke ve bölgeyi vermesini isteyebilirsiniz, çünkü genel olarak PhoneNumber, ülkelere ve bölgelere atıfta bulunur, bazı operatörler Mobil'i sınıflandırmak için önceden tanımlanmış numara Soneklerini kullanır , Office ... numaraları, böylece bu yöntemleri DomainName için aynı sınıfa ekleyebilirsiniz.

Daha fazla ayrıntı için DDD'deki (Domain Driven Design) Değer Nesnelerine göz atmanızı tavsiye ederim.


1

Buna bağlı olarak, söz konusu verilerle ilişkili davranışınız varsa (sürdürmeniz ve / veya değiştirmeniz gerekir).

Kısacası, sadece onunla ilişkili çok fazla davranış olmadan savunulan bir veri parçasıysa, ilkel bir tür kullanın ya da biraz daha karmaşık veri parçaları için (büyük ölçüde davranışsız) bir veri yapısı (örn. belirleyiciler).

Öte yandan, karar vermek için bu verileri kodunuzdaki çeşitli yerlerde kontrol ettiğiniz veya işlediğinizi fark ederseniz, genellikle birbirine yakın olmayan yerler - daha sonra bir sınıf tanımlayarak uygun bir nesneye dönüştürün ve ilgili davranışı yöntem olarak içine koymak. Herhangi bir nedenden dolayı böyle bir sınıfı tanımlamanın mantıklı olmadığını düşünüyorsanız, bir alternatif bu davranışı bu veriler üzerinde çalışan bir çeşit "hizmet" sınıfında birleştirmektir.


1

Örnekleriniz daha çok başka bir şeyin özelliklerine veya özelliklerine benziyor. Bunun anlamı, sadece ilkel ile başlamanın mükemmel makul olacağıdır. Bir noktada bir ilkel kullanmanız gerekecektir, bu yüzden aslında gerçekten ihtiyaç duyduğum zaman için karmaşıklığı kaydederim.

Diyelim ki bir oyun yapıyorum ve yaşlanan karakterler hakkında endişelenmem gerekmiyor. Bunun için bir tamsayı kullanmak mükemmeldir. Yaş, karakterin bir şey yapmasına izin verilip verilmediğini görmek için basit kontrollerde kullanılabilir.

Diğerlerinin de belirttiği gibi, yaş bulunduğunuz yere bağlı olarak karmaşık bir konu olabilir. Yaşları farklı yerlerde vb. Uzlaştırmaya ihtiyacınız varsa , özelliklere Agesahip DateOfBirthve sınıf Localeolarak bir sınıfı tanıtmak mükemmel mantıklıdır . Bu Yaş sınıfı, herhangi bir zaman damgasındaki geçerli yaşı da hesaplayabilir.

Bu nedenle, bir ilkeyi bir sınıfa tanıtmanın nedenleri vardır, ancak bunlar uygulamaya özgüdür. İşte bazı örnekler:

  • Bilgi türünün kullanıldığı her yerde (ör. Telefon numaraları, e-posta adresleri) uygulanması gereken özel doğrulama mantığınız var.
  • İnsanların okuması için kullanılan özel biçimlendirme mantığınız var (örneğin IP4 ve IP6 adresleri)
  • Bu nesne türüyle ilgili bir dizi işleviniz var
  • Benzer değer türlerini karşılaştırmak için özel kurallarınız var

Temel olarak, bir değerin nasıl ele alınacağına dair bir grup kuralınız varsa, projenizde sürekli olarak kullanmak istiyorsanız, bir sınıf oluşturun. İşlevler için gerçekten kritik olmadığı için basit değerlerle yaşayabiliyorsanız, bir ilkel kullanın.


1

Günün sonunda, bir nesne sadece bazı süreçlerin gerçekten yürütüldüğü bir tanıktır .

İlkel int, bazı işlemlerin 4 bayt belleği sıfırladığı ve daha sonra -2,147,483,648 ila 2,147,483,647 aralığında bir tamsayıyı temsil etmek için gerekli bitleri ters çevirdiği anlamına gelir; bu da yaş kavramı hakkında güçlü bir açıklama değil. Benzer şekilde, bir (boş olmayan) System.Stringbazı baytların tahsis edildiği ve bitlerin bazı kodlamaya göre bir Unicode karakter dizisini temsil edecek şekilde çevrildiği anlamına gelir.

Dolayısıyla, etki alanı nesnenizi nasıl temsil edeceğinize karar verirken, bir tanık olarak hizmet ettiği süreci düşünmelisiniz. Bir ederse FirstNamegerçekten boş olmayan bir dize olmalıdır, o zaman sistem bazı sürecini koştu o tanık olarak durmak gerektiğini en az bir boşluk olmayan karakter size teslim edildi karakterlerin sırayla oluşturulduğunu olmasını sağlar.

Benzer şekilde, bir Agenesne muhtemelen bazı süreçlerin iki tarih arasındaki farkı hesapladığına tanık olmalıdır. Bu yana ints, genel olarak iki tarih arasındaki işlem sonucu değildir, bunlar ipso yaş temsil fiili yetersizdir.

Yani, neredeyse her zaman her etki alanı nesnesi için bir sınıf (sadece bir program olan) oluşturmak istediğiniz açıktır. Peki sorun ne? Sorun şu ki, C # (ki seviyorum) ve Java sınıflar oluşturmada çok fazla tören talep ediyor. Ancak, alan nesnelerini tanımlamayı çok daha kolay hale getirecek alternatif sözdizimlerini hayal edebiliriz:

//hypothetical syntax
class Age = |start:date - end:date|.Years

(* ML-like syntax *)
type Age(x, y) = datediff(year, x, y)

//C#-like syntax with primary constructors and expression-bodied classes
class Age(DateTime x, DateTime y) => implicit operator int (Age a) => Abs((y - x).Years);

Örneğin F #, bunun için Aktif Desenler şeklinde güzel bir sözdizimine sahiptir :

//Impossible to produce an `Age` without first computing the difference of two dates
//But, any pair (tuple) of dates is implicitly converted to an `Age` when needed.

let (|Age|) (x, y) =  (date_diff y x).Days / 365

Mesele şu ki, programlama diliniz etki alanı nesnelerini tanımlamayı zorlaştırıyorsa, bunu yapmak aslında kötü bir fikir değildir.


† Bunlara post post koşulları da diyoruz , ancak bazı süreçlerin çalışabileceğinin ve çalıştığının kanıtı olarak "tanık" terimini tercih ediyorum.


0

Bir sınıf ile bir ilkel kullanarak neler elde ettiğinizi düşünün. Eğer bir sınıf kullanmak size

  • onaylama
  • biçimlendirme
  • diğer faydalı yöntemler
  • tip güvenlik (yani bir PhoneNumbersınıfla, bir telefon numarası beklediğim bir dize veya rastgele bir dize (yani "salatalık") atamak imkansız olmalıdır)
  • diğer faydalar

ve bu faydalar hayatınızı kolaylaştırır, kod kalitesini, okunabilirliği artırır ve bu tür şeyleri kullanmanın acısını aşar. Aksi takdirde, ilkellere sadık kalın. Yakınsa, bir karar çağrısı yapın.

Ayrıca bazen iyi isimlendirmenin sadece işleyebileceğini (yani firstName, sadece bir string özelliğini saran bütün bir sınıfı yapmak vs adında bir dize). Dersleri çözmenin tek yolu değildir.

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.