İşlevsel programlama bağlamında anemik modelden bahsetmek hala geçerli mi?


38

DDD taktik tasarım modellerinin çoğu, nesne yönelimli paradigmaya aittir ve anemik model, tüm iş mantığının nesneler yerine hizmetlere konması durumunu açıklar ve böylece onları bir tür DTO yapar. Diğer bir deyişle, anemik model, karmaşık model için önerilmeyen bir ussal stilin eş anlamlısıdır.

Saf işlevsel programlama konusunda pek tecrübeli değilim, ancak DDD'nin FP paradigmasına nasıl uyduğunu ve 'anemik model' teriminin hala bu durumda olup olmadığını bilmek istiyorum.

Güncelleme : Recentlry konuyla ilgili kitap ve video yayınladı .


1
Burada ne söylediğimi söylediğimi söylüyorsan, DTO'lar anemiktir, ancak DDD'deki birinci sınıf nesnelerdir ve DTO'lar ile onları işleyen hizmetler arasında doğal bir ayrım vardır. Prensipte aynı fikirdeyim. Görünüşe göre bu blog yazısı yok .
Robert Harvey,


1
“Bu durumda 'anemik model' terimi hala var mı?” Kısa cevap, anemik model terimi, OO bağlamında oluşturuldu. Anemik bir modelin FP bağlamında konuşulması hiç bir anlam ifade etmiyor. Deyimsel FP'nin tanımlanması anlamında eşdeğerler olabilir, ancak anemik modellerle ilgisi yoktur.
plalx

5
Bir keresinde Eric Evans'a, kitabında anlattığı şeyin sadece nesne yönelimli bir tasarım olduğunu suçlayan insanlara ne söylediği sorulmuştu ve bunun bir suçlama olmadığını, gerçek olduğunu söyledi, DDD sadece iyi bir OOD olduğunu yazdı. bazı tarifler ve kalıplar yazıp onlara isimler verilmiş, böylece onları takip etmek ve onlardan bahsetmek daha kolay. Bu nedenle, DDD'nin OOD ile bağlantılı olması şaşırtıcı değil. Daha geniş olan soru, ilk önce "işlevsel programlama" ile ne demek istediğinizi tanımlamanız gerekse de, OOD ve FPD arasındaki kesişme ve farkların ne olduğu olacaktır.
Jörg W Mittag

2
@ JörgWMittag: Her zamanki tanımın dışında mı demek istiyorsun? Çok fazla açıklayıcı platform var, en açık olan Haskell.
Robert Harvey,

Yanıtlar:


22

“Anemik model” probleminin tanımlanma şekli, olduğu gibi FP'ye iyi bir şekilde çevrilemez. İlk önce uygun şekilde genelleştirilmesi gerekir. Kalbinde, anemik bir model, modelin kendisi tarafından kapsanmayan, uygun şekilde nasıl kullanılacağı hakkında bilgi içeren bir modeldir. Bunun yerine, bu bilgi ilgili hizmetler yığını etrafında yayılır. Bu hizmetler sadece modelin müşterileri olmalıdır , ancak kansızlıklarından sorumlu tutuluyorlar . Örneğin, Accounthesapları bir AccountManagersınıf aracılığıyla ele alınmadıkça hesapları etkinleştirmek veya devre dışı bırakmak veya bir hesapla ilgili bilgileri aramak için kullanılamayan bir sınıfı düşünün . Hesap, bazı harici yönetici sınıflarından değil temel işlemlerden sorumlu olmalıdır.

İşlevsel programlamada, veri tipleri modellemesi gerekenleri doğru bir şekilde göstermediğinde de benzer bir problem vardır. Kullanıcı kimliklerini temsil eden bir tür tanımlamamız gerektiğini varsayalım. Bir "anemik" tanımı, kullanıcı kimliklerini dizge olarak belirtir. Bu teknik olarak mümkün, ancak kullanıcı kimlikleri rastgele dizeler gibi kullanılmadığı için büyük sorunlara yol açıyor. Bunları birleştirmek ya da altlarını kesmek bir anlam ifade etmiyor, Unicode gerçekten önemli olmamalı ve URL'lerde ve katı karakter ve format sınırlamaları olan diğer bağlamlarda kolayca gömülebilir olmalıdır.

Bu problemi çözmek genellikle birkaç aşamada gerçekleşir. Basit bir ilk kesim, "a UserID, bir dize ile eşit olarak temsil edilir, ancak farklı türlerdir ve birini diğerinden beklediğiniz yerde kullanamazsınız" demektir. Haskell (ve bazı diğer yazılı dilleri) bu özelliği aşağıdakiler aracılığıyla sağlar newtype:

newtype UserID = UserID String

