เราควรรู้อยู่เสมอว่า API กำลังทำอะไรโดยดูที่รหัส?


20

เมื่อเร็ว ๆ นี้ฉันได้พัฒนา API ของตัวเองและด้วยความสนใจลงทุนในการออกแบบ API ฉันมีความสนใจอย่างมากว่าฉันจะปรับปรุงการออกแบบ API ได้อย่างไร

แง่มุมหนึ่งที่เกิดขึ้นสองสามครั้งคือ (ไม่ใช่โดยผู้ใช้ API ของฉัน แต่ในการสังเกตการอภิปรายเกี่ยวกับหัวข้อ): สิ่งหนึ่งที่ควรรู้เพียงแค่ดูรหัสที่เรียก API ว่ามันกำลังทำอะไรอยู่

ตัวอย่างเช่นดูการสนทนานี้ใน GitHub สำหรับวาทกรรมวาทกรรมมันมีลักษณะดังนี้:

foo.update_pinned(true, true);

เพียงแค่มองไปที่โค้ด (โดยไม่รู้ชื่อพารามิเตอร์เอกสาร ฯลฯ ) เราไม่สามารถเดาได้ว่ามันจะทำอะไร - อาร์กิวเมนต์ที่ 2 หมายถึงอะไร การปรับปรุงที่แนะนำคือการมี:

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

และนั่นก็เป็นการล้างสิ่งต่าง ๆ (ข้อโต้แย้งที่สองคือว่าจะตรึง foo ทั่วโลกฉันเดา) และฉันเห็นด้วยในกรณีนี้ภายหลังจะเป็นการปรับปรุงอย่างแน่นอน

แต่ผมเชื่อว่าอาจจะมีกรณีที่วิธีการในการตั้งค่าสถานะที่แตกต่างกัน แต่ที่เกี่ยวข้องเหตุผลจะได้รับการสัมผัสที่ดีกว่าเรียกวิธีหนึ่งมากกว่าคนแยกเป็นสัดส่วนถึงแม้คุณจะไม่ทราบว่าสิ่งที่ทำเพียงแค่มองรหัส (ดังนั้นคุณจะต้องหันไปดูที่ชื่อพารามิเตอร์และเอกสารประกอบเพื่อค้นหา - ซึ่งโดยส่วนตัวแล้วฉันจะทำทุกอย่างไม่ว่าฉันจะไม่คุ้นเคยกับ API)

ตัวอย่างเช่นฉันเปิดเผยหนึ่งวิธีSetVisibility(bool, string, bool)ในFalconPeerและฉันรับทราบเพียงแค่ดูที่บรรทัด:

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

คุณคงไม่รู้ว่ามันกำลังทำอะไรอยู่ มันตั้งค่าที่แตกต่างกัน 3 ค่าที่ควบคุม "การมองเห็น" ของfalconPeerในแง่ตรรกะ: ยอมรับคำขอเข้าร่วมเฉพาะด้วยรหัสผ่านและตอบกลับคำขอค้นพบ แยกออกมานี้ในวิธีที่ 3 โทรอาจนำไปสู่ผู้ใช้ API เพื่อชุดแง่มุมหนึ่งของ "การมองเห็น" ลืมกับคนอื่น ๆ ชุดว่าผมบังคับให้พวกเขาคิดเกี่ยวกับโดยเฉพาะการเปิดเผยวิธีการหนึ่งที่จะตั้งทุกแง่มุมของ "การมองเห็น" นอกจากนี้เมื่อผู้ใช้ต้องการเปลี่ยนด้านหนึ่งพวกเขาเกือบจะต้องการเปลี่ยนด้านอื่นและตอนนี้สามารถทำได้ในสายเดียว


13
นี่คือเหตุผลที่ว่าทำไมบางภาษามีพารามิเตอร์ชื่อ (บางครั้งก็บังคับใช้พารามิเตอร์ที่มีชื่อ) ตัวอย่างเช่นคุณอาจกลุ่มจำนวนมากของการตั้งค่าในการที่ง่ายวิธีการ:update foo.update(pinned=true, globally=true)หรือ: foo.update_pinned(true, globally=true). ดังนั้นคำตอบสำหรับคำถามของคุณควรคำนึงถึงคุณสมบัติของภาษาด้วยเพราะ API ที่ดีสำหรับภาษา X อาจไม่ดีสำหรับภาษา Y และ viceversa
Bakuriu

เห็นด้วย - มาหยุดใช้ booleans :)
Ven

2
เป็นที่รู้จักกันในชื่อ"boolean trap"
user11153

@Bakuriu ถึงแม้ C จะมีจำนวนถึงแม้ว่าพวกเขาจะเป็นจำนวนเต็มปลอมตัว ฉันไม่คิดว่าจะมีภาษาใดในโลกแห่งความเป็นจริงที่ booleans ออกแบบ API ได้ดี
Doval

