“ Soft Coding” คืออะไรจริงๆ?


87

ในบทความนี้โดย Alex Papadimoulis คุณสามารถดูตัวอย่างนี้:

private void attachSupplementalDocuments()
{
  if (stateCode == "AZ" || stateCode == "TX") {

    //SR008-04X/I are always required in these states
    attachDocument("SR008-04X");
    attachDocument("SR008-04XI");
  }

  if (ledgerAmnt >= 500000) {
    //Ledger of 500K or more requires AUTHLDG-1A
    attachDocument("AUTHLDG-1A");
  }

  if (coInsuredCount >= 5  && orgStatusCode != "CORP") {
    //Non-CORP orgs with 5 or more co-ins require AUTHCNS-1A
    attachDocument("AUTHCNS-1A");
  }
}

ฉันไม่เข้าใจบทความนี้จริงๆ

ฉันพูด:

ถ้าทุกคนคงกฎทางธุรกิจที่ถูกเก็บไว้ในแฟ้มการกำหนดค่าบางอย่างในชีวิตจะมาก [มากขึ้น ( sic )] เป็นเรื่องยากสำหรับทุกคนที่ยังคงรักษาซอฟต์แวร์: มันน่าจะมีไฟล์จำนวนมากรหัสที่ใช้ร่วมกันหนึ่งไฟล์ขนาดใหญ่ (หรือ, การสนทนา, มีไฟล์กำหนดค่าเล็ก ๆ จำนวนมาก); การปรับใช้การเปลี่ยนแปลงกับกฎเกณฑ์ทางธุรกิจไม่จำเป็นต้องใช้รหัสใหม่ แต่การเปลี่ยนไฟล์กำหนดค่าด้วยตนเอง และการดีบั๊กนั้นยากกว่ามาก

นี่คืออาร์กิวเมนต์ที่มีจำนวนเต็มคงที่ "500000" ในไฟล์กำหนดค่าหรือ "AUTHCNS-1A" และค่าคงที่สตริงอื่น ๆ

นี่เป็นวิธีที่ไม่ดีได้อย่างไร

ในตัวอย่างนี้ "500000" ไม่ใช่ตัวเลข ตัวอย่างเช่นไม่ใช่:

int doubleMe(int a) { return a * 2;}

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

ในทางตรงกันข้าม "500000" ไม่ได้เป็นเพียงตัวเลข เป็นค่าที่สำคัญหนึ่งที่แสดงถึงแนวคิดของจุดพักในการทำงาน สามารถใช้หมายเลขนี้ได้มากกว่าหนึ่งแห่ง แต่ไม่ใช่หมายเลขที่คุณใช้ เป็นแนวคิดของขีด จำกัด / เส้นขอบซึ่งจะใช้กฎหนึ่งข้อด้านล่างและกฎข้อใดข้อหนึ่ง

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

นอกจากนี้หากวันพรุ่งนี้รัฐบาลเรียกร้องให้ "ตั้งแต่วันที่ 5/3/2550 คุณต้องเพิ่ม AUTHLDG-122B แทน AUTHLDG-1A" ค่าคงที่สตริงนี้ไม่ใช่ค่าคงที่สตริงที่เรียบง่าย มันเป็นสิ่งที่แสดงถึงความคิด เป็นเพียงมูลค่าปัจจุบันของแนวคิดนั้น (ซึ่งก็คือ "สิ่งที่คุณเพิ่มถ้าบัญชีแยกประเภทสูงกว่า 500k")

ให้ฉันอธิบาย ฉันไม่ได้บอกว่าบทความผิด ฉันแค่ไม่เข้าใจ อาจจะอธิบายไม่ดีเกินไป (อย่างน้อยก็สำหรับความคิดของฉัน)

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


21
เล่นตัวต่อ: อะไรคือชื่อที่ดีสำหรับตัวเลขเหล่านั้น ฉันคิดว่าคุณจะพบว่าชื่อนั้นไม่ได้เพิ่มมูลค่าใด ๆ หรืออธิบายทุกอย่างที่โค้ดได้อธิบายไปแล้วและบ่อยครั้งในขณะที่เพิ่มความกำกวม ("LedgerLimitForAuthDlg1A"?) ผมพบว่าบทความที่ยอดเยี่ยมอย่างแน่นอนเนื่องจากวิธีการที่เกี่ยวข้องเป็นอย่างนี้ ฉันได้ทำการบำรุงรักษาระบบที่ใช้ทั้งสองแนวทางและฉันสามารถบอกคุณได้ว่ากฎเกณฑ์ทางธุรกิจอยู่ในรหัส - ทำให้ง่ายต่อการติดตามดูแลและทำความเข้าใจ เมื่อคุณใช้การกำหนดค่าคุณควรนับว่าดีกว่า - มีราคาแพงกว่ามาก
Luaan

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

