İlkel saplantı ne zaman kod kokusu olmaz?


22

Son zamanlarda, ilkel takıntıyı bir kod kokusu olarak tanımlayan birçok makaleyi okudum .

İlkel takıntıdan kaçınmanın iki faydası vardır:

  1. Etki alanı modelini daha açık hale getirir. Örneğin, bir iş analisti ile posta kodu içeren bir dize yerine Posta Kodu hakkında konuşabilirim.

  2. Tüm doğrulama, uygulama genelinde değil, tek bir yerde yapılır.

Dışarıda kod kokusu olduğunu açıklayan pek çok makale var. Örneğin, böyle bir posta kodu için ilkel takıntının kaldırılmasının yararını görebiliyorum:

public class Address
{
    public ZipCode ZipCode { get; set; }
}

İşte ZipCode'un kurucusu:

public ZipCode(string value)
    {
        // Perform regex matching to verify XXXXX or XXXXX-XXXX format
        _value = value;
    }

Sen kırılma olacağını KURU posta kodu kullanıldığı her yerde bu doğrulama mantığını koyarak prensibini.

Ancak, aşağıdaki nesnelere ne dersiniz:

  1. Doğum Tarihi: Kontrol tarihinden önce ve bugünün tarihinden daha az olup olmadığını kontrol edin.

  2. Maaş: Sıfıra eşit veya daha büyük olduğunu kontrol edin.

DateOfBirth nesnesi ve Maaş nesnesi oluşturur musunuz? Bunun yararı, etki alanı modelini tarif ederken onlar hakkında konuşabilmenizdir. Bununla birlikte, bu çok fazla doğrulama olmadığı için bir mühendislik çalışmasıdır. İlkel takıntının ne zaman ve ne zaman çıkarılmayacağını açıklayan bir kural var mı, yoksa her zaman mümkünse ne yapmalısınız?

Sanırım sınıf yerine bir tür takma ad oluşturabilirim, bu da yukarıda belirtilen noktaya yardımcı olacaktır.


8
"Bir posta kodunun kullanıldığı her yerde doğrulama mantığını koyarak DRY ilkesini çiğniyor olacaksınız." Bu doğru değil. Veri modülünüze girildiği anda doğrulama yapılmalıdır . Birden fazla "giriş noktası" varsa, doğrulama tekrar kullanılabilir bir birimde olmalı ve bunun DTO olması gerekmez (ya da olmamalıdır) ...
Timothy Truckle

1
Nasıl kontrol etmesi için kurucusuna "bakan" ve "bugünün tarihi" DateOfBirthveriyorsun?
Caleth

11
Özel türler oluşturmanın bir başka avantajı da tür güvenliğidir. Eğer sahipseniz Salaryve Distanceitirazınız varsa yanlışlıkla bunları birbirlerinin yerine kullanamazsınız. Her ikisi de tür ise, yapabilirsiniz double.
Scroog1

3
@ w0051977 İfade edersiniz (anladığım gibi), DTO'ların kurucusunda doğrulamanın yapılmasından başka bir şeyin DRY'yi ihlal edeceğini ima ettiniz. Aslında doğrulama DTO dışında olmalı ...
Timothy Truckle

2
Bana göre hepsi kapsam meselesi. İlkellere geniş bir kapsam verirseniz, kötüye kullanılıp yanlış kullanılabilecekleri çok sayıda yol vardır. Genelde onlara daha dar bir kapsam vermek istersiniz ve bunu yapmanın bir yolu, onu uygulamak için bir içsel olarak özel olarak depolanmış ilkel kullanarak bir kavramı temsil eden bir sınıf tasarlamaktır. Şimdi ilkelin kapsamı dar ve yanlış kullanılması / yanlış kullanılması olası değildir ve etkili bir şekilde değişmezleri koruyabilirsiniz. Fakat eğer ilkelin kapsamı başlangıçta dardıysa, bu fazlaca olabilir ve sürdürülmesi gereken birçok ekstra bağlantı ve kod getirebilir.

Yanıtlar:


17

İlkel Gözlem, etki alanı fikirlerini temsil etmek için ilkel veri türlerini kullanıyor.

