it-swarm.asia

Ne zaman C # akıcı gitmek için?

Birçok açıdan Akıcı arayüzler fikrinden gerçekten hoşlanıyorum, ancak C # 'ın tüm modern özellikleriyle (başlatıcılar, lambdalar, adlandırılmış parametreler) kendimi "buna değer mi?" Ve "Bu doğru model mi? kullanılır?". Kabul edilen bir uygulama olmasa bile, en azından Akıcı kalıbı ne zaman kullanacağım konusunda kendi deneyimlerini veya karar matrisini kimse verebilir mi?

Sonuç:

Şimdiye kadar verilen cevaplardan bazı temel kurallar:

  • Akıcı arayüzler, ayarlayıcılardan daha fazla eyleminiz olduğunda büyük ölçüde yardımcı olur, çünkü çağrılar bağlam geçişinden daha fazla yararlanır.
  • Akıcı arayüzler, tek kullanım aracı olarak değil, bir API'nin üstünde bir katman olarak düşünülmelidir.
  • Lambdalar, başlatıcılar ve adlandırılmış parametreler gibi modern özellikler, akıcı bir arayüzü daha kolay hale getirmek için el ele çalışabilir.

İşte daha az ihtiyaç duyulan modern özellikler ile ne demek istediğimin bir örneği. Örneğin, (örneğin kötü bir örnek) Akıcı bir arayüze benzer bir Çalışan yaratmamı sağlar:

Employees.CreateNew().WithFirstName("Peter")
                     .WithLastName("Gibbons")
                     .WithManager()
                          .WithFirstName("Bill")
                          .WithLastName("Lumbergh")
                          .WithTitle("Manager")
                          .WithDepartment("Y2K");

Başlatıcılar gibi kolayca yazılabilir:

Employees.Add(new Employee()
              {
                  FirstName = "Peter",
                  LastName = "Gibbons",
                  Manager = new Employee()
                            {
                                 FirstName = "Bill",
                                 LastName = "Lumbergh",
                                 Title = "Manager",
                                 Department = "Y2K"
                            }
              });

Bu örnekte yapıcılarda adlandırılmış parametreler de kullanabilirdim.

80
Andrew Hanlon

Akıcı bir arayüz yazmak (bununla birlikte dabbled ) daha fazla çaba gerektirir, ancak bir ödeme yapar çünkü doğru yaparsanız, sonuçtaki kullanıcı kodunun amacı daha açıktır. Temelde alana özgü bir dil biçimidir.

Başka bir deyişle, kodunuz yazıldığından çok daha fazla okunursa (ve hangi kod yazılmaz?), Akıcı bir arayüz oluşturmayı düşünmelisiniz.