สำหรับภาษาขั้นสูงที่เหมาะสมการกำหนดค่าจะใช้รูปแบบของรูทีนย่อยที่แท้จริงและไม่ใช่สตริง
Thorbjørn Ravn Andersen

คำตอบ:


100

ผู้เขียนได้เตือนถึงสิ่งที่เป็นนามธรรมก่อนวัยอันควร

บรรทัดif (ledgerAmt > 500000)ดูเหมือนกฎเกณฑ์ทางธุรกิจที่คุณคาดว่าจะเห็นสำหรับระบบธุรกิจขนาดใหญ่ที่มีความต้องการซับซ้อนอย่างเหลือเชื่อ แต่แม่นยำและได้รับการบันทึกไว้อย่างดี

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

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

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

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

รหัสประเภทนี้มีแนวโน้มที่จะได้รับการปกป้องจากความจริงที่ว่ารหัสนั้นอาจมีการแมปแบบหนึ่งต่อหนึ่งตามข้อกำหนด คือเมื่อนักพัฒนารู้ว่า500000ตัวเลขปรากฏขึ้นสองครั้งในข้อกำหนดผู้พัฒนานั้นก็รู้ว่ามันปรากฏขึ้นสองครั้งในรหัส

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

สถานการณ์นี้เกิดขึ้นมากมายในซอฟต์แวร์ด้านกฎหมาย / การเงิน (เช่นใบเสนอราคาประกันภัย) - ผู้ที่เขียนเอกสารดังกล่าวไม่ใช่วิศวกรและพวกเขาไม่มีปัญหาในการคัดลอก + วางชิ้นส่วนทั้งหมดของสเปคโดยปรับเปลี่ยนคำ / ตัวเลขไม่กี่คำ ปล่อยให้ส่วนใหญ่มันเหมือนกัน

ในสถานการณ์เหล่านั้นวิธีที่ดีที่สุดในการจัดการกับข้อกำหนดการคัดลอกวางคือการเขียนรหัสคัดลอกวางและทำให้รหัสดูคล้ายกับข้อกำหนด (รวมถึงการเข้ารหัสข้อมูลทั้งหมด) ให้ได้มากที่สุด

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


28
Domain Specific Language (DSL) สามารถเป็นวิธีที่ดีในการทำให้รหัสอ่านเพิ่มเติมเช่นเอกสารความต้องการ
เอียน

13
ข้อดีอีกอย่างของ DSL คือทำให้การผสมแอปพลิเคชันงานนำเสนอหรือตรรกะการคงอยู่กับกฎทางธุรกิจยากขึ้น
Erik Eidt

16
การคิดว่าแอปพลิเคชันของคุณมีความพิเศษพอที่จะรับประกัน DSL ของตัวเองได้นั้นมักเป็นเรื่องตลก
brian_o

8
Those requirements are typically owned and maintained by business analysts and subject matter experts, rather than by engineersซึ่งไม่ใช่ความคิดที่ดีเสมอไป บางครั้งการกระทำของการเปลี่ยนความต้องการเหล่านั้นเป็นรหัสจะเปิดเผยกรณีมุมที่ข้อกำหนดไม่ได้กำหนดไว้อย่างดีหรือมีการกำหนดในลักษณะที่ขัดต่อผลประโยชน์ทางธุรกิจ หากนักวิเคราะห์ธุรกิจและนักพัฒนาสามารถร่วมมือกันเพื่อให้บรรลุเป้าหมายร่วมกันได้ก็จะสามารถหลีกเลี่ยงปัญหาได้มากมาย
kasperd

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

44

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

นอกจากนี้ในวันพรุ่งนี้รัฐบาลจะไป "ตั้งแต่วันที่ 5/3/2550 คุณต้องเพิ่ม AUTHLDG-122B แทน AUTHLDG-1A"