Bu tanımlar UserIDverilen bir işlevi Stringolan bir değer oluşturur gibi tedavi bir UserIDtipi sistem tarafından, fakat sadece halen Stringzamanında. Artık fonksiyonlar UserIDbir string yerine onların gerekli olduğunu ilan edebilir ; UserIDDaha önce dizeleri kullanmakta olduğunuz s tuşunu kullanarak birlikte s tuşunu birleştiren koda karşı korumaları kullanın UserID. Tip sistemi olamayacağını garanti eder, test gerektirmez.

Burada zayıflık kodu hala rasgele herhangi alabilir olmasıdır Stringgibi "hello"bir inşa UserIDondan. Diğer adımlar arasında, bir dize verildiğinde bazı değişmezleri denetleyen ve yalnızca UserIDmemnun kaldıklarında döndüren "akıllı kurucu" işlevi oluşturma yer alır . Daha sonra "aptal" UserIDbir istemci istiyorsa yapıcı özel yapılmış yani edilir UserIDonlar gerekir böylece ortaya gelmesini hatalı oluşturulmuş userids önlenmesi, akıllı Oluşturucu kullanın.

Daha ileri adımlar bile UserIDveri tipini, hatalı tanımlanmış veya "uygun olmayan" bir tanımı basitçe tanımlayarak inşa etmenin imkansız olduğu şekilde tanımlar. Örneğin, bir UserIDbasamak listesi olarak tanımlamak :

data Digit = Zero | One | Two | Three | Four | Five | Six | Seven | Eight | Nine
data UserID = UserID [Digit]

