Haruskah seseorang selalu tahu apa yang dilakukan API hanya dengan melihat kode?

20

Baru-baru ini saya telah mengembangkan API saya sendiri dan dengan minat yang diinvestasikan dalam desain API, saya sangat tertarik bagaimana saya dapat meningkatkan desain API saya.

Salah satu aspek yang telah muncul beberapa kali adalah (bukan oleh pengguna API saya tetapi dalam diskusi saya mengamati tentang topik): orang harus tahu hanya dengan melihat kode memanggil API apa yang dilakukannya .

Sebagai contoh, lihat diskusi tentang GitHub ini untuk repo wacana , seperti:

foo.update_pinned(true, true);

Hanya dengan melihat kode (tanpa mengetahui nama parameter, dokumentasi, dll.) Orang tidak dapat menebak apa yang akan dilakukan - apa arti argumen ke-2? Perbaikan yang disarankan adalah memiliki sesuatu seperti:

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

Dan itu sudah beres (argumen ke-2 adalah apakah akan pin foo secara global, saya kira), dan saya setuju dalam kasus ini nanti akan menjadi perbaikan.

Namun saya percaya mungkin ada contoh di mana metode untuk mengatur keadaan yang berbeda tetapi terkait secara logis akan lebih baik diekspos sebagai satu metode panggilan daripada yang terpisah, meskipun Anda tidak akan tahu apa yang dilakukannya hanya dengan melihat kode . (Jadi Anda harus mencari untuk melihat nama parameter dan dokumentasi untuk mencari tahu - yang secara pribadi saya akan selalu lakukan tidak peduli bagaimana jika saya tidak terbiasa dengan API).

Sebagai contoh saya memaparkan satu metode SetVisibility(bool, string, bool)pada FalconPeer dan saya mengakui hanya melihat baris:

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

Anda tidak akan tahu apa yang dilakukannya. Ini menetapkan 3 nilai berbeda yang mengontrol "visibilitas" falconPeerdalam arti logis: menerima permintaan bergabung, hanya dengan kata sandi dan membalas permintaan penemuan. Membagi ini menjadi 3 metode panggilan dapat menyebabkan pengguna API untuk mengatur satu aspek "visibilitas" lupa untuk mengatur orang lain yang saya paksa mereka pikirkan dengan hanya memaparkan satu metode untuk mengatur semua aspek "visibilitas" . Lebih jauh lagi ketika pengguna ingin mengubah satu aspek mereka hampir selalu ingin mengubah aspek lain dan sekarang dapat melakukannya dalam satu panggilan.

markmnl
sumber
13
Inilah tepatnya alasan mengapa beberapa bahasa telah menamai parameter (kadang-kadang bahkan diberlakukan parameter bernama). Misalnya Anda bisa kelompok banyak pengaturan secara sederhana updatemetode: foo.update(pinned=true, globally=true). Atau: foo.update_pinned(true, globally=true). Jadi, jawaban untuk pertanyaan Anda juga harus mempertimbangkan fitur bahasa, karena API yang baik untuk bahasa X mungkin tidak baik untuk bahasa Y dan sebaliknya.
Bakuriu
Setuju - mari kita berhenti menggunakan boolean :)
Ven
2
Ini dikenal sebagai "boolean trap"
user11153
@ Bakuriu Bahkan C memiliki enum, bahkan jika mereka bilangan bulat yang menyamar. Saya tidak berpikir ada bahasa dunia nyata di mana boolean adalah desain API yang baik.
Doval
1
@Doval Saya tidak mengerti apa yang Anda katakan. Saya menggunakan boolean dalam situasi itu hanya karena OP menggunakannya, tetapi poin saya sama sekali tidak terkait dengan nilai yang diteruskan. Misalnya: setSize(10, 20)tidak dapat dibaca seperti setSize(width=10, height=20)atau random(distribution='gaussian', mean=0.5, deviation=1). Dalam bahasa dengan parameter bernama yang dipaksakan, boolean dapat menyampaikan jumlah informasi yang persis sama dengan menggunakan enum / konstanta bernama, sehingga mereka bisa bagus di API.
Bakuriu

Jawaban:

27

Keinginan Anda untuk tidak membaginya menjadi tiga pemanggilan metode benar-benar dapat dipahami, tetapi Anda memang memiliki opsi lain selain parameter boolean.

Anda bisa menggunakan enum:

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

Atau bahkan flag enum (jika bahasa Anda mendukungnya):

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

Atau Anda dapat menggunakan Obyek Parameter :

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

Pola Objek Parameter memberi Anda beberapa keuntungan lain yang mungkin bermanfaat bagi Anda. Ini membuatnya mudah untuk dilewatkan dan membuat serial suatu set parameter, dan Anda dapat dengan mudah memberikan nilai parameter "default" yang tidak ditentukan.

Atau Anda bisa menggunakan parameter boolean. Microsoft tampaknya selalu melakukannya dengan .NET Framework API, jadi Anda bisa mengangkat bahu dan berkata "jika itu cukup baik untuk mereka, itu cukup baik untuk saya".

BenM
sumber
Pola Objek Parameter masih memiliki masalah OP menyatakan: " Membagi ini menjadi 3 metode panggilan dapat menyebabkan pengguna API untuk mengatur satu aspek" visibilitas "lupa mengatur yang lain ".
Lode
Benar, tetapi saya pikir itu membuat situasi lebih baik (jika tidak sempurna). Jika pengguna dipaksa untuk instantiate dan meneruskan dalam objek VisibilityOptions, itu mungkin (dengan sedikit keberuntungan) mengingatkan mereka bahwa ada properti lain pada objek VisibilityOptions yang mungkin ingin mereka atur. Dengan pendekatan tiga metode panggilan, yang harus mereka ingatkan adalah komentar tentang metode yang mereka panggil.
BenM
6

Jelas, selalu ada pengecualian untuk aturan tersebut, tetapi seperti yang Anda jelaskan sendiri, ada keuntungan tertentu dalam memiliki API yang dapat dibaca. Argumen Boolean sangat menyusahkan, karena 1) mereka tidak mengungkapkan maksud dan 2) mereka menyiratkan bahwa Anda memanggil fungsi, di mana Anda sebenarnya harus memiliki dua, karena hal-hal yang berbeda akan terjadi tergantung pada bendera boolean.

Pertanyaan utamanya adalah: berapa banyak upaya yang ingin Anda investasikan agar API Anda lebih mudah dibaca? Semakin menghadap ke luar, semakin banyak upaya yang dapat dengan mudah dibenarkan. Jika itu adalah API yang hanya digunakan oleh satu unit lain, maka itu bukan masalah besar. Jika Anda berbicara tentang API REST di mana Anda berencana untuk membiarkan seluruh dunia kehilangan itu, maka Anda mungkin juga berinvestasi lebih banyak upaya untuk membuatnya lebih dimengerti.

Adapun contoh Anda, ada solusi sederhana: Rupanya, dalam kasus Anda visibilitas bukan hanya hal yang benar atau salah. Sebaliknya, Anda memiliki seluruh rangkaian hal yang Anda anggap "visibilitas". Salah satu solusi mungkin untuk memperkenalkan sesuatu seperti Visibilitykelas, yang mencakup semua jenis visibilitas yang berbeda. Jika Anda selanjutnya menerapkan pola Builder untuk membuat ini, Anda mungkin berakhir dengan kode seperti berikut:

Visibility visibility = Visibility.builder()
  .acceptJoinRequests()
  .withPassword("aerw3")
  .replyToDiscoveryRequests()
  .build();
falconPeer.setVisibility(visibility);
jujur
sumber