Bunun tam tersi "alan modellemesi" veya belki de "aşırı mühendislik" olacaktır.

DateOfBirth nesnesi ve Maaş nesnesi oluşturur musunuz?

Bir Maaş nesnesinin tanıtılması, aşağıdaki nedenden ötürü iyi bir fikir olabilir: sayılar etki alanı modelinde nadiren tek başına durur, hemen hemen her zaman bir boyutları ve bir birimleri vardır. Bir zamana veya kütleye uzunluk eklersek normalde işe yarar bir şey modellemeyiz ve metre ve ayakları karıştırdığımız zaman nadiren iyi sonuçlar alırız .

DateOfBirth'e gelince, muhtemelen - dikkate alınması gereken iki konu var. İlk olarak, ilkel olmayan bir Tarih oluşturmak, tarih meselesi ile ilgili tüm tuhaf kaygıları merkezlemek için bir yer sağlar. Birçok dil kutudan bir tane sağlar; DateTime , java.util.Date . Bunlar tarih agnostik uygulamalarıdır, ancak ilkel değildirler .

İkincisi, DateOfBirthgerçekten bir tarih zamanı değil; ABD'de burada "doğum tarihi" kültürel bir yapı / yasal kurgudur. Biz gelen doğum tarihi ölçmek eğilimindedir yerel bir kişi doğum tarihi; Kaliforniya'da doğan Bob, ikisinin daha genç olmasına rağmen, New York'ta doğmuş Alice'ten daha erken bir doğum tarihine sahip olabilir .

İlkel takıntının ne zaman ve ne zaman çıkarılmayacağını açıklayan bir kural var mı, yoksa her zaman mümkünse yapmalısınız.

Kesinlikle her zaman değil; Sınırlarda, uygulamalar nesneye yönelik değildir . Testlerdeki davranışları tanımlamak için kullanılan ilkelleri görmek oldukça yaygındır .


1
En üstteki alıntıdan sonraki ilk yorum siradan görünmüyor. Buna ek olarak, sadece sorunun konusunu yeniden ifade ediyor. Aksi halde iyi bir cevap ama bunu gerçekten rahatsız edici buluyorum.
JimmyJames

ne C # DateTime ne de java.util.Date, DateOfBirth için altta yatan türler değildir.
kevin cline

Belki yenisi java.util.Dateile değiştirjava.time.LocalDate
Koray Tugay

7

Dürüst olmak gerekirse: bağlıdır.

Her zaman kodunuzu devralma riski vardır. DateOfBirth ve Maaş ne kadar yaygın kullanılır? Bunları sadece üç sıkı bağlantılı sınıfta mı kullanacaksınız, yoksa uygulama genelinde mi kullanılacak? Tek bir kısıtlamayı uygulamak için onları “Tip” / Sınıf içine yerleştirir misiniz, yoksa gerçekten oraya ait daha fazla kısıtlama / işlev düşünebilir misiniz?

Örneğin, Maaş alalım: "Maaş" ile herhangi bir işleminiz var mı (örneğin farklı para birimleri ile mi, yoksa bir toString () işlevi ile)? Basit bir ilkel olarak bakmadığınızda Maaşın ne yaptığını / ne yaptığını düşünün ve Maaşın kendi sınıfı olması için iyi bir şans var.


Bir tür diğer adı iyi bir alternatif midir?
w0051977

@ w0051977 charonx ve type alias ile aynı fikirdeyim alternatif olabilir
techagrammer

@ w0051977, birincil amaç katı yazmaya zorlamak, belirli bir değerin (Maaş) ne olduğunu açık bir şekilde belirtmek, "kayan dolar" ın (Saat? Hafta? Ay?)? "yüzer maaş" (aylık? Yıl?). Bu gerçekten ihtiyaçlarınızın ne olduğuna bağlı.
CharonX

@ CharonX, bir ondalık maaş için yüzer değil kullanılması gerektiğini düşünüyorum. Katılıyor musun?
w0051977

@ W0051977 İyi bir ondalık türünüz varsa, o zaman bu tercih edilebilir, evet. (Şu anda bir C ++ projesi üzerinde çalışıyorum, bu nedenle boolean, tam sayı ve yüzer aklımda ön sırada yer alıyor)
CharonX