Bir UserIDbasamak listesi oluşturmak için sağlanmalıdır. Bu tanım göz önüne alındığında UserID, bir URL'de temsil edilemeyen bir varlığın mümkün olmadığını göstermek önemsizdir . Haskell'de bunun gibi veri modellerinin tanımlanmasına genellikle , tip sisteminin kodunuz hakkında daha fazla değişmezleri tanımlamasını ve kanıtlamasını sağlayan Veri Türleri ve Genelleştirilmiş Cebirsel Veri Tipleri (GADT'ler) gibi gelişmiş tip sistem özellikleri yardımcı olur . Veriler davranıştan ayrıştırıldığında, davranış tanımlamanız gereken tek şey veri tanımınızdır.


2
Ve değişmeyenleri koruyan agregalar ve agrega kökleri ne olacak, sonuncular da daha sonra geliştiriciler tarafından kolayca ifade edilip açıklanabilir mi? Benim için DDD'nin en değerli kısmı, iş modelinin doğrudan koda eşlenmesidir. Ve cevabın tam olarak bununla ilgili.
Pavel Voronin

2
Güzel konuşma, ancak OP sorusuna cevap yok.
SerG

10

Değişmezlik, bir dereceye kadar değişmezlik, OOP'un savunucuları olarak işlevlerinizi verilerinizle sıkı bir şekilde birleştirmenizi gereksiz kılar. Özgün veri yapısından beklenmedik bir şekilde altınızdan çıkma korkusu duymadan, orijinal koddan çok uzakta kaldırılan kodda türev veri yapıları oluştururken bile istediğiniz kadar kopya oluşturabilirsiniz.

Bununla birlikte, bu karşılaştırmayı yapmanın daha iyi bir yolu muhtemelen model katmanına hangi işlevleri hangi hizmetler katmanına ayırdığınıza bakmaktır . OOP ile aynı görünmese de, FP'de çok sayıda soyutlama seviyesinin tek bir fonksiyonda olması gerektiğini sıkıştırmaya çalışmak oldukça yaygın bir hatadır.

Bildiğim kadarıyla, hiç kimse buna anemik bir model demiyor, çünkü bu bir OOP terimi, ancak etki aynı. Uygulanabildiği her yerde genel işlevleri yeniden kullanabilir ve yeniden kullanmalısınız, ancak daha karmaşık veya uygulamaya özgü işlemler için, yalnızca modelinizle çalışmak için zengin bir işlev seti sağlamanız gerekir. Uygun soyutlama katmanları oluşturmak, herhangi bir paradigmada iyi bir tasarımdır.


2
“Bir dereceye kadar değişmezlik, işlevlerinizi OOP'un savunucuları olarak verilerinizle sıkıca birleştirmenizi gereksiz kılıyor”.
Giorgio

2
DDD bağlamında verilerle birleştirme davranışının temel avantajı, işle ilgili anlamlı bir arayüz sağlamaktır; Sadece her zaman el altında. Kendinden belgelendirme kodunun doğal bir yoluna sahibiz (en azından alışkın olduğum yöntem) ve bu, iş uzmanlarıyla başarılı bir iletişim kurmanın anahtarıdır. O zaman FP'de nasıl başarılır? Muhtemelen borulama yardımcı olur, ama başka ne var? FP'nin genel niteliği iş gereksinimlerini kodlardan tersine çevirmek için zorlaştırmaz mı?
Pavel Voronin

7

OOP'de DDD kullanıldığında, iş mantığını etki alanı nesnelerine koymak için ana nedenlerden biri, iş mantığının genellikle nesnenin durumunu değiştirerek uygulanmasıdır. Bu, kapsülleme ile ilgilidir: Employee.RaiseSalarybüyük olasılıkla , açıkça belirlenememesi gereken salary, Employeeörneğin alanını değiştirir .

FP'de mutasyondan kaçınıldığından, bu davranışı RaiseSalarymevcut Employeebir örneği alan ve yeni maaş ile yeni bir Employee örnek döndüren bir işlev oluşturarak uygularsınız . Yani hiçbir mutasyon söz konusu olmaz: sadece orijinal nesneden okuma ve yeni nesneyi oluşturma. Bu nedenle, böyle bir RaiseSalaryfonksiyonun Employeesınıfta bir yöntem olarak tanımlanması gerekmez , fakat her yerde yaşayabilir.

Bu durumda, verileri davranıştan ayırmak doğal hale gelir: bir yapı Employeeas veriyi temsil eder (tamamen anemik), bir (veya birkaç) modül bu veriler üzerinde çalışan (değişmezliği koruyan) fonksiyonlar içerir.

Verileri ve davranışları DDD'deki gibi birleştirdiğinizde genellikle Tek Sorumluluk İlkesini (SRP) ihlal ettiğinize dikkat edin: EmployeeMaaş değişiklik kuralları değişiyorsa değişmesi gerekebilir; ancak, EOY bonus hesaplamasında kullanılan kuralların değişip değişmediği de değişebilir. Dekuplaj yaklaşımı ile durum böyle değildir, çünkü her biri tek bir sorumluluk taşıyan birkaç modüle sahip olabilirsiniz.

Dolayısıyla, her zamanki gibi FP yaklaşımı daha fazla modülerlik / birleştirilebilirlik sağlar.


-1

Meselenin özü, modelde çalışan hizmetlerde tüm alan mantığına sahip anemik bir modelin temelde prosedürel programlama olduğudur - "akıllı" olan ve sadece veri içermeyen "gerçek" OO programlamanın tersine aynı zamanda verilere en yakın olan mantık.

Ve aynı kontrast işlevsel programlama ile de mevcuttur: "gerçek" FP, sinek üzerine inşa edilen ve geri dönüş değeri olarak döndürülen, parametreler olarak dolaşılan birinci sınıf varlıklar olarak fonksiyonların kullanılması anlamına gelir. Ancak tüm bu gücü kullanamadığınızda ve yalnızca aralarında geçen veri yapılarında çalışan işlevlere sahip olduğunuzda, o zaman aynı yerde kalırsınız: temel olarak prosedürel programlama yapıyorsunuz.


5
Evet, temelde OP'nin sorusunda söylediği şey bu. İkiniz de hala işlevsel kompozisyona
Robert Harvey,

-3

DDD'nin FP paradigmasına nasıl uyduğunu bilmek istiyorum.

Bence öyle ama büyük oranda değişmez Değerli Nesneler arasında geçiş yapmak için taktiksel bir yaklaşım veya varlıklar üzerindeki yöntemleri tetiklemenin bir yolu olarak düşünüyorum . (Mantığın çoğunun hala varlıkta yaşadığı yer.)

ve 'anemik model' teriminin hala bu durumda olup olmadığı.

Peki, "geleneksel OOP'a benzer bir şekilde" demek istiyorsanız, normal uygulama ayrıntılarını göz ardı etmenize ve temel konulara geri dönmenize yardımcı olur: Etki alanı uzmanlarınız hangi dili kullanıyor? Kullanıcılarınızdan aldığınız niyet nedir ?

Birlikte süreçleri ve fonksiyonları zincirleme hakkında konuşmak Diyelim, o zaman fonksiyonları gibi görünüyor (ya da en azından "do-er" nesneleri) temelde vardır alanınıza nesneler!

Bu nedenle, bu senaryoda, "işlevleriniz" gerçekten çalıştırılamadığında ve bunun yerine , asıl işi yapan bir Servis tarafından yorumlanan meta veri takımyıldızları olduğunda muhtemelen bir "anemik model" ortaya çıkacaktır .


1
İşlemler için farklı işlevlere tote, kayıt veya liste gibi soyut veri türlerini ilettiğinizde anemik bir model ortaya çıkar. “Yürütmeyen bir işlev” (ne olursa olsun) kadar egzotik bir şeye ihtiyacınız yok.
Robert Harvey,

Bu nedenle, etiket, anemik olduklarında ne kadar uygunsuz hale geldiklerini vurgulamak için “işlevler” etrafındaki tırnak işaretidir.
Darien

Eğer ironik davranıyorsanız, biraz ince.
Robert Harvey,
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.