Akıcı arayüzler bağlamla ilgilidir ve nesneleri yapılandırmanın yollarından çok daha fazlasıdır. Yukarıdaki bağlantıda da görebileceğiniz gibi, akıcı bir API kullandım:

  1. Bağlam (bu nedenle, genellikle aynı şeyle bir sırayla birçok işlem yaptığınızda, bağlamınızı defalarca bildirmek zorunda kalmadan eylemleri zincirleyebilirsiniz).
  2. Keşfedilebilirlik (objectA. sonra intellisense size birçok ipucu verir. Yukarıdaki durumumda, plm.Led. yerleşik LED'i kontrol etmek için tüm seçenekleri sunar ve plm.Network. size ağ arayüzü ile yapabileceğiniz şeyleri verir. plm.Network.X10. size X10 aygıtları için ağ eylemleri alt kümesini verir. Bunu yapıcı başlatıcıları ile elde edemezsiniz (deyimsel olmayan her farklı eylem türü için bir nesne oluşturmak istemiyorsanız).
  3. Yansıma (yukarıdaki örnekte kullanılmamaktadır) - LINQ ifadesinde başarılı olma ve onu manipüle etme yeteneği, özellikle birim testler için oluşturduğum bazı yardımcı API'larda çok güçlü bir araçtır. Bir özellik alıcı ifadesi geçmek, yararlı ifadeler bir sürü inşa, bunları derlemek ve çalıştırmak, hatta bağlamımı ayarlamak için özellik alıcı kullanın.

Genelde yaptığım bir şey:

test.Property(t => t.SomeProperty)
    .InitializedTo(string.Empty)
    .CantBeNull() // tries to set to null and Asserts ArgumentNullException
    .YaddaYadda();

Akıcı bir arayüz olmadan böyle bir şeyi nasıl yapabileceğinizi görmüyorum.

Düzenleme 2 : Ayrıca, gerçekten ilginç okunabilirlik geliştirmeleri de yapabilirsiniz, örneğin:

test.ListProperty(t => t.MyList)
    .ShouldHave(18).Items()
    .AndThenAfter(t => testAddingItemToList(t))
    .ShouldHave(19).Items();
28
Scott Whitlock

Scott Hanselman, Jonathan Carter'la podcast Hanselminutes'in 260. Bölümü 'de bunu anlatıyor. Akıcı bir arayüzün bir API'daki bir kullanıcı arayüzü gibi olduğunu açıklarlar. Tek erişim noktası olarak akıcı bir arayüz sağlamamalı, bunun yerine "normal API arayüzünün" üstünde bir çeşit kod kullanıcı arayüzü olarak sağlamalısınız.

Jonathan Carter ayrıca API tasarımı hakkında biraz konuşuyor blogunda .

24
Kristof Claes

Akıcı arayüzler, "doğru" muhakeme ile çalışmadığınızda, kodunuzun bağlamında sağlamak için çok güçlü özelliklerdir.

Amacınız bir çeşit sahte kara kutu olarak tek satırlık büyük kod zincirleri oluşturmaksa, muhtemelen yanlış ağacı havlıyorsunuz demektir. Öte yandan, yöntem çağrılarını zincirlemek ve kod okunabilirliğini artırmak için bir araç sağlayarak API arayüzünüze değer katmak için kullanıyorsanız, o zaman çok iyi planlama ve çaba ile çabaya değer olduğunu düşünüyorum.

Akıcı arayüzler oluştururken, bağlamının potansiyel olarak iyi bir API arayüzünü ve bu nedenle içsel değerini çaldığı için akıcı yöntemlerinizi "-something" ile "adlandıracağınız" akıcı arayüzler oluştururken, ortak bir "model" gibi görünen şeyleri takip etmekten kaçınırım. .

Anahtar, akıcı sözdizimini Etki Alanına özgü bir dilin özel bir uygulaması olarak düşünmektir. Neden bahsettiğimin gerçekten iyi bir örneği olarak, akıcılığı bir DSL'yi çok değerli ve esnek bir şekilde ifade etmenin bir aracı olarak kullanan StoryQ'ya bir göz atın.

14
S.Robins

İlk not: Sorudaki bir varsayımla ilgili sorun yaşıyorum ve belirli sonuçlarımı çıkarıyorum (bu yazının sonunda) Bundan. Bu muhtemelen tam ve kapsayıcı bir cevap vermediğinden, bunu CW olarak işaretliyorum.

Employees.CreateNew().WithFirstName("Peter")…

Başlatıcılar gibi kolayca yazılabilir:

Employees.Add(new Employee() { FirstName = "Peter", … });

Gözlerimde, bu iki versiyonun farklı şeyler ifade etmesi ve yapması gerekiyor.

  • Akıcı olmayan sürümden farklı olarak, akıcı sürüm yeni Employee'nin de Add koleksiyonuna Employeesed olduğu gerçeğini gizler - yalnızca yeni bir nesnenin Created olduğunu gösterir.

  • ….WithX(…) 'nin anlamı belirsiz, özellikle nesne ifadeleri için with anahtar kelimesine sahip olan F #' dan gelen insanlar için : obj.WithX(x) öğesini olarak yorumlayabilirler. new nesnesi obj ile aynıdır, bu da obj değeri dışında X özelliği dışında x özelliği ile türetilmiştir. Diğer yandan, ikinci sürümde, türetilmiş hiçbir nesnenin oluşturulmadığı ve orijinal nesne için tüm özelliklerin belirtildiği açıktır.

….WithManager().With…
  • Bu ….With… İfadesinin başka bir anlamı daha vardır: özellik başlatmanın "odağı" nı farklı bir nesneye geçirmek. Akıcı API'nizin With için iki farklı anlamı olması, neler olduğunu doğru bir şekilde yorumlamayı zorlaştırıyor… belki de bu nedenle bu kodun amaçlanan anlamını göstermek için örneğinizdeki girinti kullandınız. Bu daha açık olurdu:

    (employee).WithManager(Managers.CreateNew().WithFirstName("Bill").…)
    //                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    //                     value of the `Manager` property appears inside the parentheses,
    //                     like with `WithName`, where the `Name` is also inside the parentheses 
    

Sonuçlar: Akıcı bir API (Ts.CreateNew().WithX(x)) ile yeterince basit bir dil özelliği olan new T { X = x } "Gizleniyor" belli ki yapılabilir, ama:

  1. Ortaya çıkan akıcı kod okuyucularının hala tam olarak ne yaptığını anlamalarına dikkat edilmelidir. Yani, akıcı API'nin anlamı şeffaf ve açık olmalıdır. Böyle bir API tasarlanması beklenenden daha fazla iş olabilir (kullanım kolaylığı ve kabul için test edilmesi gerekebilir) ve/veya…

  2. tasarlanması gerekenden daha fazla iş olabilir: Bu örnekte akıcı API, temel API (dil özelliği) üzerine çok az "kullanıcı konforu" ekler. Akıcı bir API'nin temeldeki API/dil özelliğini "kullanımını kolaylaştırması" gerektiğini söyleyebiliriz; yani programcıya büyük miktarda çaba sarf etmelidir. Aynı şeyi yazmanın başka bir yolu ise, muhtemelen buna değmez, çünkü programcının hayatını kolaylaştırmaz, ancak sadece tasarımcının çalışmasını zorlaştırır (bkz. Yukarıdaki 1. sonuç).

  3. Yukarıdaki her iki nokta da sessizce akıcı API'nin mevcut bir API veya dil özelliği üzerinde bir katman olduğunu varsayar. Bu varsayım başka bir iyi kılavuz olabilir: Akıcı bir API tek bir yol değil, bir şey yapmanın ekstra bir yolu olabilir. Yani, "kaydolma" seçeneği olarak akıcı bir API sunmak iyi bir fikir olabilir.

5
stakx

Akıcı tarzı seviyorum, niyetini çok açık bir şekilde ifade ediyor. Sonra sahip olduğunuz nesne initalizer örneğinde, sözdizimini kullanmak için genel özellik ayarlayıcılarına sahip olmanız gerekir, akıcı stille yapmazsınız. Bunu söyleyerek, örneğinizle, kamu belirleyicileri üzerinde fazla bir kazanç elde edemezsiniz, çünkü neredeyse Java esque bir set/get stili yöntemi kullanmaya başladınız.

Bu da beni ikinci noktaya getiriyor, akıcı stili sahip olduğunuz şekilde kullanacağımdan emin değilim, birçok özellik ayarlayıcıyla, bunun için muhtemelen ikinci sürümü kullanacağım, birlikte zincirlemek için çok sayıda fiil veya ayarlar yerine en az çok şey var.

2
Ian

akıcı arayüz terimine aşina değildim, ancak bana LINQ dahil kullandığım birkaç API'yi hatırlatıyor .

Şahsen C # 'ın modern özelliklerinin böyle bir yaklaşımın kullanışlılığını nasıl önleyeceğini görmüyorum. El ele gittiğini söylemeyi tercih ederim. Örneğin. zatma yöntemleri kullanarak böyle bir arayüze ulaşmak daha da kolaydır.

Belki de bahsettiğiniz modern özelliklerden birini kullanarak akıcı bir arayüzün nasıl değiştirilebileceğinin somut bir örneğiyle cevabınızı netleştirin.

1
Steven Jeuris