ใช่แล้วคุณเปลี่ยนรหัส จุดของบทความคือมันไม่ซับซ้อนในการเปลี่ยนรหัสมากกว่าการเปลี่ยนไฟล์การกำหนดค่า

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

คุณจะรู้ได้อย่างไรว่าคุณจะไม่ต้องการมันในภายหลัง หรือคนอื่นสำหรับเรื่องนั้น?

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

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

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


4
บ่อยครั้งที่มันซับซ้อนกว่าการเปลี่ยนรหัสมากกว่าไฟล์กำหนดค่า คุณอาจต้องการนักพัฒนาและสร้างระบบ / รอบการเปิดตัวสำหรับอดีตในขณะที่หลังต้องเปลี่ยนตัวเลขในกล่องใน UI การตั้งค่าที่เป็นมิตร
OrangeDog

6
@ OrangeDog ใช่นั่นคือวิธีที่มันดูครั้งแรก แต่ถ้าคุณทำสิ่งนี้ UI การตั้งค่าจะเป็นอะไรก็ได้แต่เป็นมิตรกับกล่องข้อความที่ไร้ความหมายหลายร้อยกล่องขอให้คุณรู้ว่าใครรู้ และตอนนี้คุณต้องสร้าง UI และจัดทำเอกสาร โปรดทราบว่านั่นไม่ได้หมายความว่าการกำหนดค่าไม่ใช่วิธีที่ดีที่จะไป - มีหลายกรณีที่เป็นตัวเลือกที่เหมาะสม แต่ไม่ได้อยู่ในตัวอย่างใด ๆในบทความ และครั้งสุดท้ายที่มีการเปลี่ยนแปลงกฎหมายเมื่อใด? ครั้งล่าสุดที่มีการเปลี่ยนแปลงกฎ VAT ที่นี่เราต้องทำการคำนวณซ้ำทั้งหมด
Luaan

2
@OrangeDog: คุณกำลังสมมติว่าที่นี่การกำหนดค่าซอฟต์แวร์ให้ตะขอที่จำเป็นสำหรับการตรวจสอบที่คุณต้องการ สังเกตว่าในแต่ละ OP ifนั้นใช้ตัวแปรที่แตกต่างกันอย่างไร! หากตัวแปรที่คุณต้องการไม่สามารถเข้าถึงได้จากการกำหนดค่าคุณต้องแก้ไขซอฟต์แวร์ต่อไป
Matthieu M.

2
@OrangeDog ดังนั้นคุณแนะนำว่าควรมีการเปลี่ยนแปลงอย่างมีนัยสำคัญต่อตรรกะของแอปพลิเคชันซอฟต์แวร์โดยไม่มีวงจร dev / qa / release และการทดสอบที่เหมาะสม?
NPSF3000

3
@OrangeDog: ตกลงคุณใช้ YAML สำหรับการกำหนดค่าตรรกะในตัวอย่าง เนื่องจากตรรกะมีกฎตามเงื่อนไขคุณจึงหาวิธีแสดงเงื่อนไขเหล่านี้ใน YAML ขอแสดงความยินดีคุณได้สร้าง Python ใหม่ ทำไมไม่เขียนแอพทั้งหมดใน Python ล่ะ?
JacquesB

26

บทความจะกล่าวถึง 'Enterprise Rule Engine ซึ่งอาจเป็นตัวอย่างที่ดีกว่าของสิ่งที่เขาโต้เถียง

เหตุผลก็คือคุณสามารถพูดคุยกับประเด็นที่การกำหนดค่าของคุณซับซ้อนจนมีภาษาโปรแกรมของตัวเอง

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

<statecode id="AZ">
    <document id="SR008-04X"/>
    <document id="SR008-04XI"/>
</statecode>

บางทีคุณอาจจะใส่จำนวนบัญชีแยกประเภทด้วย?

<statecode id="ALL">
    <document id="AUTHLDG-1A" rule="ledgerAmt >= 50000"/>
</statecode>

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

ควรสังเกตว่าบทความนี้มาจาก 2007 เมื่อสิ่งประเภทนี้เป็นวิธีการทั่วไป

ทุกวันนี้เราอาจจะแก้ปัญหาด้วยการพึ่งพาการฉีด (DI) เช่นคุณจะมี 'รหัสยาก'

InvoiceRules_America2007 : InvoiceRules

ซึ่งคุณจะแทนที่ด้วยฮาร์ดโค้ดหรือกำหนดค่าได้มากกว่า

