SOLID ilkelerine başvurmak - Tek Sorumluluk, Açık / Kısa, Liskov Altung, Interface Segregation ve Bağımlılık Invers – nesne odaklı programlama (OOP) yaygın olarak kullanılabilir ve ölçeklenebilir bir yazılım için en iyi uygulama olarak kabul edilir. Ancak, geliştiriciler OOP kökenlerini işlevsel bir bağlamda çevirmeye çalışır.

Bu makale derinlikteki bu zorlukları keşfeder ve OOP meslektaşları olarak esnek olan fonksiyonel kodbazlara adapte olmak için pratik stratejiler sunar.SağID ve FP arasındaki gerginlikleri ve sinerjikleri anlamakla, sadece modüler, test edilebilir ve esnek olarak yazabilirsiniz - ait olmayan nesne odaklı kalıpları.

SOLID İlkelerini Context

Zorluklara dalmadan önce, her SOLID prensibinin OOP'da ne yapmayı amaçladığını hatırlamak faydalı olacaktır:

  • [FONT=0) Tek Sorumluluk Prensi (SRP))[Dönetici: Bir sınıf sadece bir sorumluluğun yaratılması için bir nedene sahip olmalıdır.
  • [FONT:0) Açık / Kısa Prensip (OCP))[değiştir | kaynağı değiştirilmelidir.
  • [FONT:0)Liskov Substitution Prensliği (LSP))[tr|Sekiz]; Alttipler programın doğruliğini değiştirmeden temel türlerine karşı altüst olmalıdır.
  • [FONT=0) Interface Segregation Prens (ISP))[değiştirmedikleri arayüzlere bağlı olmak zorunda kalmamalıdır. Bu, iyi hazırlanmış, rol özel arayüzlere yol açar.
  • [DIP)[0]Dependency Invers Prensipleri (DIP)[DIP)[DIP)[DIP: Yüksek seviyeli modüller düşük seviyeli modüllere bağlı olmamalıdır; her ikisi de soyutlamalara bağlı olmalıdır. Abstractions to abstractions.

OOP'da, bu ilkeler sınıflarla sıkı bir şekilde çiftleştirilmiştir, miras, arayüzler ve polimorfik davranışlar. Fonksiyonel programlama bu mekanizmaları işlevleri, cebirsel veri türleriyle değiştirir (ADTs), tip sınıflar (in Haskell) veya protokolleri (in Clokell) ve fonksiyon kompozisyonu uygular. Sonuç olarak, doğrudan doğru bir şekilde, doğru bir reçeteye yol açar.

Fonksiyonel Dillerde SOLID'in özel meydan okumaları

Tek Sorumluluk Prensi (SRP)

OOP'da, SRP genellikle sınıf seviyesinde uygulanır. Bir sınıf tek, iyi tanımlanmış bir endişe ve bir kohesive yöntem kümesine sahiptir. Fonksiyonel diller, dekompozisyon ünitesi genellikle küçük ve saf, doğal olarak SRP ile uyumludur. Ancak, işlevlerin daha büyük bir iş akışlarına oluştuğunda zorluk ortaya çıkabilir.Tek bir çalışma birden çok sorumluluk alabilir - veri toplama, dönüştürme ve bir günlük yazma gibi - bir sınır olmadan - bunu bir sınırla- uzlaşarak.

Örneğin, boru sözcülüğü gibi işlevsel bir boru hattında:0) (örneğin boru sözcülüğü) her adım saf bir işlevdir. Ancak boru hattının kendisi, boru hattının belirsizliği ile ilgili olarak, bağlantıların tek bir sorumluluğu vardır: her bir fonksiyonun kendi başına olması gerekir mi? Uzun boru hatları veya birçok argümanın SRP ihlal edebileceği işlevleridir.

Açık / Kısa Prensip (OCP)

OOP'da OCP genellikle alt sınıf tarafından uygulanır: bir temel sınıf yaratırsınız ve temelleri değiştirmeden genişletirsiniz. FP'de, daha yüksek sipariş fonksiyonları, parametrik polimorizm veya açık miktarlar ile davranış genişletilebilir.

Örneğin, Haskell'de, yeni bir uygulama oluşturmak için tip sınıfları kullanabilirsiniz. Yeni bir yöntem eklemek, OCP'yi ihlal eden herhangi bir tür üzerinde polimorfik yapılabilir, yeni uygulamaları tanımlamadan eklemeye izin verir. Ancak, bu dikkatli bir tasarım gerektirir.

Temel zorluk, FP'nin eskiliğe yaklaşımının mirastan daha az reklam-hoc olması; başlangıçtan itibaren açık soyutlamalar talep ediyor. Tersine, miras yeni bir alt sınıf tanıtarak geri alınabilir. FP, retrofiting data types or functions.

Liskov Altung Prensliği (LSP)

LSP, OOP'da, bir temel sınıf GÜNCÜT:2) bir yöntem ile belirlenen davranışları ve alt sınıfANSDÜ:3) uçamayan, altüstleştirme işlemine sahipseniz, alt tipler programdan beklenen davranışları korur.

Fonksiyonel diller nadiren OOP anlamda altüstpinge sahiptir. Bunun yerine, Haskell'in parametrik polimorfizme güvenebilirler, bir tür doğrulayıcı veya uygun olmayan bir uygulama sağlar. LSP, örneğin, kapalı sözleşmeyi (örneğin, Haskell'in yasasını bekleyen bir işlev) geçerli girişlere göre kimlik olmalıdır.

