ใช้การตรวจสอบชนิดคงที่เพื่อป้องกันข้อผิดพลาดทางธุรกิจ


13

ฉันเป็นแฟนตัวยงของการตรวจสอบประเภทคงที่ มันป้องกันคุณจากการทำผิดพลาดแบบนี้:

// java code
Adult a = new Adult();
a.setAge("Roger"); //static type checker would complain
a.setName(42); //and here too

แต่มันไม่ได้ป้องกันคุณจากการทำผิดพลาดแบบนี้:

Adult a = new Adult();
// obviously you've mixed up these fields, but type checker won't complain
a.setAge(150); // nobody's ever lived this old
a.setWeight(42); // a 42lb adult would have serious health issues

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

class Age extends Integer{};
class Pounds extends Integer{};

class Adult{
    ...
    public void setAge(Age age){..}
    public void setWeight(Pounds pounds){...}
}

Adult a = new Adult();
a.setAge(new Age(42));
a.setWeight(new Pounds(150));

นี่ถือว่าเป็นการปฏิบัติที่ดีหรือไม่? หรือมีปัญหาทางวิศวกรรมที่ไม่คาดฝันเกิดขึ้นกับการออกแบบที่ จำกัด เช่นนี้หรือไม่?


5
จะa.SetAge( new Age(150) )ยังคงรวบรวมหรือไม่
John Wu

1
ประเภท int ของ Java มีช่วงคงที่ เห็นได้ชัดว่าคุณต้องการมีจำนวนเต็มที่มีช่วงที่กำหนดเองเช่นจำนวนเต็ม <18, 110> คุณกำลังมองหาประเภทการปรับแต่งหรือประเภทที่ขึ้นต่อกันซึ่งมีภาษา (ที่ไม่ใช่กระแสหลัก) ให้บริการ
Theodoros Chatzigiannakis

3
สิ่งที่คุณกำลังพูดถึงเป็นวิธีที่ยอดเยี่ยมในการออกแบบ! น่าเศร้าที่ Java มีระบบการพิมพ์แบบดั้งเดิมดังนั้นจึงเป็นการยากที่จะทำได้ และแม้ว่าคุณจะพยายามทำมันอาจส่งผลให้เกิดผลข้างเคียงเช่นประสิทธิภาพที่ลดลงหรือประสิทธิภาพของนักพัฒนาลดลง
ร่าเริง

@JohnWu ใช่ตัวอย่างของคุณจะยังคงรวบรวม แต่นั่นเป็นจุดเดียวของความล้มเหลวสำหรับตรรกะนั้น เมื่อคุณประกาศnew Age(...)วัตถุของคุณคุณจะไม่สามารถกำหนดให้กับตัวแปรประเภทWeightในสถานที่อื่นได้อย่างไม่ถูกต้อง ช่วยลดจำนวนสถานที่ที่สามารถเกิดข้อผิดพลาดได้
J-bob

คำตอบ:


12

คุณต้องการระบบหน่วย (ไม่ไม่ใช่การทดสอบหน่วย "หน่วย" เช่นเดียวกับใน "หน่วยทางกายภาพ" เช่นเมตรโวลต์ ฯลฯ )

ในรหัสของคุณAgeแสดงเวลาและPoundsแสดงถึงมวล สิ่งนี้นำไปสู่สิ่งต่าง ๆ เช่นการแปลงหน่วยหน่วยฐานความแม่นยำ ฯลฯ


มี / มีความพยายามที่จะได้สิ่งนั้นเป็น Java เช่น:

สองคนต่อมาดูเหมือนจะมีชีวิตอยู่ในสิ่ง gitub นี้: https://github.com/unitsofmeasurement


C ++ มีหน่วยงานผ่านBoost


LabView มาพร้อมกับยูนิตจำนวนมาก


มีตัวอย่างอื่น ๆ ในภาษาอื่น ๆ (ยินดีต้อนรับการแก้ไข)


นี่ถือว่าเป็นการปฏิบัติที่ดีหรือไม่?

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

แต่สำหรับภาษาระดับสูงที่ใช้งานทั่วไปซึ่งความต้องการต่ำสำหรับความแม่นยำจำนวนมาก

หรือมีปัญหาทางวิศวกรรมที่ไม่คาดฝันเกิดขึ้นกับการออกแบบที่ จำกัด เช่นนี้หรือไม่?