5

Muhtemel bir temel kural, programın katmanına bağlı olabilir. For Alan aka (DDD) Varlıkları Katmanı şey yoluna kadar (Martin, 2018), bu kudreti "etki alanı / iş konseptini temsil eden herhangi bir şey için ilkeller önlemek için". Gerekçeler OP tarafından belirtildiği gibidir: daha etkileyici bir etki alanı modeli, iş kuralları doğrulaması, dolaylı kavramların açıklanması (Evans, 2004).

Bir tür diğer adı hafif bir alternatif olabilir (Ghosh, 2017) ve gerektiğinde varlık sınıfına yeniden yansıtılabilir. Örneğin, önce bunun Salaryolmasını isteyebiliriz >=0ve daha sonra (müşteriyi iflas edecek) $100.33333yukarıda belirtilen herhangi bir şeyi ve buna izin vermeme kararı alabiliriz $10,000,000. Kullanımı Nonnegativeilkel temsil etmek Salaryve diğer kavramlar bu üstlenmeden karmaşık hale getirecek.

İlkellerden kaçınmak, aşırı mühendislikten kaçınmaya da yardımcı olabilir. Maaş ve Doğum Tarihini bir veri yapısında birleştirmemiz gerektiğini varsayalım : örneğin, daha az yöntem parametresi olması veya modüller arasında veri aktarımı için. O zaman türüne sahip bir tuple kullanabiliriz (Salary, DateOfBirth). Aslında, ilkellerden oluşan bir demet (Nonnegative, Nonnegative), bilgi vermez, oysa bazı şişirilmiş class EmployeeDataalanlar diğerlerinin arasında gerekli alanları gizler. Söz konusu imza , Arabirim Ayrıştırma İlkesini ihlal edenlere calcPension(d: (Salary, DateOfBirth))göre daha fazla odaklanmıştır calcPension(d: EmployeeData). Aynı şekilde, bir uzman class SalaryAndDateOfBirthgarip görünüyor ve muhtemelen bir overkill. Daha sonra bir veri sınıfı tanımlamayı seçebiliriz; dokular ve temel alan türleri bu tür kararları ertelememize izin verir.

Dış bir katmanda (örneğin GUI), varlıkları kurucu ilkellerine kadar "soymak" mantıklı olabilir (örneğin bir DAO içine koymak için). Bu, Martin (2018) 'de iddia edildiği gibi dış alanlara sızan alan soyutlamalarını önler.

Kaynaklar
E. Evans, "Etki Alanına Dayalı Tasarım", 2004
D. Ghosh, "İşlevsel ve Reaktif Etki Alanı Modellemesi", 2017
RC Martin, "Temiz mimari", 2018


Tüm referanslar için +1.
w0051977

4

İlkel Gözlem ya da Mimari Astronot olmaktan daha iyi acı mu çekiyorsunuz ?

Her iki vaka da patolojiktir, bir durumda çok az soyutlama yaparsınız, bir elmanın bir portakal için tekrarlanmasına ve kolayca karışmasına neden olur, diğerinde ise onunla durmayı ve bir şeyler yapmayı zorlaştırmayı bırakmayı unutmuşsunuzdur. .

Neredeyse her zaman olduğu gibi, ılımlı olmayı, umarım iyi düşünülmüş bir orta yolu istersiniz.

Bir türün yanı sıra bir özelliğin de bir adı olduğunu unutmayın. Ayrıca, bir adresin bileşen parçalarına ayrıştırılması, her zaman aynı şekilde yapıldığında çok da kısıtlayıcı olabilir. Tüm dünya şehir NY değil.


3

Maaş sınıfınız olsaydı, ApplyRaise gibi yöntemlere sahip olabilirdi.

Öte yandan, ZipCode sınıfınızın, her yerde doğrulamayı tekrarlamaktan kaçınmak için dahili bir onaylama zorunluluğu yoktur; bu nedenle, enjekte edilebilecek bir ZipCodeValidator sınıfına sahip olabilirsiniz; Doğrulayıcıyı doğrulayın ve AUS adreslerini kullanmanız gerektiğinde, yeni bir onaylayıcı ekleyebilirsiniz.

