SORU
8 Mart 2014, CUMARTESİ


F uygulama mimarisi/kompozisyon#

KATI C yapıyorum# bir noktada fark ettim de son zamanlarda oldukça aşırı bir düzeye aslında çok fazla bir şey günümüzde işlevleri beste daha yapıyorum. Ve sonra geçenlerde aramaya başladım F# yine düşündüm, o olurdu muhtemelen çok daha uygun bir seçim dili için çok an yapıyorum, çok isterim denemek ve bir bağlantı noktası gerçek dünya C# projesi, F# gibi bir kanıt kavramı. Sanırım elimden çekip gerçek kod (bir çok un-deyimsel moda), ama ben hayal bile edemiyorum ne bir mimari gibi görünecektir. bana işlerinde benzer esnek moda olarak C#.

Bu ne demek, küçük sınıflar, bir sürü ve bir IoC konteyner kullanıyorum oluşturan arayüzleri var ve ben de Dekoratör ve Kompozit gibi desenler çok kullanırım. Bu bana kolayca veya uygulama herhangi bir noktada işlevselliğini değiştirmek genişletmek için izin veren (bence) çok esnek ve evolvable genel bir mimarisi olur. Gerekli değişim şekline bağlı olarak, tek bir arayüz yeni bir uygulama yazmak, IoC kayıt olarak değiştirin ve yapılması gerekebilir. Eğer değişim daha büyük değilse bile, uygulamanın geri kalanı daha önce yaptığı gibi sadece duruyor iken nesne grafiği parçaları değiştirmek istiyorum.

Şimdi F# yok sınıflar ve arayüzler (biliyorum, ancak sanırım ama konumuz o değil ne zaman yapmak istiyorum gerçek fonksiyonel programlama) yok yapıcı enjeksiyon, ve ben yok IoC kaplar. Dekoratör desen yüksek mertebeden fonksiyonlar kullanmak gibi bir şey yapabileceğimi biliyorum, ama bu beni yeterince esneklik ve yapıcı enjeksiyon ile sınıflar olarak idame aynı tür vermek için görünmüyor.

Bu düşünün C# türleri:

public class Dings
{
    public string Lol { get; set; }

    public string Rofl { get; set; }
}

public interface IGetStuff
{
    IEnumerable<Dings> For(Guid id);
}

public class AsdFilteringGetStuff : IGetStuff
{
    private readonly IGetStuff _innerGetStuff;

    public AsdFilteringGetStuff(IGetStuff innerGetStuff)
    {
        this._innerGetStuff = innerGetStuff;
    }

    public IEnumerable<Dings> For(Guid id)
    {
        return this._innerGetStuff.For(id).Where(d => d.Lol == "asd");
    }
}

public class GeneratingGetStuff : IGetStuff
{
    public IEnumerable<Dings> For(Guid id)
    {
        IEnumerable<Dings> dingse;

        // somehow knows how to create correct dingse for the ID

        return dingse;
    }
}

Benim IoC arayüzü ile kendi bağımlılık IGetStuff GeneratingGetStuff AsdFilteringGetStuff gidermek için konteyner söyleyeyim. Şimdi farklı bir filtre ihtiyacım var, yoksa filtreyi Kaldır tamamen, IGetStuff ilgili uygulanması gereken ve sadece IoC kayıt değiştirin. Arayüzü aynı kaldığı sürece, dokunmam gerek yokiçindeuygulama. OCP ve LSP, DİP tarafından etkin.

Şimdi ben F ne yapıyorsun#?

type Dings (lol, rofl) =
    member x.Lol = lol
    member x.Rofl = rofl

let GenerateDingse id =
    // create list

let AsdFilteredDingse id =
    GenerateDingse id |> List.filter (fun x -> x.Lol = "asd")

Bu ne kadar seviyorum, ama esneklik kaybettim. Evet, ben çağrı AsdFilteredDingse GenerateDingse aynı yer, çünkü bu tür aynı ama nasıl karar veren bir çağrı olmadan sabit kodlama görüşme sitesi? Bu iki işlevi değiştirilebilir olsa da, artık bu işlevini değiştirmeden AsdFilteredDingse içinde jeneratör işlevi de yerine geçemez. Bu hiç hoş değil.

Bir sonraki girişimi:

let GenerateDingse id =
    // create list

let AsdFilteredDingse (generator : System.Guid -> Dings list) id =
    generator id |> List.filter (fun x -> x.Lol = "asd")

Şimdi composability yüksek dereceden bir fonksiyon AsdFilteredDingse yaparak var, ama iki işlevi değiştirilebilir değildir artık. Düşündüm de, onlar zaten olmamalı.

Başka ne yapabilirdim ki? Bu taklit edebilirim "kompozisyon kök" kavramı benim C# F son dosya SAĞLAM# proje. Çoğu dosya sadece koleksiyonları fonksiyonları, o zaman ben bir çeşit "kayıt defteri" hangi değiştirir IoC konteyner, ve nihayet orada bir fonksiyon I aramak için aslında uygulama Çalıştır ve kullanan işlevleri "kayıt defteri". ""Yazın bir fonksiyon ihtiyacım var biliyorum (Guıd ->kayıt defterinde Söylerim Dings listesi), GetDingseForId Ara. Bu dediğim, asla tek işlevleri daha önce tanımlanmış.

