จะโหลดและจัดเก็บการตั้งค่าจากไฟล์ได้ที่ไหน


9

ฉันคิดว่าคำถามนี้ควรใช้กับโปรแกรมส่วนใหญ่ที่โหลดการตั้งค่าจากไฟล์ คำถามของฉันมาจากมุมมองการเขียนโปรแกรมและเป็นวิธีจัดการกับการโหลดการตั้งค่าจากไฟล์ในแง่ของคลาสและการเข้าถึงที่แตกต่างกัน ตัวอย่างเช่น

  • หากโปรแกรมมีsettings.iniไฟล์อย่างง่ายเนื้อหาของมันควรจะโหลดในload()วิธีการเรียนหรือบางทีนวกรรมิก?
  • ควรเก็บค่าไว้ในpublic staticตัวแปรหรือควรมีstaticวิธีรับและตั้งค่าคุณสมบัติหรือไม่
  • จะเกิดอะไรขึ้นหากไฟล์ไม่มีอยู่หรืออ่านไม่ได้? คุณจะให้โปรแกรมที่เหลือรู้ว่าไม่สามารถรับคุณสมบัติเหล่านั้นได้อย่างไร
  • เป็นต้น

ฉันหวังว่าฉันจะขอสิ่งนี้ในสถานที่ที่เหมาะสมที่นี่ ฉันต้องการตั้งคำถามให้เป็นผู้ไม่เชื่อเรื่องภาษามากที่สุด แต่ฉันมุ่งเน้นที่ภาษาที่มีสิ่งต่าง ๆ เช่นมรดกโดยเฉพาะ Java และ C # .NET


1
สำหรับ. NET, วิธีที่ดีที่สุดคือใช้ App.config และคลาส System.Configuration.ConfigurationManager เพื่อรับการตั้งค่า
Gibson

@Gibson ฉันเพิ่งเริ่มต้นใน. NET ดังนั้นคุณสามารถเชื่อมโยงฉันกับบทเรียนที่ดีสำหรับชั้นเรียนที่?
Andy

Stackoverflow มีคำตอบมากมาย การค้นหาอย่างรวดเร็วเผยให้เห็น: stackoverflow.com/questions/114527/…และstackoverflow.com/questions/13043530/…นอกจากนี้ยังมีข้อมูลจำนวนมากใน MSDN เริ่มต้นที่นี่msdn.microsoft.com/en-us/library/ms228063(v = vs.100) .aspxและmsdn.microsoft.com/en-us/library/ms184658(v=vs.110).aspx
Gibson

@Gibson ขอบคุณสำหรับลิงก์เหล่านั้น พวกเขาจะมีประโยชน์มาก
Andy

คำตอบ:


8

นี่เป็นคำถามที่สำคัญมากและมักจะทำผิดพลาดเพราะไม่ได้รับความสำคัญเพียงพอแม้ว่ามันจะเป็นส่วนสำคัญของทุก ๆ แอปพลิเคชัน นี่คือแนวทางของฉัน:

คลาส 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)

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

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


+1 ขอบคุณสำหรับคำตอบ ก่อนหน้านี้เมื่อฉันจัดเก็บการตั้งค่าผู้ใช้ฉันเพิ่งอ่านจากไฟล์เช่นไฟล์.iniเพื่อให้สามารถอ่านได้โดยมนุษย์ แต่คุณแนะนำว่าควรจัดลำดับคลาสกับตัวแปรในหรือไม่?
Andy

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

ตอนนี้ฉันเข้าใจดีขึ้นเล็กน้อย ดังนั้นโดยหลักแล้วคุณกำลังบอกว่ามีคลาสหนึ่งคลาสที่มีเขตข้อมูล / การตั้งค่าทั้งหมดและอีกอันหนึ่งตั้งค่าในคลาสนั้นจากไฟล์? จากนั้นฉันจะได้รับค่าจากชั้นหนึ่งในชั้นเรียนอื่นได้อย่างไร
Andy

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

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

4

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

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

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


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

@ Andy - แน่นอน แต่ถ้าโมดูลเข้าถึงการกำหนดค่าโดยตรงพวกเขาไม่ทราบว่าบริบทกำลังทำงานอยู่ดังนั้นพวกเขาจึงไม่สามารถกำหนดวิธีจัดการกับปัญหาการกำหนดค่าได้ หากแอปพลิเคชันทำการโหลดมันสามารถจัดการกับปัญหาใด ๆ เพราะมันรู้บริบท แอพบางตัวอาจต้องการล้มเหลวเป็นค่าเริ่มต้นบางแอพต้องการยกเลิก ฯลฯ
Telastyn

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

0

ถ้าคุณใช้. NET สำหรับการเขียนโปรแกรมคลาสคุณมีตัวเลือกต่าง ๆ เช่นทรัพยากร, web.config หรือแม้แต่ไฟล์ที่กำหนดเอง

หากคุณใช้ทรัพยากรหรือ web.config ข้อมูลจะถูกจัดเก็บจริงในไฟล์ XML แต่การโหลดจะเร็วกว่า

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

สำหรับไฟล์อื่น ๆ หรือภาษาการเขียนโปรแกรมคำตอบข้างต้นโดยเบเนดิกต์จะทำงาน


ขอบคุณสำหรับคำตอบของคุณและขออภัยสำหรับการตอบกลับช้า การใช้ทรัพยากรจะเป็นตัวเลือกที่ดี แต่ฉันเพิ่งรู้ว่าอาจไม่ใช่ตัวเลือกที่ดีที่สุดสำหรับฉันเพราะฉันวางแผนที่จะพัฒนาโปรแกรมเดียวกันในภาษาอื่นดังนั้นฉันควรมีไฟล์ XML หรือ JSON แต่ อ่านในชั้นเรียนของฉันเองเพื่อให้ไฟล์เดียวกันสามารถอ่านได้ในภาษาการเขียนโปรแกรมอื่น
แอนดี้
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.