Bir API sadece kod bakarak ne yaptığını her zaman bilmek gerekir?


20

Son zamanlarda kendi API'mı geliştiriyorum ve API tasarımına bu kadar ilgi duyduğumda, API tasarımımı nasıl geliştirebileceğimi çok merak ettim.

Birkaç kez ortaya çıkan bir yönü (API'mın kullanıcıları tarafından değil, konu hakkındaki gözlemli tartışmamda): API'yı ne yaptığını çağıran koda bakarak bilmelisiniz .

Örneğin , söylem repo için GitHub'daki bu tartışmaya bakın , şuna benzer:

foo.update_pinned(true, true);

Sadece koda bakarak (parametre adlarını, dokümantasyonu vb. Bilmeden) ne yapacağını tahmin edemez - 2. argüman ne demektir? Önerilen iyileştirme şuna benzer:

foo.pin()
foo.unpin()
foo.pin_globally()

Ve bu bir şeyleri temizler (2. arg, küresel olarak foo'nun sabitlenip sabitlenmeyeceği idi, sanırım) ve bu durumda daha sonra kesinlikle bir gelişme olacağına katılıyorum.

Ancak, sadece koda bakarak ne yaptığını bilmeseniz bile , farklı ancak mantıksal olarak ilgili durumu ayarlamak için yöntemlerin ayrı olanlardan ziyade bir yöntem çağrısı olarak daha iyi açığa çıkabileceği durumlar olabileceğine inanıyorum . (Bu yüzden parametre adlarına ve belgelerine bakmak için başvurmanız gerekir - bir API'ya aşina olmadığım zaman ne kişisel olarak her zaman yapacağım).

Örneğin SetVisibility(bool, string, bool)bir FalconPeer üzerinde bir yöntem maruz ve sadece hatta bakarak kabul:

falconPeer.SetVisibility(true, "aerw3", true);

Ne yaptığı hakkında hiçbir fikriniz olmaz. falconPeerMantıksal anlamda "görünürlüğünü" kontrol eden 3 farklı değer ayarlıyor : birleştirme isteklerini kabul et, sadece şifre ile ve keşif isteklerine cevap ver. Bunu 3 yöntem çağrısına bölmek, API'nın bir kullanıcısına "görünürlük" ün bir yönünü ayarlamaya ve diğerlerini "görünürlük" ün tüm yönlerini ayarlamak için tek bir yöntemi açığa çıkararak onları düşünmeye zorladığımı ayarlamayı unutmasına neden olabilir . Ayrıca, kullanıcı bir yönü değiştirmek istediğinde neredeyse her zaman başka bir yönü değiştirmek isteyecektir ve şimdi bunu tek bir çağrıda yapabilir.


13
Bu, bazı dillerin parametreleri adlandırdığı nedendir (bazen adlandırılmış parametreler bile zorunludur ). Örneğin bu grubun basit ayarların bir sürü olabilir updateyöntemle: foo.update(pinned=true, globally=true). Veya: foo.update_pinned(true, globally=true). Bu nedenle, sorunuzun cevabı dil özelliklerini de dikkate almalıdır, çünkü X dili için iyi bir API Y ve viceversa dili için iyi olmayabilir.
Bakuriu

Kabul etti - booleans kullanmayı bırakalım :)
Ven


@Bakuriu Kılıkta tamsayı olsalar bile C bile numaralandırır. Booleans'in iyi API tasarımı olduğu gerçek bir dünya dili olduğunu düşünmüyorum.
Doval

1
@Yapma Söylediğin şeyi anlamıyorum. Bu durumda boolean kullandım, çünkü OP onları kullandı, ama benim açımdan geçen değerle tamamen ilgisiz. Örneğin: veya setSize(10, 20)okunabilir değil . Zorunlu adlandırılmış parametrelere sahip dillerde booleans, enums / adlandırılmış sabitleri kullanmakla aynı miktarda bilgi iletebilir, böylece API'larda iyi olabilirler . setSize(width=10, height=20)random(distribution='gaussian', mean=0.5, deviation=1)
Bakuriu

Yanıtlar:


27

Bunu üç yöntem çağrısına bölmeme arzunuz tamamen anlaşılabilir, ancak boolean parametrelerinin yanı sıra başka seçenekleriniz de var.

Numaralandırma kullanabilirsiniz:

falconPeer.SetVisibility(JoinRequestOptions.Accept, "aerw3", DiscoveryRequestOptions.Reply);

Veya bayraklar numaralandırılır (eğer diliniz destekliyorsa):

falconPeer.SetVisibility(VisibilityOptions.AcceptJoinRequests | VisibilityOptions.ReplyToDiscoveryRequests, "aerw3");

Veya bir Parametre Nesnesi kullanabilirsiniz :

var options = new VisibilityOptions();
options.AcceptJoinRequests = true;
options.ReplyToDiscoveryRequest = true;
options.Password="aerw3";
falconPeer.SetVisibility(options);

Parametre Nesnesi deseni size yardımcı bulabileceğiniz başka avantajlar sağlar. Bir dizi parametrenin etrafından geçilmesini ve serileştirilmesini kolaylaştırır ve belirtilmemiş parametrelere kolayca "varsayılan" değerler verebilirsiniz.

Veya sadece boole parametrelerini kullanabilirsiniz. Microsoft her zaman .NET Framework API ile yapıyor gibi görünüyor, bu yüzden sadece omuz silkebilir ve "onlar için yeterince iyi ise, benim için yeterince iyi" diyebilirsiniz.


Parametre Nesnesi kalıbında hala OP'nin şöyle bir sorunu vardır: " Bunu 3 yöntem çağrısına bölmek," görünürlüğün "bir yönünü başkalarını ayarlamayı unutmak için API kullanıcısına yol açabilir .
Lode

Doğru, ama durumu daha iyi hale getirdiğini düşünüyorum (mükemmel değilse). Kullanıcı bir VisibilityOptions nesnesini başlatmak ve iletmek zorunda kalırsa, (herhangi bir şansla) VisibilityOptions nesnesinde ayarlamak isteyebilecekleri başka özellikler olduğunu hatırlatabilir. Üç yöntem çağrısı yaklaşımı ile, onlara hatırlatmaları gereken tek şey, aradıkları yöntemler hakkında yorumlar.
BenM

6

Açıkçası, kuralın her zaman istisnaları vardır, ancak kendiniz iyi açıkladığınız gibi, okunabilir bir API'ye sahip olmanın bazı avantajları vardır. Boole argümanları özellikle rahatsız edicidir, çünkü 1) bir niyet ortaya koymazlar ve 2) aslında iki olmanız gereken bir işlevi çağırdığınızı ima ederler, çünkü boole bayrağına bağlı olarak farklı şeyler olacak.

Temel soru şudur: API'nizi daha okunabilir hale getirmek için ne kadar çaba harcamak istiyorsunuz? Dışa doğru ne kadar dışa dönük olursa, daha fazla çaba kolaylıkla haklı görülebilir. Yalnızca başka bir birim tarafından kullanılan bir API ise, o kadar büyük bir anlaşma değildir. Tüm dünyanın kaybetmesine izin vermeyi planladığınız bir REST API'sinden bahsediyorsanız, onu daha anlaşılır hale getirmek için biraz daha çaba harcayabilirsiniz.

Örneğinize gelince, basit bir çözüm var: Görünüşe göre, sizin durumunuzda görünürlük sadece doğru veya yanlış bir şey değil. Bunun yerine, "görünürlük" olarak düşündüğünüz bir dizi şey var. Bir çözüm, Visibilitytüm bu farklı görünürlük türlerini kapsayan bir sınıf gibi bir şey tanıtmak olabilir . Bunları oluşturmak için Oluşturucu desenini daha da uygularsanız, aşağıdaki gibi bir kodla karşılaşabilirsiniz:

Visibility visibility = Visibility.builder()
  .acceptJoinRequests()
  .withPassword("aerw3")
  .replyToDiscoveryRequests()
  .build();
falconPeer.setVisibility(visibility);
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.