Başka bir endişe, EntityFramework aracılığıyla bir veritabanına veri yazmak zorunda olmanız durumunda, Maaş veya ZipCode ile nasıl başa çıkılacağını bilmek gerekecektir.

Akıllı sınıfların nasıl olması gerektiği arasında nasıl bir çizgi çizileceğine dair kesin bir cevap yok, ancak iş mantığını doğrulama gibi, veri sınıflarının saf veri olduğu gibi iş mantığı sınıflarına taşıma eğiliminde olduğumu söyleyeceğim. EntityFramework ile daha iyi çalışmak için.

Tür takma adlarını kullanma konusunda üye / özellik adı, içerik hakkında gereken tüm bilgileri vermelidir, bu nedenle tür takma adı kullanmazdım.


Bir tür diğer adı iyi bir alternatif midir?
w0051977

2

(Muhtemelen soru gerçekten nedir?)

İlkel tür kullanımı ne zaman bir kod kokusudur?

(Cevap)

Parametrenin içinde kurallar yoksa - ilkel bir tür kullanın.

Beğeniler için ilkel türü kullanın:

htmlEntityEncode(string value)

Aşağıdakiler için nesne kullan:

numberOfDaysSinceUnixEpoch(SimpleDate value)

Sonraki örnek, bu kurallar, örneğin, nesnenin sahip SimpleDateoluşur Year, Monthve Day. Bu durumda Nesne kullanımıyla, SimpleDategeçerli olma kavramı nesne içinde kapsanabilir.


1