InvoiceRules_America2008 : InvoiceRules

เมื่อกฎหมายหรือข้อกำหนดทางธุรกิจเปลี่ยนไป


4
บางทีคุณควรกำหนด "DI" และอาจอธิบายเพิ่มเติมอีกเล็กน้อย
Basil Bourque

9
ทำไมไฟล์นั้นไม่อยู่ในระบบควบคุมแหล่งที่มา?
JDługosz

2
หากเป็นลูกค้าเฉพาะรุ่นที่เข้ารหัสจะมีifคำสั่งที่ยุ่งเหยิงขนาดใหญ่เพื่อให้ค่าที่แตกต่างกันสำหรับลูกค้าแต่ละคน? ฟังดูเหมือนบางสิ่งที่ควรอยู่ในไฟล์ปรับแต่ง การอยู่ในไฟล์ประเภทหนึ่งหรืออย่างอื่นทุกอย่างเท่าเทียมกันไม่มีเหตุผลที่จะไม่ควบคุม / ติดตาม / สำรองไฟล์ @ewan ดูเหมือนว่าจะบอกว่าไฟล์ DSL ที่ไม่สามารถบันทึกเป็นส่วนหนึ่งของโครงการด้วยเหตุผลบางอย่างเมื่อสินทรัพย์แม้ไม่ใช่รหัสเช่นภาพและไฟล์เสียงและเอกสารอย่างแน่นอนคือ
JDługosz

2
คุณควรปรับโครงสร้างค่า "50000" อีกครั้งจาก XML ของคุณและวางไว้ในไฟล์ config แยกต่างหากคุณไม่คิดเหรอ? ... และมันควรจะเป็น 500,000 โดยวิธี
Wildcard

1
@jdlugosz แนวคิดของ ERE คือคุณซื้อระบบแล้วกำหนดค่าตามความต้องการของคุณ อาจเป็นเพราะ devs ภายในกำลังแข่งขันกับระบบ 'ยืดหยุ่น' เหล่านี้พวกเขาจะพยายามเลียนแบบพวกเขา เปลี่ยนการควบคุมการกำหนดค่าแม้ในระบบจาก บริษัท ใหญ่ ๆ อย่าง IBM ก็มักจะเป็นสิ่งที่ตามมาภายหลัง จุดขายคือการเปลี่ยนแปลงอย่างรวดเร็ว
Ewan

17

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

และนั่นแสดงโดยมี (และฉันสามารถโต้แย้งได้ว่าแม้แต่ความคิดเห็นซ้ำซ้อน):

 if (ledgerAmnt >= 500000) {
    //Ledger of 500K or more requires AUTHLDG-1A
    attachDocument("AUTHLDG-1A");
  }

นี่เป็นเพียงการทำซ้ำสิ่งที่รหัสกำลังทำอยู่:

LEDGER_AMOUNT_REQUIRING_AUTHLDG1A=500000
if (ledgerAmnt >= LEDGER_AMOUNT_REQUIRING_AUTHLDG1A) {
    //Ledger of 500K or more requires AUTHLDG-1A
    attachDocument("AUTHLDG-1A");
}

โปรดทราบว่าผู้เขียนสันนิษฐานว่าความหมายของ 500,000 คือผูกกับกฎนี้ มันไม่ใช่ค่าที่เป็นหรือน่าจะนำกลับมาใช้ที่อื่น:

กฎทางธุรกิจเพียงข้อเดียวที่เปลี่ยนแปลงก่อนหน้านี้การเข้ารหัสแบบซอฟต์ก่อนหน้านี้อาจจะมีผลต่อการเปลี่ยนแปลงในจำนวนบัญชีแยกประเภทที่ต้องใช้ฟอร์ม AUTHLDG-1A การเปลี่ยนแปลงกฎธุรกิจอื่น ๆ จะต้องมีการทำงานมากขึ้น - การกำหนดค่าเอกสารรหัส ฯลฯ

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


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