Dekoratör için, tanımı olur

let GetDingseForId id = AsdFilteredDingse GenerateDingse

Filtreyi kaldırmak için, bunu değiştirmek istiyorum

let GetDingseForId id = GenerateDingse

Olumsuz(?) bu diğer fonksiyonlar kullanan tüm işlevleri makul yüksek mertebeden fonksiyonlar olması ve benim "" göster . olurdu defteri ^strong>tümgerçek işlevleri, önceden belirlenmiş herhangi bir fonksiyon daha sonra tanımlanan arayamaz çünkü kullandığım fonksiyonları, özellikle bu"". kayıt değil Ayrıca "kayıt" eşleştirmeleri. döngüsel bağımlılık sorunları ile aday olabileceğini

Bu mantıklı mı? Gerçekten nasıl bir F oluşturursun# ve (bahsetmiyorum bile test edilebilir) sürdürülebilir evolvable? olmak için uygulama

CEVAP
8 Mart 2014, CUMARTESİ


Bu Nesne Yönelimli çok yakından Enjeksiyon karşılık İşlevsel Yapıcı olduğunu fark basittirKısmi Fonksiyon Uygulama.

İlk olarak, bir kayıt türü olarak Dings yazmak istiyorum:

type Dings = { Lol : string; Rofl : string }

F# IGetStuff arabirim imza ile tek bir işlev için azaltılabilir

Guid -> seq<Dings>

Biristemcibu fonksiyonu kullanarak bir parametre olarak kabul eder:

let Client getStuff =
    getStuff(Guid("055E7FF1-2919-4246-876E-1DA71980BE9C")) |> Seq.toList

Client işlevin imzası

(Guid -> #seq<'b>) -> 'b list

Gördüğünüz gibi, hedef imza bir fonksiyonu olarak girdi alır ve bir liste verir.

Jeneratör

Jeneratör işlevi yazmak kolay

let GenerateDingse id =
    seq {
        yield { Lol = "Ha!"; Rofl = "Ha ha ha!" }
        yield { Lol = "Ho!"; Rofl = "Ho ho ho!" }
        yield { Lol = "asd"; Rofl = "ASD" } }

GenerateDingse işlevi bu imza vardır:

'a -> seq<Dings>

Bu aslındadaha fazlaGuid -> seq<Dings> ama bu bir sorun değil daha genel. Eğer sadece GenerateDingse ile Client oluşturmak istiyorsanız sadece bu gibi kullanabilirsiniz:

let result = Client GenerateDingse

GenerateDingse 50 *tüm üç değer döndürür.

Dekoratör

Orijinal Dekoratör biraz daha zor, ama çok değil. Yapıcı bir argüman olarak (iç) Dekore edilmiş bu tür eklemek yerine, genel olarak, sadece bir işlev için bir parametre değeri olarak ekleyin:

let AdsFilteredDingse id s = s |> Seq.filter (fun d -> d.Lol = "asd")

Bu işlev, bu imza vardır:

'a -> seq<Dings> -> seq<Dings>

Bizim istediğimiz bu değildi, ama kolay GenerateDingse ile oluşturmak için:

let composed id = GenerateDingse id |> AdsFilteredDingse id

composed işlev imzası vardır

'a -> seq<Dings>

Sadece biz arıyoruz!

Şimdi böyle composed 58 *kullanabilirsiniz:

let result = Client composed

sadece [{Lol = "asd"; Rofl = "ASD";}] dönecektir.

Değilsinvarcomposed işlev yapmak için; ayrıca bu noktada oluşturabilirsiniz:

let result = Client (fun id -> GenerateDingse id |> AdsFilteredDingse id)

Bu da [{Lol = "asd"; Rofl = "ASD";}] döndürür.

Alternatif Dekoratör

Önceki örnekte iyi çalışıyor, ama değilgerçektenBenzer bir işlev süsleyin. İşte bir alternatif:

let AdsFilteredDingse id f = f id |> Seq.filter (fun d -> d.Lol = "asd")

Bu işlev imzası vardır:

'a -> ('a -> #seq<Dings>) -> seq<Dings>

Gördüğünüz gibi, f savı daha yakından Dekoratör desen benzer yani aynı imzaya sahip başka bir işlevdir. Bu şekilde oluşturabilirsiniz:

let composed id = GenerateDingse |> AdsFilteredDingse id

Yine, bu gibi composed 69 *kullanabilirsiniz:

let result = Client composed

ya böyle içi:

let result = Client (fun id -> GenerateDingse |> AdsFilteredDingse id)

F ile tüm uygulamaları oluşturmak için daha fazla örnek ve ilkeleri#, my on-line course on Functional architecture with F# bkz.

Fonksiyonel Programlamaya onlar göster nasıl Nesne Yönelimli İlkeler hakkında daha fazla bilgi için, my blog post on the SOLID principles and how they apply to FP bkz.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Dion Coulls

    Dion Coulls

    16 AĞUSTOS 2006
  • How To Cook That

    How To Cook

    16 NİSAN 2011
  • Toddler Fun Learning

    Toddler Fun

    8 ŞUBAT 2013