นี่เป็นคำถามที่สำคัญมากและมักจะทำผิดพลาดเพราะไม่ได้รับความสำคัญเพียงพอแม้ว่ามันจะเป็นส่วนสำคัญของทุก ๆ แอปพลิเคชัน นี่คือแนวทางของฉัน:
คลาส config ของคุณซึ่งมีการตั้งค่าทั้งหมดควรเป็นประเภทข้อมูลเก่าแบบธรรมดา struct / class:
class Config {
int prop1;
float prop2;
SubConfig subConfig;
}
ไม่จำเป็นต้องมีวิธีการและไม่ควรเกี่ยวข้องกับการสืบทอด (ยกเว้นว่าเป็นตัวเลือกเดียวที่คุณมีในภาษาของคุณสำหรับการใช้งานชุดข้อมูล - ดูย่อหน้าถัดไป) สามารถและควรใช้การจัดองค์ประกอบเพื่อจัดกลุ่มการตั้งค่าเป็นคลาสการกำหนดค่าเฉพาะที่เล็กลง ถ้าคุณทำแบบนี้มันจะเหมาะอย่างยิ่งที่จะผ่านการทดสอบหน่วยและแอปพลิเคชันโดยทั่วไปเนื่องจากจะมีการพึ่งพาน้อยที่สุด
คุณอาจจะต้องใช้ประเภทตัวแปรในกรณีที่การกำหนดค่าสำหรับการตั้งค่าที่แตกต่างกันมีความแตกต่างในโครงสร้าง เป็นที่ยอมรับว่าคุณจะต้องใช้การส่งแบบไดนามิกในบางจุดเมื่อคุณอ่านค่าเพื่อส่งไปยังคลาสการกำหนดค่า (ย่อย) ด้านขวาและไม่ต้องสงสัยเลยว่าสิ่งนี้จะขึ้นอยู่กับการตั้งค่าการกำหนดค่าอื่น
คุณไม่ควรขี้เกียจที่จะพิมพ์การตั้งค่าทั้งหมดเป็นฟิลด์โดยทำสิ่งนี้:
class Config {
Dictionary<string, string> values;
};
นี่คือการดึงดูดเพราะมันหมายความว่าคุณสามารถเขียนคลาสการทำให้เป็นอนุกรมทั่วไปซึ่งไม่จำเป็นต้องรู้ว่ามันเกี่ยวข้องกับสาขาใด แต่มันผิดและฉันจะอธิบายว่าทำไมในช่วงเวลาหนึ่ง
การทำให้เป็นอนุกรมของการตั้งค่าเสร็จในชั้นแยกต่างหาก ไม่ว่า API หรือไลบรารีที่คุณใช้ในการทำสิ่งนี้เนื้อหาของฟังก์ชั่นการทำให้เป็นอันดับของคุณควรมีรายการที่โดยทั่วไปจะเป็นแผนที่จากพา ธ / คีย์ในไฟล์ไปยังฟิลด์บนวัตถุ บางภาษาให้การวิปัสสนาที่ดีและสามารถทำสิ่งนี้ให้คุณนอกกรอบคนอื่น ๆ ที่คุณจะต้องเขียนการแมปอย่างชัดเจน แต่สิ่งสำคัญคือคุณควรจะต้องเขียนการแมปครั้งเดียวเท่านั้น เช่นพิจารณาแยกนี้ฉันปรับจากเอกสาร parser ตัวเลือกโปรแกรมเพิ่ม c ++:
struct Config {
int opt;
} conf;
po::options_description desc("Allowed options");
desc.add_options()
("optimization", po::value<int>(&conf.opt)->default_value(10);
โปรดทราบว่าบรรทัดสุดท้ายโดยทั่วไประบุว่า"การปรับให้เหมาะสม" จะจับคู่กับ Config :: optและมีการประกาศประเภทที่คุณคาดหวัง คุณต้องการให้การอ่านการกำหนดค่าล้มเหลวหากประเภทไม่ใช่สิ่งที่คุณคาดหวังหากพารามิเตอร์ในไฟล์ไม่ใช่แบบลอยหรือแบบ int หรือไม่มีอยู่จริง Ie Failure ควรเกิดขึ้นเมื่อคุณอ่านไฟล์เนื่องจากปัญหาเกิดขึ้นกับรูปแบบ / การตรวจสอบความถูกต้องของไฟล์และคุณควรโยนโค้ดยกเว้น / ส่งคืนและรายงานปัญหาที่แน่นอน คุณไม่ควรล่าช้าในภายหลังในโปรแกรม นั่นเป็นสาเหตุที่คุณไม่ควรถูกล่อลวงให้จับสไตล์พจนานุกรมทั้งหมดดังกล่าวข้างต้นซึ่งจะไม่ล้มเหลวเมื่ออ่านไฟล์ - เนื่องจากการคัดเลือกนักแสดงล่าช้าจนกว่าค่าจะต้องการ
คุณควรทำให้คลาส Config อ่านอย่างเดียวในบางแบบ - การตั้งค่าเนื้อหาของคลาสเมื่อคุณสร้างและกำหนดค่าเริ่มต้นจากไฟล์ หากคุณจำเป็นต้องมีการตั้งค่าแบบไดนามิกในแอปพลิเคชันของคุณที่เปลี่ยนแปลงตลอดจนค่าคงที่ที่ไม่มีคุณควรมีคลาสแยกต่างหากเพื่อจัดการกับค่าไดนามิกแทนที่จะพยายามอนุญาตบิตของคลาสปรับแต่งของคุณให้อ่านได้อย่างเดียว .
เป็นการดีที่คุณอ่านไฟล์ในที่เดียวในโปรแกรมของคุณนั่นคือคุณมี " ConfigReader
" เพียงหนึ่งเดียว อย่างไรก็ตามหากคุณกำลังดิ้นรนกับการได้รับอินสแตนซ์การตั้งค่าที่ส่งไปยังที่ที่คุณต้องการมันจะดีกว่าถ้าคุณมี ConfigReader ตัวที่สองมากกว่าที่จะแนะนำการตั้งค่าระดับโลก (ซึ่งฉันคาดเดาว่า ") ซึ่งพาฉันไปยังจุดต่อไปของฉัน:
หลีกเลี่ยงเสียงไซเรนที่เย้ายวนใจของซิงเกิลตัน: "ฉันจะช่วยให้คุณไม่ต้องผ่านชั้นเรียนนั้นอีกรอบสิ่งก่อสร้างทั้งหมดของคุณจะน่ารักและสะอาดไปเลยมันจะง่ายมาก" ความจริงก็คือสถาปัตยกรรมทดสอบที่ออกแบบมาอย่างดีคุณจะไม่จำเป็นต้องผ่านคลาส Config หรือบางส่วนของมันผ่านคลาสต่างๆของแอปพลิเคชันของคุณ สิ่งที่คุณจะพบในชั้นเรียนระดับบนของคุณฟังก์ชั่น main () หรืออะไรก็ตามที่คุณจะปลดปล่อยความเชื่อมั่นของแต่ละค่าซึ่งคุณจะให้กับคลาสองค์ประกอบของคุณเป็นอาร์กิวเมนต์ที่คุณใส่กลับเข้าไปด้วยกัน ฉีด) singleton / global / static conf จะทำให้การทดสอบหน่วยของคุณใช้งานได้ยากขึ้นและเข้าใจได้ง่ายขึ้น - เช่นจะทำให้นักพัฒนาใหม่สับสนกับทีมของคุณที่ไม่ทราบว่าพวกเขาต้องตั้งค่าสถานะส่วนกลางเพื่อทดสอบสิ่งต่างๆ
หากภาษาของคุณรองรับคุณสมบัติคุณควรใช้เพื่อจุดประสงค์นี้ เหตุผลก็คือมันจะง่ายมากในการเพิ่มการตั้งค่า 'ที่ได้รับ' ซึ่งขึ้นอยู่กับการตั้งค่าอื่นอย่างน้อยหนึ่งรายการ เช่น
int Prop1 { get; }
int Prop2 { get; }
int Prop3 { get { return Prop1*Prop2; }
หากภาษาของคุณไม่สนับสนุนสำนวนคุณสมบัติตามปกติมันอาจมีวิธีแก้ไขปัญหาเพื่อให้ได้ผลเช่นเดียวกันหรือคุณเพียงแค่สร้างคลาส wrapper ที่ให้การตั้งค่าโบนัส หากคุณไม่สามารถมอบผลประโยชน์จากคุณสมบัติเป็นอย่างอื่นการเขียนด้วยตนเองและเสียเวลาก็เป็นการใช้เกเตอร์ / setters เพื่อจุดประสงค์ในการสร้าง OO-god คุณจะดีขึ้นด้วยสนามเก่าธรรมดา
คุณอาจต้องมีระบบในการรวมและใช้หลายการกำหนดค่าจากสถานที่ที่แตกต่างกันในลำดับความสำคัญ ลำดับความสำคัญดังกล่าวควรมีการกำหนดและเข้าใจโดยผู้พัฒนา / ผู้ใช้ทั้งหมดเช่นพิจารณารีจิสทรีหน้าต่าง HKEY_CURRENT_USER / HKEY_LOCAL_MACHINE คุณควรทำลักษณะการทำงานนี้เพื่อให้คุณสามารถกำหนดค่าให้อ่านได้อย่างเดียวเช่น:
final_conf = merge(user_conf, machine_conf)
ค่อนข้างมากกว่า:
conf.update(user_conf)
ในที่สุดฉันก็ควรเพิ่มสิ่งนั้นไว้ถ้ากรอบงาน / ภาษาที่คุณเลือกนั้นมีกลไกการกำหนดค่าในตัวที่เป็นที่รู้จักกันดีคุณควรพิจารณาถึงประโยชน์ของการใช้สิ่งนั้นแทนการม้วนตัวเอง
ดังนั้น. หลายแง่มุมที่ต้องพิจารณา - ทำให้ถูกต้องและมันจะส่งผลกระทบอย่างลึกซึ้งต่อสถาปัตยกรรมแอปพลิเคชันของคุณลดข้อบกพร่องทำให้สิ่งต่าง ๆ สามารถทดสอบได้ง่ายและบังคับให้คุณใช้การออกแบบที่ดีในที่อื่น