วิธีที่ดีที่สุดในการโหลดการตั้งค่าแอปพลิเคชัน


24

วิธีง่าย ๆ ในการรักษาการตั้งค่าของแอปพลิเคชัน Java จะถูกแสดงด้วยไฟล์ข้อความที่มีนามสกุล ".properties" ที่มีตัวระบุของแต่ละการตั้งค่าที่เกี่ยวข้องกับค่าเฉพาะ (ค่านี้อาจเป็นตัวเลขสตริงวันที่ ฯลฯ ) . C # ใช้วิธีการที่คล้ายกัน แต่ไฟล์ข้อความจะต้องมีชื่อว่า "App.config" ในทั้งสองกรณีในซอร์สโค้ดคุณต้องเริ่มต้นคลาสเฉพาะสำหรับการอ่านการตั้งค่า: คลาสนี้มีวิธีการส่งคืนค่า (เป็นสตริง) ที่เกี่ยวข้องกับตัวระบุการตั้งค่าที่ระบุ

// Java example
Properties config = new Properties();
config.load(...);
String valueStr = config.getProperty("listening-port");
// ...

// C# example
NameValueCollection setting = ConfigurationManager.AppSettings;
string valueStr = setting["listening-port"];
// ...

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

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

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

เพื่อแก้ปัญหานี้ฉันคิดว่าจะใช้รูปแบบวิธีการของแม่แบบดังนี้

public abstract class Setting
{
    protected abstract bool TryParseValues();

    protected abstract bool CheckValues();

    public abstract void SetDefaultValues();

    /// <summary>
    /// Template Method
    /// </summary>
    public bool TrySetValuesOrDefault()
    {
        if (!TryParseValues() || !CheckValues())
        {
            // parsing error or domain error
            SetDefaultValues();
            return false;
        }
        return true;
    }
}

public class RangeSetting : Setting
{
    private string minStr, maxStr;
    private byte min, max;

    public RangeSetting(string minStr, maxStr)
    {
        this.minStr = minStr;
        this.maxStr = maxStr;
    }

    protected override bool TryParseValues()
    {
        return (byte.TryParse(minStr, out min)
            && byte.TryParse(maxStr, out max));
    }

    protected override bool CheckValues()
    {
        return (0 < min && min < max);
    }

    public override void SetDefaultValues()
    {
        min = 5;
        max = 10;
    }
}

ปัญหาคือด้วยวิธีนี้เราจำเป็นต้องสร้างคลาสใหม่สำหรับแต่ละการตั้งค่าแม้สำหรับค่าเดียว มีวิธีแก้ไขปัญหาอื่น ๆ อีกไหม?

สรุป:

  1. บำรุงรักษาง่าย: ตัวอย่างเช่นการเพิ่มพารามิเตอร์หนึ่งตัวหรือมากกว่า
  2. Extensibility: เวอร์ชันแรกของแอปพลิเคชั่นสามารถอ่านไฟล์การกำหนดค่าไฟล์เดียว แต่เวอร์ชั่นที่ใหม่กว่าอาจให้ความเป็นไปได้ของการตั้งค่าผู้ใช้หลายคน (ผู้ดูแลระบบตั้งค่าพื้นฐานผู้ใช้สามารถตั้งค่าบางอย่างได้
  3. การออกแบบเชิงวัตถุ

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

คำตอบ:


8

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

ผลลัพธ์สุดท้ายนั้นแข็งแกร่งและเหนือสิ่งอื่นใดที่จัดการได้ง่าย


7

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

API การกำหนดค่า

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

int getInt(name, default, min, max)
float getFloat(name, default, min, max)
boolean getBoolean(name, default)
String getString(name, default)
<T extends Enum<T>> T getEnum(name, Class<T> enumClass, T default)

(ฉันคิดว่าฉันได้รับยาชื่อสามัญในอันสุดท้ายแล้ว)

โดยทั่วไปแต่ละวิธีรู้วิธีการแยกวิเคราะห์ค่าสตริงจากไฟล์ config และจัดการข้อผิดพลาดและส่งคืนค่าเริ่มต้นหากเหมาะสม การตรวจสอบช่วงสำหรับค่าตัวเลขน่าจะเพียงพอ คุณอาจต้องการโอเวอร์โหลดที่ละเว้นค่าช่วงซึ่งจะเทียบเท่ากับการระบุช่วงของ Integer.MIN_VALUE, Integer.MAX_VALUE Enum เป็นวิธีที่ปลอดภัยในการตรวจสอบความถูกต้องของสตริงกับชุดสตริงที่แน่นอน

มีบางสิ่งที่ไม่สามารถจัดการได้เช่นค่าหลายค่าค่าที่สัมพันธ์กันการค้นหาตารางแบบไดนามิก ฯลฯ คุณสามารถเขียนการแยกวิเคราะห์พิเศษและการตรวจสอบตามปกติสำหรับสิ่งเหล่านี้ แต่ถ้าสิ่งนี้ซับซ้อนเกินไปฉันก็เริ่มตั้งคำถาม ไม่ว่าคุณจะพยายามทำมากเกินไปกับไฟล์กำหนดค่า

รูปแบบการจัดเก็บ

ไฟล์คุณสมบัติ Java ดูเหมือนดีสำหรับการจัดเก็บคู่คีย์ - ค่าแต่ละรายการและสนับสนุนประเภทของค่าที่ฉันอธิบายไว้ข้างต้นค่อนข้างดี คุณสามารถพิจารณารูปแบบอื่น ๆ เช่น XML หรือ JSON แต่สิ่งเหล่านี้อาจเกินความเป็นจริงเว้นแต่ว่าคุณมีข้อมูลที่ซ้อนกันหรือซ้ำซ้อน ณ จุดนั้นดูเหมือนว่าเกินกว่าไฟล์กำหนดค่า ....

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

ฉันว่าจะติดกับคุณสมบัติถ้าเป็นไปได้ทั้งหมด


2
สวัสดีจวร์ตดีใจที่ได้พบคุณที่นี่ :-) ฉันจะเพิ่มคำตอบ Stuarts ที่ฉันคิดว่าความคิดของคุณ tempalte จะทำงานใน Java ถ้าคุณใช้ Generics เพื่อพิมพ์อย่างยิ่งดังนั้นคุณสามารถมีการตั้งค่า <T> เป็นตัวเลือกเช่นกัน
Martijn Verburg