Sorun LSP ihlallerinin FP'de tespit edilmesi daha zor olabilir çünkü gerçek davranışı (örneğin, sipariş veren) tahmin etmek mümkün değildir.

Interface Segregation Principles (ISP)

ISS küçük, odaklanmış arabirimleri teşvik eder. OOP'da, aramalarla büyük bir arayüz kırıyorsunuz, böylece müşteriler sadece ihtiyaç duydukları şeye bağlı. FP'de, bir arayüz eşdeğer bir işlev imzası veya fonksiyonların kaydıdır (örneğin, Elixir'in geri çağırmaları ile ilgili bir yöntem sözlüğü).

Sorun şu ki, FP genellikle çeşitli yeteneklere bağlı olarak, sadece bir tane "saç arabirimlerine" benzeyen çok basit bir tür kullanıyor. Örneğin, bir işlevden oluşan bir işlev, küçük kayıtları veya sınıfları bilinçli olarak tasarlayabilir.In languages like Scala, Cake Pattern or implicit classes can be used, but they add complex.

Başka bir sorun: FP, mevcut tip sınıfları kullanarak, bir bağlam ihlal ettiği için teşvik eder: işlev, 03: 14) ve [[FONT: 16) Bu durumda, ancak en az sınıf kısıtlaması (örneğin, [[ŞUygunlukT: 16) için bir bağlantıya sahip değildir.

Bağlanma Prensipleri (DIP)

DIP hem üst düzey hem de düşük seviyeli modüllerin soyutlamaya bağlı olması gerektiğini belirtir, beton uygulamalarına bağlı değildir: Yüksek seviyeli işlev, bağımlılıklara bağlı olarak otomatik veya soyut sınıflar kullanır; genellikle işlev parametreleri veya bir yapılandırma kaydı olarak geçer.Bu genellikle "işlevler aracılığıyla enjeksiyonency" olarak adlandırılır ve doğal olarak inversiyonel olarak sonuçlanır: üst düzey işlev, bağımlılıklarını anlık olarak almaz.

Örneğin, süreçlerin siparişlerinin bir argüman olarak işlev alabileceği bir işlev. caller bir veritabanı veya hafıza mağazası kullanmaya karar verir. Bu zaten DIP ile uyumlu. Ancak, bağımlılık grafiği karmaşık olduğunda sorunlar ortaya çıkar.OOP, bağımlılık çerçevesi (şarı gibi) otomatik olarak kablolamayı başarır. FP, arama zincirine veya bir okuyucuyu kullanarak veya bir okuyucuyu kullanarak bağlantıya bağlı tutmanız gerekir.

Başka bir nuance: saf fonksiyonlar yan etkilere sahip olamaz, bu yüzden yan etkileri üreten bağımlılıklar (örneğin veritabanı çağrıları) bir etki türünde kapatılmalıdır. Bu güçler DIP için iyi bir şey olan bağımlılığın açık bir temsilidir, bu da soyutlama etki türüdür.

SOLID'i Fonksiyonel Programlamaya Adaptasyonlar

OOP tarzı SOLID'yi FP'ye zorlamak yerine, ilkeleri içselleştirip FP-natif kavramlar yoluyla ifade etmeyi deneyimledi. Aşağıdaki stratejiler büyük ölçekli fonksiyonel kodbases'te etkili oldu.

Embrace Pure Functions ve Clear Data Flow

SRP, her işlevin tam olarak bir şey yaptığında doğal olarak memnundur: giriş verileri yan etkiler olmadan veriye dönüştürür. Monolithic pipelines, break down transforms into separate name functions.Use Module (e.g.,ENFLT:23)

Örneğin, bir dosyayı okuyan bir işlev yerine, pars JSON'u uygular ve bunu doğrular, ayrı saf işlevleri vardır - [DFLT:25] (gösterme, IO/Effect)) [[Çalışkanlık|)|evcutFLT:27) (kıt) - ve onları tek bir orkestrasyon işlevinde oluşturur.

OCP ve LSP için Tip Sisteminin Kullanımı

Algebraic veri türleriyle model uyumu ile kombine edildiğinde OCP'ye ulaşabilir.Bir türe yeni bir değişken eklerken, tüm desenleri güncellemeniz için derleyici güçler.Bu, OCP'nin tam tersi - bir değişiklik gerektirir - bu da açık veri türlerini kullanmak daha iyidir (örneğin Haskell) veya protokolleri belirtildiği gibi.For OCP için, eski kullanım türlerinden kaçınır.

LSP, yasaları ve mülk tabanlı testler yoluyla uygulanabilir. Tanımladığınız her sınıf için, yasaları belirtmelisiniz (örneğin, associativity) ve onları otomatik olarak QuickCheck veya ScalaCheck gibi araçları kullanarak test etmelisiniz. Bu, herhangi bir yeni örnek, değişmezleri kırmadan altüst olur.

ISS için Yüksek Lisans Fonksiyonlar ve Kompozisyon kullanın

Büyük bir işlev kaydı geçmek yerine, ihtiyacınız olan işlevleri tam olarak geçmek. Bu, ISS'nin özüdür: işlevlerin küçük parametre listeleri olması gerekir.Bir işlev iki farklı işleme ihtiyaç duyarsa, iki ayrı fonksiyon argümanını almalı, her iki tipte bir nesneye sahip olmamalıdır.In typed FP dilleri, her yerde yaymak için küçük bir tür takmaları tanımlayabilirsiniz.

Örneğin, Scala'da, yerine:

def process(config: Config): Result // Config has many fields

tercih:

def process(database: String => IO[Data], logger: String => IO[Unit]): IO[Result]

Bu, gerçek bağımlılıkları açık ve ayrıştırır.

Açıklama : DIP için Parametreler ile Bağlanmaya bağlı

FP'deki en basit DIP formu, tüm yetersiz veya dış bağımlılıkları işlev argümanları olarak açık hale getirmektir.Bu, temel olarak mükemmel bir şekilde uyumludur, çünkü yüksek seviyeli mantık, soyutlamalara bağlıdır (işe imzaları) ve çağrıcı beton uygulamaları sağlar. Daha karmaşık bağımlılık grafiği için, bir Okuyucu modeli (in Haskell:FLT:31) veya ZIO gibi bir etki sistemi, bağlılık için yerleşik bir ortam tipine sahiptir.

Örneğin, ZIO'da bir veritabanı hizmetine ihtiyaç duyan bir işlev ve bir giriş servisinin etkisi tip GÜNCELDER'e sahip olabilir. Bağımlılıklar bunları açık ve runtime çözmektedir. Bu, DIP'in temiz, güvenli bir uygulamasıdır.

FP'de SOLID'i Kabul Etmek için Pratik İpuçları

  • [FONT:0] Tasarım açık bir giriş / ⁇ sözleşme ile işlev görür . Tartışmalarını veya küresel duruma güvenen işlevleri kaçının. Bu doğrudan SRP'yi destekler ve daha fazla yatırım yapar.
  • [FONT:0)Favor küçük, kohesive modüller büyük olanları üzerinde[Dönetici:0) Her modülün tek bir amaç hizmet eden bir dizi işlevi ihraç etmesi gerekir. Bu, SRP modülü seviyesinde uygulanır.
  • [FONT:0) Bu tür sınıflar için yasaları tanımlar ve LSP'yi tatmin etmeyi test edin.
  • [FONT:0) En genel sınıf kısıtlamasını ([Dönetici: 1 ), bir işlev sadece [[FONTD:33) talep ederse, [[ŞUYGÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞ
  • [FONT=0)Pass, parametrelerin ([Döneticiler) olarak bağlı olarak, onları zorlaştırmak yerine, karmaşık uygulamalar için bir okuyucu etkisi veya akanlık kütüphanesini [DÜDÜDÜ: 2)ZIO) veya [[FLT:Katılım Etkisi).
  • [[Döneticileri:0) Gayrimenkul bazlı testler [Dönderlik kodun tüm uygulamalar için doğru davrandığını doğrulamak için[Döneticileri) kullanılır.
  • [FONT:0] OOP benzeri özellikleri ile bile dilbilimleri eksik, . Bunun yerine, kompozisyon ve yüksek sipariş fonksiyonlarını kullanmak, bu doğal olarak değiştirme için kapalı kod tutmak.
  • [FONT:0) Küçük yardımcı fonksiyonlarını çıkarmak içinRefaksiyon[Dönetici 1] birkaç çizginin ötesinde büyürken otomatik olarak SRP ile uyum sağlayacaktır.

Dış Kaynaklar

Daha fazla okuma için aşağıdaki yazar kaynakları düşünün:

  • [FONT=0)Wikipedia: SOLID İlkeleri) - orijinal OOP odaklı ilkelerin ayrıntılı bir bakış.
  • [FONT=0)Martin Fowler: Kontrol konteynerlerinin ve Bağımlılığı Enjeksiyonu ) - DIP ve DI'daki klasik makale, her iki paradigmaya uygulanabilir.
  • [FONT:0)Haskell 2010 Dil Raporu: Tip Sınıflar) - Sınıfların reklam-hoc polimorphism ve OCP-dost tasarımı nasıl etkinleştirilebileceğine dair ayrıntılar.
  • [FONT:0]Katallar Tip Sınıflar[Dönemli Scala'nın ISS-like granularity elde etmek için nasıl iyi hazırlanmış tip sınıfları kullandığı örnekler.
  • [FONT:0]Clojure protokolleri[[Döntilmiş: 1) Dinamik bir FP dilinde miras olmadan açık / kapalı bir genişleme gösterir.

Sonuç Sonuç Sonuç Sonuç Sonuç Sonuç Sonuç Sonuç

SOLID ilkelerine fonksiyonel programlama dilleri, OOP modellerini FP sözlüğe aktarmakla ilgili değildir. Bunun yerine, her ilkenin arkasındaki hedefleri daha derin bir anlayışa ihtiyaç duyar - esneklik, ve kullanılabilirlik - ve bu hedeflere ulaşan FP-natif mekanizmaları bulmak.

Bu makalede belirtilen zorluklar - SRP boru hatlarında belirsizlik gibi, OCP karmaşıklığı, yasaları aracılığıyla LSP uygulama, genel tip sınıflarla ISS ve DIP iş parçacığı olarak en iyi OOPential kodu olarak kullanılabilir ve aynı zamanda nesnelerden ziyade dönüşüm ve soyutlamalar açısından da düşünmek için istekli olun.