Neden bir anemik etki alanı modeli C # / OOP'ta kötü kabul edilir, ancak F # / FP'de çok önemlidir?


46

F # 'daki bir blog yazısında eğlence ve kazanç için:

İşlevsel bir tasarımda, davranışı verilerden ayırmak çok önemlidir. Veri türleri basit ve "aptal". Ve ayrıca, ayrı olarak, bu veri türlerine etki eden bir takım işlevlere sahipsiniz.

Davranış ve verilerin birleştirilmesi gereken nesneye yönelik tasarımın tam tersi budur. Sonuçta, tam olarak bir sınıf budur. Gerçekten nesne yönelimli bir tasarımda, aslında davranıştan başka hiçbir şeye sahip olmamanız gerekir - verilere özel ve sadece yöntemlerle erişilebilir.

Aslında, OOD'de, bir veri türü etrafında yeterli davranışa sahip olmamak, Kötü Bir Şey olarak kabul edilir ve hatta bir adı vardır: " anemik etki alanı modeli ".

C # 'dan F #' dan ödünç almaya devam ettiğimiz ve daha işlevsel stil kodu yazmaya çalıştığımız göz önüne alındığında; Neden veriyi / davranışını ayırma fikrini ödünç almıyoruz ve hatta kötü olduğunu düşünüyoruz? Tanımın OOP ile birlikte olmaması mı, yoksa C # da kötü olmasının somut bir nedeni var mı? Bazı nedenlerden dolayı F # 'da geçerli değil (ve aslında, tersine çevrilmiş)?