Bu soruda başka yerde verilen e-posta adresleri veya posta kodlarının kanonik örnekleri dışında, İlkel Gözlemden uzaklaşmayı yeniden tespit ettiğimde, özellikle kurum kimlikleri konusunda yardımcı olabilir (bkz. Https://andrewlock.net/using-strongly-typed-entity -id-to-önlemek-ilkel-saplantı-bölüm-1 / . NET'te nasıl yapılacağına bir örnek için).

Bir yöntemin böyle bir imzası olduğu için, bir böceğin çarpma sayısını sayımı kaybettim:

int leaveId = 12345;
int submitterId = 23456;
int approverId = 34567;

SubmitLeaveApplication(leaveId, approverId, submitterId);

public void SubmitLeaveApplication(int leaveId, int submitterId, int approverId) {
  // implementation here
}

Derler, para cezası derler ve ünite testlerine sert davranmazsanız, bunu da geçebilir. Ancak, bu varlık kimliklerini etki alanına özgü sınıflar olarak yeniden düzenleyin ve hey presto, derleme zamanı hataları:

LeaveId leaveId = 12345;
SubmitterId submitterId = 23456;
ApproverId approverId = 34567;

SubmitLeaveApplication(leaveId, approverId, submitterId);

public void SubmitLeaveApplication(LeaveId leaveId, SubmitterId submitterId, ApproverId approverId) {
  // implementation here
}

Bu yöntemin, tüm intveri türlerinde 10 veya daha fazla parametreye kadar ölçeklendirildiğini hayal edin ( Long Parameter List kodunun kokusunu almayın). otomatik haritalama tarafından algılanmaz.


0

Bir posta kodunun kullanıldığı her yerde doğrulama mantığını koyarak DRY prensibini bozuyordunuz.

Öte yandan, birçok farklı ülke ve farklı posta kodu sistemleriyle uğraşırken, söz konusu ülkeyi tanımadığınız sürece posta kodunu doğrulayamazsınız. Bu yüzden ZipCodesınıfınızın ülkeyi de saklaması gerekiyor.

Ancak, ülkeyi Address(posta kodunun da bir parçası olan) hem de posta kodunun bir parçası olarak (doğrulama için) ayrı ayrı saklıyor musunuz?

  • Bunu yaparsanız, DRY'yi de ihlal ediyorsunuz. Bunu DRY ihlali olarak adlandırmasanız bile (çünkü her bir örnek farklı bir amaca hizmet ediyor), yine de iki ülke değeri farklı olduğunda (yani mantıksal olarak asla ) olabilir.
    • Veya, alternatif olarak, her zaman aynı olduklarından emin olmak için iki veri noktasını senkronize etmeniz gerekmekte, bu da verileri gerçekten tek bir noktada saklamanız gerektiğini, bu nedenle amacı yitirmenizi önermektedir.
  • Siz yapmazsanız, o zaman bir ZipCodesınıf değil, bir Addresssınıf olacaktır string ZipCode.

Örneğin, bir iş analisti ile posta kodu içeren bir dize yerine Posta Kodu hakkında konuşabilirim.

Bunun yararı, etki alanı modelini tarif ederken onlar hakkında konuşabilmenizdir.

Temelde, bir bilgi parçasının belirli bir değişken tipine sahip olduğu zaman, bir iş analistiyle konuşurken her nasılsa o tipten bahsetmek zorunda olduğunuz iddiasını anlamıyorum.

Niye ya? Neden sadece "posta kodu" hakkında konuşamıyor ve belirli bir türü tamamen atlıyorsunuz? Mülkiyetin türünün konuşmaya özelleştiği iş yerinde (teknik değil!) Analistle ne tür tartışmalar yapıyorsunuz?

Ben nereliyim, posta kodları her zaman sayısaldır. Yani bir seçeneğimiz var, onu bir intveya bir olarak depolayabiliriz string. Bir dize kullanma eğilimindeyiz, çünkü verilerde matematiksel işlem beklentisi yoktur, ancak hiçbir zaman bir iş analisti bana bunun bir dize olması gerektiğini söylemedi. Bu karar geliştiriciye bırakılmıştır (veya tartışmaya göre teknik analist olsa da, benim deneyimime göre nitty kumlu ile doğrudan ilgilenmiyorlar).

Bir iş analisti, uygulama yapması beklenenleri yaptığı sürece veri türünü önemsemez.


Doğrulama başa çıkması zor bir hayvandır, çünkü insanların beklentilerine dayanır.

Birincisi, ilkel takıntının neden önlenebileceğini göstermenin bir yolu olarak doğrulama argümanına katılmıyorum, çünkü (evrensel bir gerçek olarak) verilerin her zaman her zaman doğrulanması gerektiğine katılıyorum.

Örneğin, eğer bu daha karmaşık bir arama ise? Basit bir format kontrolü yerine, doğrulamanız harici bir API ile iletişim kurmayı ve cevap beklemeyi gerektiriyorsa ne olur? Uygulamanızı, ZipCodebaşlattığınız her nesne için bu harici API'yi çağırması için gerçekten zorlamak istiyor musunuz ?
Belki de sıkı bir iş gereksinimi ve sonra elbette haklı. Ancak bu evrensel bir gerçek değil. Bunun bir çözümden çok daha fazla yük olduğu birçok kullanım durumu olacaktır.

İkinci bir örnek olarak, adresinizi bir forma girerken posta kodunuzu ülkenizden önce girmek yaygındır. Kullanıcı arabiriminde anında doğrulama geri bildirimine sahip olmak güzel olsa da, uygulama beni "yanlış" bir posta kodu biçiminde uyardıysa (kullanıcı olarak) bu benim için bir engeldir, çünkü sorunun asıl kaynağı (örn.) ülkem varsayılan olarak seçilen ülke değil ve bu nedenle yanlış ülke için doğrulama gerçekleşti.
Kullanıcıyı rahatsız eden ve gereksiz karışıklığa neden olan yanlış hata mesajıdır.

Nasıl sürekli onaylamanın evrensel bir gerçek olmadığı gibi, benim de örneklerim. Bu var içeriksel . Bazı uygulama alanları, her şeyden önce veri doğrulaması gerektirir. Diğer alanlar, öncelikler listesinde yüksek olan bir doğrulama yapmaz; çünkü beraberinde getirdiği güçlük, asıl öncelikleriyle çakışır (örneğin, kullanıcı deneyimi veya başlangıçta hatalı verileri kaydetme yeteneği; saklanmış)

Doğum Tarihi: Kontrol tarihinden önce ve bugünün tarihinden daha az olup olmadığını kontrol edin.
Maaş: Sıfıra eşit veya daha büyük olduğunu kontrol edin.

Bu doğrulamalarla ilgili sorun eksik, fazla veya çok daha büyük bir sorunun göstergesidir .

Bir tarihin, randevundan daha büyük olduğunu kontrol etmek gereksiz. Bakan, tam anlamıyla mümkün olan en küçük tarih olduğu anlamına gelir. Ayrıca, alaka düzeyini nerede çiziyorsun? Engellemenin DateTime.MinDateancak izin vermenin amacı nedir DateTime.MinDate.AddSeconds(1)? Diğer birçok değere kıyasla özellikle yanlış olmayan belirli bir değeri şifreliyorsunuz.

Doğum günüm 2 Ocak 1978 (öyle değil, ama farz edelim ki). Ancak uygulamanızdaki verilerin yanlış olduğunu ve bunun yerine doğum günümün geçerli olduğunu söyleyelim:

  • 1 Ocak 1978
  • 1 Ocak 1722
  • 1 Ocak 2355

Bu tarihlerin hepsi yanlış. Hiçbiri diğerinden daha "doğru" değil. Ancak validasyon kuralınız bu üç örnekten sadece birini yakalar .

Bu verileri nasıl kullandığınızın içeriğini de tamamen göz ardı ettiniz. Bu, örneğin bir doğumgünü hatırlatıcısı botunda kullanılıyorsa, doğrulamanın doldurulmasının belirli bir kötü sonucu olmadığından, onaylamanın anlamsız olduğunu söyleyebilirim.
Öte yandan, eğer bu bir devlet verisi ise ve bir kişinin kimliğini doğrulamak için doğum tarihine ihtiyacınız varsa (ve bunu yapmamanız kötü sonuçlara yol açar, örneğin birinin sosyal güvenliğini reddetmek), o zaman verilerin doğruluğu çok önemlidir ve tamamen veriyi doğrula Şu anda sahip olduğunuz önerilen doğrulama yeterli değil.

Maaş için, olumsuz olamayacağına dair bazı sağduyular vardır. Fakat gerçekçi olmayan saçma verilerin girilmesini bekliyorsanız, bu saçma verinin kaynağını araştırmanızı öneririm. Çünkü, duygusal veri girmek için güvenilmezlerse, doğru verileri girmeleri için onlara güvenemezsiniz .

Bunun yerine maaş, başvurunuz tarafından hesaplanırsa ve bir şekilde, negatif (ve doğru) bir sayı ile Math.Max(myValue, 0)sonuçlanabilirse, doğrulama başarısız olmak yerine, negatif sayıları 0'a çevirmek daha iyi bir yaklaşım olacaktır . Çünkü mantığınız sonucun negatif bir sayı olduğuna karar verdiyse, onaylamanın başarısız olması, hesaplamayı tekrar yapmak zorunda kalacağı anlamına gelir ve ikinci kez farklı bir sayı ile geleceğini düşünmek için hiçbir neden yoktur.
Ve eğer farklı bir rakamla gelirse, bu yine hesaplamanın tutarlı olmadığını ve bu nedenle güvenilir olamayacağından şüphelenmenizi sağlar.

Bu, onaylamanın faydalı olmadığını söylemek değildir. Fakat anlamsız doğrulama kötüdür, çünkü hem bir sorunu çözmemek hem de insanlara yanlış bir güvenlik hissi vermek için çaba harcar.


Bir çocuğun doğum tarihi, şu anda bir bebek şu anda bir sonraki güne atlamış bir zaman diliminde doğmuşsa, şu anki tarihi geçmiş olabilir. Bir hastane “beklenen doğum tarihini” gelecek aylarda olabilecek bir veritabanında saklayabilir. Bunun için farklı bir tür ister misiniz?
gnasher729

@ gnasher729: Takip ettiğimden emin değilim, benimle aynı fikirde gibi görünüyorsunuz (doğrulama bağlamsal ve evrensel olarak doğru değil), ancak yorumunuzun ifadesi size katılmıyorum sanıyor. Yoksa yanlış mı okudum?
Flater
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.