ฉันเดาว่าจะเป็น: ประสิทธิภาพ / หน่วยความจำ หากคุณจัดการกับค่าจำนวนมากค่าใช้จ่ายของวัตถุต่อมูลค่าอาจกลายเป็นปัญหา แต่เป็นเสมอ: การเพิ่มประสิทธิภาพก่อนกำหนดเป็นรากของความชั่วร้ายทั้งหมด

ฉันคิดว่า "ปัญหา" ที่ใหญ่กว่านั้นคือการทำให้ผู้คนคุ้นเคย

class Adult
{
    ...
    public void setAge(int ageInYears){..}

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


"ผู้คนจะสับสนเมื่อพวกเขาต้องผ่านวัตถุเป็นค่าสำหรับบางสิ่งบางอย่างที่ดูเหมือนจะถูกลบล้างด้วยความเรียบง่ายint... " --- ซึ่งเรามีอาจารย์ใหญ่แห่งความประหลาดใจน้อยที่สุดเป็นแนวทางที่นี่ จับดี.
เกร็ก Burghardt

1
ฉันคิดว่าช่วงที่กำหนดเองย้ายสิ่งนี้ออกมาจากขอบเขตของไลบรารีหน่วยและเป็นประเภทที่ขึ้นกับ
jk

6

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

หากคุณต้องการระดับการวัดที่ละเอียดยิ่งขึ้นซึ่งกำหนดประเภทสำหรับหน่วยนั้นจะเป็นประโยชน์:

public struct Weight {
    private int pounds;
    private int ounces;

    public Weight(int pounds, int ounces) {
        // Value range checks go here
        // Throw exception if ounces is greater than 16?
    }

    // Getters go here
}

สำหรับบางอย่างเช่น "อายุ" ฉันขอแนะนำให้คำนวณในเวลาทำงานตามวันเดือนปีเกิดของบุคคล:

public class Adult {
    private Date birthDate;

    public Interval getCurrentAge() {
        return calculateAge(Date.now());
    }

    public Interval calculateAge(Date date) {
        // Return interval between birthDate and date
    }
}

2

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

บางภาษาเช่น Scala รองรับประเภทที่ติดแท็กค่อนข้างง่าย (ดูลิงก์ด้านบน) ในอื่น ๆ คุณสามารถสร้างคลาสของ wrapper ของคุณเองได้ แต่วิธีนี้จะสะดวกกว่า

การตรวจสอบความถูกต้องเช่นการตรวจสอบว่าความสูงของบุคคลนั้น "สมเหตุสมผล" เป็นปัญหาอื่น คุณสามารถใส่รหัสดังกล่าวในAdultชั้นเรียนของคุณ(ตัวสร้างหรือ setters) หรือภายในชั้นเรียนประเภท / wrapper ที่ติดแท็กของคุณ ในทางใดทางหนึ่งคลาสที่มีอยู่ภายในเช่นURLหรือUUIDเติมเต็มบทบาทดังกล่าว (ในกลุ่มอื่น ๆ เช่นการจัดเตรียมวิธีการยูทิลิตี้)

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

ในรหัสที่ฉันเขียนฉันมักจะสร้างคลาส wrapper ถ้าฉันผ่านแผนที่ ประเภทต่าง ๆ เช่นMap<String, String>ทึบแสงด้วยตัวเองดังนั้นการห่อไว้ในชั้นเรียนด้วยชื่อที่มีความหมายเช่นNameToAddressช่วยได้มาก แน่นอนว่าด้วยประเภทแท็กคุณสามารถเขียนMap<Name, Address>และไม่จำเป็นต้องใช้ wrapper สำหรับแผนที่ทั้งหมด

อย่างไรก็ตามสำหรับประเภทที่เรียบง่ายเช่น Strings หรือ Integers ฉันได้พบคลาส wrapper (ใน Java) ว่าเป็นเรื่องที่น่ารำคาญมาก ตรรกะทางธุรกิจปกติไม่ได้เลวร้ายนัก แต่มีปัญหาหลายอย่างเกี่ยวกับการทำให้เป็นอนุกรมประเภทเหล่านี้กับ JSON การจับคู่กับวัตถุ DB ฯลฯ คุณสามารถเขียนโปรแกรมแมปและตะขอสำหรับกรอบใหญ่ทั้งหมด (เช่น Jackson และ Spring Data) แต่งานพิเศษและการบำรุงรักษาที่เกี่ยวข้องกับรหัสนี้จะชดเชยสิ่งที่คุณได้รับจากการใช้โปรแกรมเสริมเหล่านี้ แน่นอนว่า YMMV และในระบบอื่นยอดคงเหลืออาจแตกต่างกัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.