(Not: C # / F # 'daki, blog postasındaki herhangi bir görüşe katılmayacak kişiler yerine, neyin iyi / kötü olduğu fikrini değiştirebilecek farklılıklarla özellikle ilgileniyorum).


1
Selam Dan! Davranışların ilham verici. .NET (ve haskell) platformunun yanı sıra, skalaya bakmanızı da tavsiye ediyorum. Debashish ghosh, işlevsel araçlarla etki alanı modellemesi hakkında bir çift blog yazdı, benim için anlayışlıydı, umarım sizin için de buradasınız: debasishg.blogspot.com/2012/01/…
AndreasScheinert

2
Bugün bir meslektaşım tarafından ilginç bir blog yazısı gönderildi: blog.inf.ed.ac.uk/sapm/2014/02/04/… İnsanların anemik alan modellerinin düpedüz kötü olduğu fikrine itiraz etmeye başladılar ; ki bence iyi bir şey olabilir!
Danny Tuppeny

1
Başvurduğunuz blog yazısı yanlış bir fikir üzerine kuruludur: "Verilerin normalde kapsüllenmeden gösterilmesi normaldir. Veriler değişmezdir, bu nedenle yanlış bir işlev nedeniyle" zarar göremez ". Değişmez tiplerde bile korunması gereken değişmezler vardır ve bu verilerin gizlenmesini ve nasıl yaratılabileceğini kontrol etmeyi gerektirir. Örneğin, değişmez bir kırmızı-siyah ağacın uygulamasını gösteremezsiniz, çünkü o zaman birisi sadece kırmızı düğümlerden oluşan bir ağaç oluşturabilir.
Doval

4
@Doval dürüst olmak gerekirse, birisi diskinizi doldurabileceğinden, bir dosya sistemi yazıcısını açığa çıkaramayacağınızı söylemek gibi. Sadece kırmızı düğümlerden oluşan bir ağaç yaratan biri, klonlandığı kızıl-siyah ağaca ve bu iyi biçimlendirilmiş örneği kullanacak olan sistemdeki herhangi bir koda kesinlikle zarar vermez. Aktif olarak yeni çöp örnekleri oluşturan veya tehlikeli şeyler yapan bir kod yazarsanız, değişmezlik sizi kurtarmaz, ancak başkalarını sizden korur . Uygulamanın gizlenmesi, insanların sıfıra bölünmesiyle sonuçlanan saçma bir kod yazmalarını engellemez.
Jimmy Hoffa

2
@JimmyHoffa Katılıyorum, ama eleştirdiğim şeyle ilgisi yok. Yazar değişmez olduğu için normalde verinin açığa çıkmasının normal olduğunu iddia ediyor ve değişmezliğin, uygulama ayrıntılarını gizleme ihtiyacını sihirli bir şekilde ortadan kaldırmadığını söylüyorum.
Doval

Yanıtlar:


37

FP'nin bunu amaçlayan ve C # OOP'un temel nedeni, FP'de odağın referanssal şeffaflık olduğu; yani, veri bir işleve giriyor ve veri çıkıyor, ancak orijinal veri değişmiyor.

C # OOP'ta, bir nesnenin yönetimini ona devrettiğiniz ve bu nedenle kendi içsellerini değiştirmesini istediğiniz bir sorumluluk delegasyonu kavramı vardır.

FP'de hiçbir zaman bir nesnedeki değerleri değiştirmek istemezsiniz, bu nedenle işlevlerinizin nesnenize gömülü olması bir anlam ifade etmez.

Ayrıca FP'de, fonksiyonlarınızın C # OOP'un izin verdiğinden çok daha genelleştirilmesine izin veren daha yüksek tür polimorfizmine sahipsiniz. Bu şekilde, herhangi biri için işe yarayan bir işlev yazabilirsiniz ave bu nedenle onu bir veri bloğuna gömmek mantıklı gelmez; o olur sıkıca çift yöntemi, böylece sadece o belirli çalışır türden bir a. Bunun gibi davranışlar C # OOP'ta gayet iyi ve yaygındır, çünkü işlevleri zaten genel olarak soyutlama yeteneğiniz yoktur, ancak FP'de bir tradeoff olur.

Anemik etki alanı modellerinde C # OOP'ta gördüğüm en büyük sorun, yinelenen kodla bitiyor olmanızdır, çünkü DTO x ve DTO x'e aktivite sağlayan 4 farklı fonksiyon vardır çünkü 4 farklı kişi diğer uygulamayı görmedi . Yöntemi doğrudan DTO x'e koyduğunuzda, bu 4 kişi f'nin uygulanmasını görür ve yeniden kullanır.

C # OOP'taki anemik veri modelleri yeniden kodlamayı engeller, ancak bu, FP'de durum böyle değildir, çünkü tek bir işlev, bir işlevden çok daha fazla senaryoda kullanılabildiğinden, bu işlev bir işlevden çok daha fazla senaryoda kullanılabildiğinden daha fazla kod kullanımı elde edersiniz. C # tek bir DTO için yazardım.


Yorumlarda da belirtildiği gibi , tür çıkarımı, FP'nin bu kadar önemli polimorfizme izin vermesinin faydalarından biridir ve özellikle bunu Algoritma W tipi çıkarımlı Hindley Milner tipi sisteme kadar takip edebilirsiniz ; C # OOP tipi sistemdeki bu tür bir sonuçtan kaçınılmıştır, çünkü kısıtlamaya dayalı çıkarım eklendiğinde derleme süresi, gerekli kapsamlı arama nedeniyle çok uzun olmaktadır, burada ayrıntıları: https://stackoverflow.com/questions/3968834/generics-why -cant-derleyici-infer-tipi-argümanlar-in-bu-durumda


F # 'daki hangi özellikler bu yeniden kullanılabilir kodları yazmayı kolaylaştırır? Neden C # kodunu yeniden kullanılamaz? (Bir arayüze ihtiyaç duymaksızın, belirli özelliklerle ilgili argümanlar alabileceğimi düşünüyorum; bunun anahtar bir şey olacağını tahmin ediyorum?)
Danny Tuppeny

7
@DannyTuppeny dürüstçe F # karşılaştırma için kötü bir örnektir, sadece biraz giyinik C #; tıpkı C # gibi değiştirilebilen bir zorunlu dildir, C # 'nın sahip olmadığı ancak çok fazla olmayan birkaç FP özelliğine sahiptir. FP'nin gerçekten nerede öne çıktığını görmek için haskell'e bakın ve bunun gibi şeyler ADT'lerin yanı sıra tür sınıfları nedeniyle daha da mümkün hale geldi
Jimmy Hoffa

@MattFenwick Açıkça C # 'ya atıfta bulunuyorum çünkü posterin sorduğu şey bu. Cevabım boyunca OOP'a başvurduğum yerde C # OOP demek istiyorum, açıklığa kavuşturmak için düzenleme yapacağım.
Jimmy Hoffa,

2
Bu tür yeniden kullanımlara izin veren işlevsel diller arasındaki ortak özellik dinamik veya çıkarımlı yazımdır. Dil iyi tanımlanmış veri türlerini kullanabilse de, tipik işlev, üzerinde gerçekleştirilen işlemler (diğer işlevler veya aritmetik) geçerli olduğu sürece, hangi verinin ne olduğu ile ilgilenmez. Bu aynı zamanda OO paradigmalarında da mevcuttur (Go, örneğin, bir nesnenin bir Ördek olmasına izin veren Uçan, Yüzen ve Kaybedebilen bir nesne olduğu için bir Ördek olarak izin veren dolaylı bir arayüz uygulamasına sahiptir), ancak hemen hemen işlevsel bir programlama şartıdır.
KeithS

4
@KeithS Haskell'de değere dayalı aşırı yüklenme ile, kalıp eşleştirme demek istediğinizi düşünüyorum. Haskell'in aynı desende birden fazla üst seviye fonksiyona sahip olması, farklı desenlerle anında 1 üst seviye fonksiyonu + bir desen eşleşmesine iniyor.
jozefg

6

Neden bir anemik etki alanı modeli C # / OOP'ta kötü kabul edilir, ancak F # / FP'de çok önemlidir?

Sorunuz, aldığınız cevapların faydasını sınırlayacak büyük bir soruna sahiptir: F # ve FP'nin benzer olduğunu varsayıyorsunuz / ima ediyorsunuz. FP sembolik terim yeniden yazma, dinamik ve statik de dahil olmak üzere çok sayıda dil ailesidir. Statik olarak yazılmış FP dilleri arasında bile OCaml ve SML'deki (F # 'da bulunmayan) yüksek dereceli modüller gibi etki alanı modellerini ifade etmek için birçok farklı teknoloji vardır. F #, bu işlevsel dillerden biridir, fakat özellikle yalın olması nedeniyle kayda değerdir ve özellikle de yüksek dereceli modüller veya daha yüksek türler sunmamaktadır.

Aslında, etki alanı modellerinin FP'de nasıl ifade edildiğini söylemeye başlayamadım. Buradaki diğer cevap özellikle Haskell'de nasıl yapıldığından bahseder ve Lisp (tüm FP dillerinin annesi), ML dil ailesi veya diğer işlevsel diller için geçerli değildir.

Neden veriyi / davranışını ayırma fikrini ödünç almıyoruz ve hatta kötü olduğunu düşünüyoruz?

Jenerikler, verileri ve davranışları ayırmanın bir yolu olarak düşünülebilir. Jenerikler, işlevsel ailelerin ML ailesinden gelen dillerin bir parçası değildir. Tabii ki C # 'nın jeneriği var. Bu yüzden C # 'nin veri ve davranış ayrımı fikrini yavaşça ödünç aldığı iddia edilebilir.

Tanımın OOP ile uyuşmaması basitçe,

OOP'un temelde farklı bir öncül temele dayandığını ve sonuç olarak veri ve davranışları ayırmanız için gereken araçları size vermediğine inanıyorum. Tüm pratik amaçlar için, ürüne ve veri tiplerine ihtiyacınız var ve bunlar için gönderi yapın. ML'de bu birleşme ve kayıt tipleri ve kalıp eşleştirme anlamına gelir.

Check out Buraya verdi örneği .

ya da C # 'nın kötü olmasının somut bir nedeni var mı, çünkü F #' da geçerli değil (ve aslında, tersine çevrilmiş)?

OOP'den C # 'ya atlamaya dikkat edin. C #, OOP hakkında diğer diller kadar puritanik bir yere yakın değildir. .NET Framework artık jenerik, statik yöntemler ve hatta lambdalarla doludur.

(Not: C # / F # 'daki, blog postasındaki herhangi bir görüşe katılmayacak kişiler yerine, neyin iyi / kötü olduğu fikrini değiştirebilecek farklılıklarla özellikle ilgileniyorum).

C # cinsinden sendika tipleri ve örüntü eşleşmesinin olmaması onu neredeyse imkansız kılar. Sahip olduğun tek şey bir çekiç olduğunda, her şey bir çiviye benziyor ...


2
... sahip olduğun tek şey bir çekiç olduğunda, OOP halkı çekiç fabrikaları yaratacaktır; veri ve davranışların birbirinden tamamen soyutlanmasına izin vermek için C # 'nın eksikliğinin temelini bulmak için P +1: Birlik türleri ve kalıp eşleştirme.
Jimmy Hoffa,

-4

Bir iş uygulamasında sık sık veri gizlemek istemediğinizi düşünüyorum çünkü değişken değerlerde desen eşleştirmesi olası tüm durumları ele almanızı sağlamak için mükemmeldir. Ancak, karmaşık algoritmalar veya veri yapıları kullanıyorsanız, ADT'leri (cebirsel veri türleri) ADT'lere (soyut veri türleri) dönüştüren uygulama ayrıntılarını daha iyi gizlersiniz.


4
Bunun nesneye yönelik programlamaya karşı işlevsel programlamaya nasıl uygulandığını biraz daha açıklayabilir misiniz?
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.