1
@Doval ฉันไม่เข้าใจสิ่งที่คุณกำลังจะพูด ฉันใช้ booleans ในสถานการณ์นั้นเพียงเพราะ OP ใช้พวกเขา แต่ประเด็นของฉันไม่เกี่ยวข้องอย่างสมบูรณ์กับค่าที่ส่งผ่าน ตัวอย่างเช่น: setSize(10, 20)ไม่เป็นที่อ่านได้เป็นหรือsetSize(width=10, height=20) random(distribution='gaussian', mean=0.5, deviation=1)ในภาษาที่มีพารามิเตอร์ที่บังคับใช้ชื่อ booleans สามารถนำเสนอข้อมูลจำนวนเดียวกันกับการใช้ค่าคงที่ enums / ชื่อดังนั้นพวกเขาสามารถดีใน API
Bakuriu

คำตอบ:


27

ความปรารถนาของคุณที่จะไม่แบ่งออกเป็นสามวิธีการโทรเป็นที่เข้าใจได้อย่างสมบูรณ์ แต่คุณมีตัวเลือกอื่นนอกเหนือจากพารามิเตอร์บูลีน

คุณสามารถใช้ enums:

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

หรือแม้กระทั่งค่าสถานะ enum (หากภาษาของคุณรองรับ):

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

หรือคุณสามารถใช้วัตถุพารามิเตอร์ :

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

รูปแบบวัตถุพารามิเตอร์ให้ข้อดีอื่น ๆ ที่คุณอาจเห็นว่ามีประโยชน์ มันทำให้การผ่านไปมาและทำให้เป็นชุดของพารามิเตอร์เป็นเรื่องง่ายและคุณสามารถให้ค่า "เริ่มต้น" ที่ไม่ระบุพารามิเตอร์ได้อย่างง่ายดาย

หรือคุณสามารถใช้พารามิเตอร์บูลีนก็ได้ Microsoft ดูเหมือนว่าจะทำตลอดเวลาด้วย. NET Framework API ดังนั้นคุณสามารถยักและพูดว่า "ถ้ามันดีพอสำหรับพวกเขามันก็ดีพอสำหรับฉัน"


รูปแบบวัตถุพารามิเตอร์ยังคงมีปัญหาที่ OP ระบุไว้: "การแยกออกเป็น 3 การเรียกใช้เมธอดอาจนำไปสู่ผู้ใช้ API เพื่อตั้งค่าแง่มุมหนึ่งของ" การมองเห็น "ที่ลืมตั้งค่าอื่น ๆ "
แร่

จริง แต่ฉันคิดว่ามันทำให้สถานการณ์ดีขึ้น (ถ้าไม่สมบูรณ์) หากผู้ใช้ถูกบังคับให้สร้างอินสแตนซ์และส่งผ่านวัตถุ VisibilityOptions มันอาจ (ด้วยโชคใด ๆ ) เตือนพวกเขาว่ามีคุณสมบัติอื่น ๆ บนวัตถุ VisibilityOptions ที่พวกเขาอาจต้องการตั้งค่า ด้วยวิธีการโทรสามวิธีสิ่งที่พวกเขาต้องเตือนพวกเขาคือความคิดเห็นเกี่ยวกับวิธีที่พวกเขาโทร
BenM

6

เห็นได้ชัดว่ามีข้อยกเว้นกฎอยู่เสมอ แต่เมื่อคุณอธิบายได้ดีด้วยตัวเองมีข้อได้เปรียบบางประการในการมี API ที่สามารถอ่านได้ อาร์กิวเมนต์บูลีนนั้นน่ารำคาญเป็นพิเศษโดยที่ 1) พวกเขาไม่ได้แสดงเจตนาและ 2) พวกเขาบอกเป็นนัยว่าคุณเรียกใช้ฟังก์ชันซึ่งคุณควรมีสองสิ่งเพราะสิ่งต่าง ๆ จะเกิดขึ้นตามธงบูลีน

คำถามหลักคือ: คุณต้องการลงทุนในการทำให้ API ของคุณอ่านง่ายขึ้นหรือไม่ ยิ่งหันหน้าไปทางด้านนอกมากขึ้นความพยายามมากขึ้นสามารถเป็นธรรมได้อย่างง่ายดาย หากเป็น API ที่ใช้โดยหน่วยอื่นเพียงหน่วยเดียวนั่นไม่ใช่ข้อตกลงที่ยิ่งใหญ่ หากคุณกำลังพูดถึง REST API ที่คุณวางแผนที่จะปล่อยให้คนทั้งโลกสูญเสียมันคุณก็อาจทุ่มเทความพยายามในการทำความเข้าใจให้มากขึ้น

สำหรับตัวอย่างของคุณมีวิธีแก้ปัญหาง่าย ๆ : เห็นได้ชัดว่าในกรณีของคุณการมองเห็นไม่ได้เป็นเพียงเรื่องจริงหรือเท็จ คุณมีสิ่งต่าง ๆ ที่คุณคิดว่าเป็น "ทัศนวิสัย" แทน วิธีแก้ปัญหาหนึ่งอาจจะแนะนำอะไรบางอย่างเช่นVisibilityคลาสซึ่งครอบคลุมการมองเห็นที่แตกต่างกันเหล่านี้ทั้งหมด หากคุณใช้ตัวสร้างรูปแบบเพิ่มเติมสำหรับการสร้างสิ่งเหล่านี้คุณอาจต้องใช้รหัสดังนี้:

Visibility visibility = Visibility.builder()
  .acceptJoinRequests()
  .withPassword("aerw3")
  .replyToDiscoveryRequests()
  .build();
falconPeer.setVisibility(visibility);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.