@StuartMarks: ดีความคิดแรกของฉันเป็นเพียงการเขียนConfigการเรียนและการใช้วิธีการที่เสนอโดยคุณ: getInt(), getByte(), getBoolean()ฯลฯ .. ต่อเนื่องกับความคิดนี้เป็นครั้งแรกที่ผมอ่านค่าทั้งหมดและฉันจะเชื่อมโยงแต่ละมูลค่าให้กับธง (แฟล็กนี้เป็นเท็จหากเกิดปัญหาระหว่างการดีซีเรียลไลซ์เซชันเช่นการแยกวิเคราะห์ข้อผิดพลาด) หลังจากนั้นฉันสามารถเริ่มขั้นตอนการตรวจสอบความถูกต้องสำหรับค่าที่โหลดทั้งหมดและตั้งค่าเริ่มต้นใด ๆ
enzom83

2
ฉันชอบวิธีการ JAXB หรือ YAML บางอย่างเพื่อทำให้รายละเอียดทั้งหมดง่ายขึ้น
Gary Rowe

4

ฉันทำได้อย่างไร:

เริ่มต้นทุกอย่างเป็นค่าเริ่มต้น

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


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

2

มีวิธีแก้ไขปัญหาอื่น ๆ อีกไหม?

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


1
ไม่ต้องยุ่งเกี่ยวกับการแยกวิเคราะห์หรือการแปลงไม่มีการขันรอบด้วยสตริงการกำหนดค่าไม่มีการทิ้งขยะ คุณหมายถึงอะไร
enzom83

1
ฉันหมายถึง: 1. คุณไม่จำเป็นต้องใช้ผลลัพธ์ AppConfig (สตริง) และแยกวิเคราะห์เป็นสิ่งที่คุณต้องการ 2. คุณไม่จำเป็นต้องระบุสตริงใด ๆ เพื่อเลือกพารามิเตอร์การตั้งค่าที่คุณต้องการ นั่นเป็นหนึ่งในสิ่งเหล่านั้นที่มีแนวโน้มที่จะเกิดข้อผิดพลาดของมนุษย์และยากที่จะสร้างใหม่และ 3 คุณไม่จำเป็นต้องทำการแปลงประเภทอื่นเมื่อทำการตั้งค่าโดยทางโปรแกรม
Telastyn

2

อย่างน้อยใน. NET คุณสามารถสร้างออบเจ็กต์การกำหนดค่าที่พิมพ์ตัวเองได้อย่างง่ายดาย - ดูบทความ MSDN นี้สำหรับตัวอย่างรวดเร็ว

Protip: ล้อมคลาส config ของคุณในอินเตอร์เฟสและให้แอปพลิเคชันของคุณพูดคุยกับมัน ทำให้ง่ายต่อการฉีดการกำหนดค่าปลอมสำหรับการทดสอบหรือเพื่อผลกำไร


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

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