2
@ ZeroOne: ยกเว้นว่าหากการเปลี่ยนแปลงกฎธุรกิจเป็น "บัญชีแยกประเภท 500K ขึ้นไปต้องใช้ AUTHLDG-1A และ AUTHLDG-2B" เป็นไปได้มากว่าบุคคลที่เพิ่มattachDocument("AUTHLDG-2B");บรรทัดจะไม่สามารถอัปเดตชื่อคงที่ในเวลาเดียวกัน ในกรณีนี้ฉันคิดว่ารหัสค่อนข้างชัดเจนเพียงพอโดยไม่มีความคิดเห็นหรือตัวแปรตัวอธิบาย (แม้ว่าอาจเหมาะสมที่จะมีการประชุมเพื่อระบุส่วนที่เหมาะสมของเอกสารข้อกำหนดทางธุรกิจผ่านทางความคิดเห็นของรหัสภายใต้การประชุมดังกล่าวความคิดเห็นของรหัสที่จะเหมาะสมที่นี่)
ruakh

@ruakh ตกลงแล้วฉันจะสร้างค่าคงที่ที่จะเรียกอีกครั้งLEDGER_AMOUNT_REQUIRING_ADDITIONAL_DOCUMENTS(ซึ่งฉันน่าจะทำตั้งแต่แรก) ฉันยังติดนิสัยในการใส่รหัสความต้องการทางธุรกิจลงในข้อความคอมมิท Git ไม่ใช่ในซอร์สโค้ด
ZeroOne

1
@ ZeroOne: แต่สำหรับ AUTHLDG-3C จำนวนเงินในบัญชีแยกประเภทเป็นจำนวนสูงสุดจริงๆ และสำหรับ AUTHLDG-4D จำนวนบัญชีแยกประเภทที่เหมาะสมขึ้นอยู่กับสถานะ (คุณได้รับคะแนนหรือยังสำหรับรหัสประเภทนี้คุณต้องการให้รหัสของคุณสะท้อนถึงกฎเกณฑ์ทางธุรกิจไม่ใช่ความพยายามที่เป็นนามธรรมของกฎเกณฑ์ทางธุรกิจเพราะไม่มีเหตุผลที่จะคาดหวังว่าวิวัฒนาการของกฎเกณฑ์ทางธุรกิจจะสอดคล้องกับ abstractions ที่คุณนำมาใช้)
ruakh

2
โดยส่วนตัวแล้วฉันไม่คัดค้านการใส่หมายเลขเวทย์มนตร์ในโค้ดฉันคัดค้านการจัดโครงสร้างรหัสเพื่อให้ต้องการความคิดเห็นเหล่านี้ ถ้าเป็นฉันฉันจะทำให้เอกสารแต่ละตัวอย่างเป็น enum ด้วยattachIfNecessary()วิธีการของตัวเองและแค่วนรอบเอกสารทั้งหมด
David Moles

8

คำตอบอื่น ๆ นั้นถูกต้องและรอบคอบ แต่นี่คือคำตอบสั้น ๆ และหวานของฉัน

  Rule/value          |      At Runtime, rule/value…
  appears in code:    |   …Is fixed          …Changes
----------------------|------------------------------------
                      |                 |
  Once                |   Hard-code     |   Externalize
                      |                 |   (soft-code)
                      |                 |
                      |------------------------------------
                      |                 |
  More than once      |   Soft-code     |   Externalize
                      |   (internal)    |   (soft-code)
                      |                 |
                      |------------------------------------

หากกฎและค่าพิเศษปรากฏในที่เดียวในรหัสและไม่เปลี่ยนแปลงระหว่างรันไทม์ให้ใช้รหัสยากตามที่แสดงในคำถาม

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

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


1
ฉันชอบคำตอบของคุณ แต่ฉันคิดว่าคุณควรพิจารณาด้วยว่าการเปลี่ยนแปลงนั้นเกิดขึ้นเมื่อใช้งานหรือไม่ สิ่งนี้มีความเกี่ยวข้องเป็นหลักหากสิ่งนั้นเป็นผลิตภัณฑ์ที่จะใช้ในหลาย ๆ องค์กรซึ่งอาจมีกฎที่แตกต่างกันออกไปว่าหัวหน้างานจะต้องอนุมัติการคืนเงินมากกว่า X เป็นต้นและอื่น ๆ
Bloke Down The Pub

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

7

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

ในตัวอย่างที่กำหนดมันไม่ได้ทำให้เกิดความแตกต่างเล็กน้อยว่าค่าที่ให้นั้นเป็นฮาร์ดโค้ดเป็นค่าอินไลน์หรือกำหนดเป็น const

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

ดูว่าหากมีรหัสอยู่รอบ ๆ สิ่งที่ไม่ดีจะเกิดขึ้นอย่างชัดเจน

สิ่งที่เลวร้ายอันดับแรกคือมูลค่า 50000 จะถูกนำไปใช้กับค่าอื่นที่บางแห่งกล่าวว่าจำนวนเงินในบัญชีแยกประเภทที่เปลี่ยนแปลงอัตราภาษีในบางรัฐ ... จากนั้นเมื่อมีการเปลี่ยนแปลงเกิดขึ้นผู้ดูแลไม่มีทางรู้เมื่อเขาพบว่า สองอินสแตนซ์ของ 50,000 ในโค้ดไม่ว่าจะหมายถึง 50k เดียวกันหรือ 50ks ที่ไม่เกี่ยวข้องทั้งหมด และคุณควรค้นหา 49999 และ 5,0001 ในกรณีที่มีคนใช้ค่าคงที่ด้วยหรือไม่ นี่ไม่ใช่การเรียกร้องให้ plonk ตัวแปรเหล่านั้นในไฟล์ปรับแต่งของบริการที่แยกต่างหาก: แต่ควรเป็นค่าคงที่กำหนดและกำหนดขอบเขตภายในคลาสหรือไฟล์ที่ใช้ หากสองอินสแตนซ์ของ 50k ใช้ค่าคงที่เดียวกันพวกเขาก็น่าจะเป็นตัวแทนของข้อ จำกัด ทางกฎหมายเดียวกัน ถ้าไม่พวกเขาอาจไม่; และทั้งสองวิธีพวกเขาจะมีชื่อ

ชื่อไฟล์จะถูกส่งผ่านไปยังฟังก์ชั่น - attachDocument () - ซึ่งยอมรับชื่อไฟล์ฐานเป็นสตริงโดยไม่มีเส้นทางหรือส่วนขยาย ชื่อไฟล์โดยพื้นฐานแล้วคือคีย์ต่างประเทศในบางระบบไฟล์หรือฐานข้อมูลหรือที่ใดก็ตามที่ AttachDocument () รับไฟล์จาก แต่สายไม่ได้บอกอะไรคุณเกี่ยวกับเรื่องนี้ - มีกี่ไฟล์? ไฟล์ประเภทใด คุณจะรู้ได้อย่างไรว่าเมื่อคุณเปิดตลาดใหม่ไม่ว่าคุณจะต้องอัพเดตฟังก์ชั่นนี้หรือไม่? พวกเขาสามารถแนบกับสิ่งประเภทใดได้บ้าง ผู้ดูแลถูกทิ้งให้อยู่ในความมืดมิดและสิ่งที่เขามีคือสตริงซึ่งอาจปรากฏหลายครั้งในรหัสและหมายถึงสิ่งต่าง ๆ ทุกครั้งที่ปรากฏ ในที่เดียว "SR008-04X" เป็นรหัสโกง อีกอย่างหนึ่งก็คือคำสั่งให้สั่งจรวดเสริม SR008 สี่ตัว ที่นี่มัน ' ชื่อไฟล์ สิ่งเหล่านี้เกี่ยวข้องกันหรือไม่? มีคนเพิ่งเปลี่ยนฟังก์ชั่นนั้นเพื่อพูดถึงไฟล์อื่น "ลูกค้า" จากนั้นคุณผู้ดูแลที่ไม่ดีได้รับแจ้งว่าไฟล์ "ลูกค้า" ต้องเปลี่ยนชื่อเป็น "ลูกค้า" แต่สตริง "ลูกค้า" ปรากฏขึ้น 937 ครั้งในรหัส ... คุณจะเริ่มมองหาที่ไหน?

ปัญหาของเล่นคือค่าที่มีความผิดปกติและสามารถรับประกันสมควรจะไม่ซ้ำกันในรหัส ไม่ใช่ "1" หรือ "10" แต่ "50,000" ไม่ใช่ "ลูกค้า" หรือ "รายงาน" แต่ "SR008-04X"

ตัวแทนเชิดคือเพียงวิธีการอื่น ๆ ในการแก้ไขปัญหาของค่าคงที่ทึบแสง impenetrably คือการรังพวกเขาออกลงในไฟล์ config ของการบริการที่ไม่เกี่ยวข้องบางส่วน

คุณสามารถใช้ข้อผิดพลาดสองข้อนี้เพื่อพิสูจน์ว่าเป็นจริงได้


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

4
ตัวอย่างไม่พังเพราะมันเป็นปัญหาของเล่น รหัสที่ล้อมรอบจะน่ากลัวเสมอเนื่องจากกฎเกณฑ์ทางธุรกิจที่ซอฟต์แวร์ที่มีการใช้งานนั้นน่ากลัวมาก ความพยายามที่จะทำตามขั้นตอนที่ท้าทายนี้ด้วยเครื่องมือสร้างกฎและ DSL และอะไรก็ตามที่มักจะไม่ทำให้โปรแกรมเมอร์ผัดวันประกันพรุ่งเนื่องจากการแก้ปัญหา CS นั้นสนุกกว่าการแก้ไขความซับซ้อนของแบบฟอร์มภาษี ความพยายามในการบรรลุถึง 'ความสง่างาม' มักจะเป็นเรื่องไร้สาระเพราะงานที่ดีที่สุดของซอฟต์แวร์คือการจำลองแบบหายนะที่ซับซ้อน
whatsisname

กฎทางธุรกิจอาจสยองขวัญ แต่นั่นก็ไม่ได้เป็นข้อแก้ตัวสำหรับการเขียนโค้ดขั้นตอนแบบนี้ (ฉันมักจะเห็นด้วยกับ Papadimoulis ว่ามันง่ายกว่าในการสร้างแบบจำลองและรักษากฎในโค้ดมากกว่าในการตั้งค่าฉันแค่คิดว่ามันควรจะเป็นรหัสที่ดีกว่า) ปัญหา DRY ที่ฉันเห็นไม่ใช่ตัวเลขเวทย์มนตร์if (...) { attachDocument(...); }ปัญหาแห้งผมเห็นไม่ได้เป็นหมายเลขมายากลมันซ้ำแล้วซ้ำอีก
David Moles

2

มีหลายประเด็นในเรื่องนี้

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

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

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

เช่นเดียวกับค่าคงที่เป็นส่วนตัวจึงไม่สามารถนำไปใช้ในที่อื่นในรหัสได้

จากนั้นมีรายการกฎทั้งหมดและใช้รายการ

ปัญหาเพิ่มเติมคือวิธีจัดการค่าคงที่ 500,000 อาจดูไม่เด่น แต่ต้องใช้ความระมัดระวังเป็นอย่างมากเพื่อให้แน่ใจว่าแปลงได้อย่างถูกต้อง หากใช้การคำนวณเลขทศนิยมใด ๆ มันอาจถูกแปลงเป็น 500,000.00001 ดังนั้นการเปรียบเทียบกับ 500,000.00000 อาจล้มเหลว หรือแย่ลง 500,000000 ทำงานได้ตามปกติ แต่อย่างใด 565000 ล้มเหลวเมื่อแปลง ตรวจสอบให้แน่ใจว่าการแปลงนั้นชัดเจนและคุณไม่ต้องเดาโดยคอมไพเลอร์ บ่อยครั้งสิ่งนี้ทำได้โดยการแปลงเป็น BigInteger หรือ BigDecimal ก่อนที่จะถูกใช้


2

แม้ว่าจะไม่ได้กล่าวถึงในคำถามโดยตรง แต่ฉันต้องการทราบว่าสิ่งที่สำคัญไม่ควรทำ ฝังตรรกะทางธุรกิจไว้ในโค้ด

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

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

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

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


นี่คือสิ่งที่ฉันคิด !!! เมื่อลอจิกถูกฝังลึกในโค้ดผู้ใช้โดเมน / หัวข้อสำคัญหรือผู้ใช้ทางธุรกิจจะเห็นคุณค่าและตรรกะที่ใช้เพื่อให้มั่นใจว่าถูกต้องและวิเคราะห์พฤติกรรมของระบบได้อย่างไร สิ่งหนึ่งที่ไฟล์ config ไม่สามารถทำให้การตั้งค่าที่มองเห็นได้ จะต้องมีวิธีการบางอย่างสำหรับการส่งเสริมการมองเห็นของกฎเกณฑ์ทางธุรกิจ - แม้ว่าจะทำให้การเข้ารหัส "ยาก" ฉันสามารถยอมรับคลาสที่บางหรือชุดของคลาสที่ทำงานโดยไม่ต้องกังวลในเรื่องอื่น ๆ ตราบใดที่ผู้ใช้ทางธุรกิจมีวิธีการเข้าถึงและเข้าใจพวกเขา
ErikE
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.