กลิ่นรหัสเป็นอาการที่บ่งชี้ว่ามีปัญหาในการออกแบบซึ่งอาจเพิ่มจำนวนข้อบกพร่อง: นี่ไม่ใช่กรณีสำหรับภูมิภาค แต่ภูมิภาคสามารถมีส่วนร่วมในการสร้างกลิ่นรหัสเช่นวิธีการที่ยาวนาน
ตั้งแต่:
anti-pattern (หรือ antipattern) เป็นรูปแบบที่ใช้ในการดำเนินงานทางสังคมหรือธุรกิจหรือวิศวกรรมซอฟต์แวร์ที่อาจใช้กันทั่วไป แต่ไม่มีประสิทธิภาพและ / หรือต่อต้านการปฏิบัติในทางปฏิบัติ
ภูมิภาคเป็นรูปแบบการต่อต้าน พวกเขาต้องการงานมากขึ้นซึ่งไม่ได้เพิ่มคุณภาพหรือความสามารถในการอ่านของรหัสซึ่งไม่ได้ลดจำนวนของข้อบกพร่องและซึ่งอาจทำให้รหัสมีความซับซ้อนมากขึ้นในการปรับโครงสร้าง
อย่าใช้ภูมิภาคภายในวิธีการ refactor แทน
วิธีการต้องสั้น หากมีเพียงสิบบรรทัดในวิธีหนึ่งคุณอาจจะไม่ใช้ขอบเขตเพื่อซ่อนห้าบรรทัดเมื่อทำงานกับอีกห้าบรรทัด
นอกจากนี้แต่ละวิธีต้องทำสิ่งหนึ่งและเพียงคนเดียว ภูมิภาคบนมืออื่น ๆ ที่มีวัตถุประสงค์เพื่อแยกสิ่งที่แตกต่าง หากวิธีการของคุณเป็นแบบ A ดังนั้น B เป็นตรรกะที่จะสร้างสองภูมิภาค แต่นี่เป็นวิธีการที่ผิด คุณควรปรับโครงสร้างวิธีการเป็นสองวิธีแยกกันแทน
การใช้พื้นที่ในกรณีนี้อาจทำให้การปรับโครงสร้างทำได้ยากขึ้น ลองนึกภาพคุณมี:
private void DoSomething()
{
var data = LoadData();
#region Work with database
var verification = VerifySomething();
if (!verification)
{
throw new DataCorruptedException();
}
Do(data);
DoSomethingElse(data);
#endregion
#region Audit
var auditEngine = InitializeAuditEngine();
auditEngine.Submit(data);
#endregion
}
การยุบขอบเขตแรกเพื่อมุ่งเน้นที่สองไม่เพียง แต่มีความเสี่ยง: เราสามารถลืมเกี่ยวกับข้อยกเว้นในการหยุดการไหลได้อย่างง่ายดาย (อาจมีส่วนคำสั่งป้องกันพร้อมreturn
แทน ถ้ารหัสควรจะ refactored ด้วยวิธีนี้:
private void DoSomething()
{
var data = LoadData();
#region Work with database
var verification = VerifySomething();
var info = DoSomethingElse(data);
if (verification)
{
Do(data);
}
#endregion
#region Audit
var auditEngine = InitializeAuditEngine(info);
auditEngine.Submit(
verification ? new AcceptedDataAudit(data) : new CorruptedDataAudit(data));
#endregion
}
ตอนนี้ภูมิภาคไม่มีเหตุผลและคุณไม่สามารถอ่านและเข้าใจรหัสในภูมิภาคที่สองได้โดยไม่ต้องดูรหัสในภาคแรก
อีกกรณีหนึ่งที่ฉันเห็นคือกรณีนี้:
public void DoSomething(string a, int b)
{
#region Validation of arguments
if (a == null)
{
throw new ArgumentNullException("a");
}
if (b <= 0)
{
throw new ArgumentOutOfScopeException("b", ...);
}
#endregion
#region Do real work
...
#endregion
}
มันเป็นเรื่องดึงดูดที่จะใช้พื้นที่เมื่อการตรวจสอบข้อขัดแย้งเริ่มขยาย LOC เป็นสิบ แต่มีวิธีที่ดีกว่าในการแก้ปัญหานี้: แหล่งที่ใช้โดยซอร์สโค้ด. NET Framework:
public void DoSomething(string a, int b)
{
if (a == null)
{
throw new ArgumentNullException("a");
}
if (b <= 0)
{
throw new ArgumentOutOfScopeException("b", ...);
}
InternalDoSomething(a, b);
}
private void InternalDoSomething(string a, int b)
{
...
}
อย่าใช้ภูมิภาคนอกวิธีการจัดกลุ่ม
บางคนใช้เพื่อจัดกลุ่มเขตข้อมูลคุณสมบัติ ฯลฯวิธีการนี้ไม่ถูกต้อง: หากรหัสของคุณสอดคล้องกับ StyleCop ดังนั้นเขตข้อมูลคุณสมบัติวิธีการส่วนตัวผู้สร้าง ฯลฯ จะถูกจัดกลุ่มไว้ด้วยกันและหาได้ง่าย ถ้าไม่ใช่ก็ถึงเวลาที่จะเริ่มคิดเกี่ยวกับการใช้กฎที่สร้างความมั่นใจในความสม่ำเสมอของ codebase ของคุณ
คนอื่นใช้ภูมิภาคเพื่อซ่อนเอนทิตีที่คล้ายกันมากมาย ตัวอย่างเช่นเมื่อคุณมีคลาสที่มีฟิลด์นับร้อย (ซึ่งทำให้โค้ดอย่างน้อย 500 บรรทัดถ้าคุณนับความคิดเห็นและช่องว่าง) คุณอาจถูกล่อลวงให้ใส่ฟิลด์เหล่านั้นในพื้นที่ยุบและลืมมันไป อีกครั้งคุณกำลังทำผิด: ด้วยฟิลด์จำนวนมากในชั้นเรียนคุณควรคิดให้ดีขึ้นเกี่ยวกับการใช้การสืบทอดหรือการแบ่งวัตถุเป็นวัตถุหลายชิ้น
ในที่สุดบางคนก็ถูกล่อลวงให้ใช้พื้นที่เพื่อรวมกลุ่มสิ่งต่าง ๆ ที่เกี่ยวข้องกัน: เหตุการณ์กับตัวแทนหรือวิธีการที่เกี่ยวข้องกับ IO ด้วยวิธีอื่น ๆ ที่เกี่ยวข้องกับ IO ฯลฯ ในกรณีแรกมันจะกลายเป็นความยุ่งเหยิงซึ่งยากต่อการรักษา อ่านและทำความเข้าใจ ในกรณีที่สองการออกแบบที่ดีกว่าน่าจะเป็นการสร้างหลายคลาส
มีการใช้ที่ดีสำหรับภูมิภาคหรือไม่?
ไม่ได้มีการใช้งานแบบดั้งเดิม: รหัสที่สร้างขึ้น ถึงกระนั้นเครื่องมือการสร้างรหัสก็ต้องใช้คลาสบางส่วนแทน หาก C # รองรับภูมิภาคส่วนใหญ่เป็นเพราะมรดกนี้ใช้และเพราะตอนนี้มีคนจำนวนมากใช้พื้นที่ในรหัสของพวกเขามันเป็นไปไม่ได้ที่จะลบพวกเขาโดยไม่ทำลายรหัสฐานที่มีอยู่
goto
คิดเกี่ยวกับมันเป็นเรื่องเกี่ยวกับ ความจริงที่ว่าภาษาหรือ IDE รองรับคุณสมบัติไม่ได้หมายความว่าควรใช้ทุกวัน กฎ StyleCop SA1124 ชัดเจน: คุณไม่ควรใช้ภูมิภาค ไม่เคย
ตัวอย่าง
ขณะนี้ฉันกำลังตรวจสอบรหัสของเพื่อนร่วมงานของฉัน codebase มีพื้นที่จำนวนมากและเป็นตัวอย่างที่สมบูรณ์แบบของวิธีการใช้ภูมิภาคและสาเหตุที่ทำให้รหัสไม่ดี นี่คือตัวอย่างบางส่วน:
สัตว์ประหลาด 4,000 LOC:
ฉันเพิ่งอ่านบางที่ใน Programmers.SE ว่าเมื่อไฟล์มีusing
s มากเกินไป(หลังจากรันคำสั่ง "ลบการใช้งานที่ไม่ได้ใช้") มันเป็นสัญญาณที่ดีที่คลาสภายในไฟล์นี้ทำงานมากเกินไป เช่นเดียวกับขนาดของไฟล์เอง
ในขณะที่ตรวจสอบรหัสฉันเจอไฟล์ 4 ล้าน LOC ปรากฏว่าผู้เขียนรหัสนี้เพียงคัดลอกวางวิธี 15 บรรทัดเดียวกันหลายร้อยครั้งเปลี่ยนชื่อตัวแปรและวิธีการที่เรียกว่าเล็กน้อย regex ง่าย ๆ ที่ได้รับอนุญาตให้ตัดไฟล์จาก 4 000 LOC เป็น 500 LOC เพียงแค่เพิ่มชื่อสามัญเพียงเล็กน้อย ฉันค่อนข้างมั่นใจว่าด้วยการรีแฟคเตอร์ที่ชาญฉลาดยิ่งขึ้นคลาสนี้อาจลดลงเหลือเพียงไม่กี่บรรทัด
จากการใช้ภูมิภาคผู้เขียนสนับสนุนให้เขาเพิกเฉยต่อความจริงที่ว่ารหัสนั้นเป็นไปไม่ได้ที่จะรักษาและเขียนไม่ดีและจะทำซ้ำรหัสอย่างหนักแทนที่จะเป็น refactor
ภูมิภาค“ ทำ A”, ภูมิภาค“ ทำ B”:
อีกตัวอย่างที่ยอดเยี่ยมคือวิธีการเริ่มต้นมอนสเตอร์ซึ่งทำภารกิจที่ 1 จากนั้นก็คือภารกิจที่ 2 จากนั้นก็ที่งานที่ 3 ฯลฯ มีงานห้าหรือหกงานที่เป็นอิสระโดยสิ้นเชิง งานทั้งหมดเหล่านั้นถูกจัดกลุ่มเป็นวิธีการหนึ่งและจัดกลุ่มเป็นภูมิภาค
สิ่งนี้มีข้อดีอย่างหนึ่ง:
- วิธีนี้ค่อนข้างชัดเจนที่จะเข้าใจโดยดูที่ชื่อภูมิภาค สิ่งนี้ถูกกล่าวว่าวิธีการเดียวกันเมื่อได้รับการฟื้นฟูจะมีความชัดเจนเหมือนต้นฉบับ
ในทางกลับกันปัญหามีหลายรายการ:
ไม่ชัดเจนหากมีการพึ่งพาระหว่างภูมิภาค หวังว่าจะไม่มีการใช้ตัวแปรซ้ำ มิฉะนั้นการบำรุงรักษาอาจเป็นฝันร้ายมากยิ่งขึ้น
วิธีนี้แทบจะเป็นไปไม่ได้เลยที่จะทดสอบ คุณจะรู้ได้อย่างง่ายดายว่าวิธีการใดที่ทำยี่สิบสิ่งในเวลานั้นถูกต้องหรือไม่
เขตข้อมูลพื้นที่คุณสมบัติพื้นที่คอนสตรัค:
รหัสที่ตรวจสอบยังมีพื้นที่จำนวนมากจัดกลุ่มเขตข้อมูลทั้งหมดเข้าด้วยกันคุณสมบัติทั้งหมดเข้าด้วยกัน ฯลฯ ปัญหานี้มีปัญหาชัดเจน: การเติบโตของรหัสที่มา
เมื่อคุณเปิดไฟล์และดูรายการฟิลด์จำนวนมากคุณมีแนวโน้มที่จะปรับโครงสร้างคลาสอีกครั้งจากนั้นทำงานกับโค้ด ด้วยภูมิภาคคุณมีนิสัยที่จะยุบเรื่องและลืมมันไป
ปัญหาอีกประการหนึ่งคือถ้าคุณทำทุกที่คุณจะพบว่าตัวเองกำลังสร้างพื้นที่แบบหนึ่งช่วงตึกซึ่งไม่มีเหตุผลใด ๆ นี่เป็นกรณีในรหัสที่ฉันตรวจสอบซึ่งมี#region Constructor
ตัวสร้างหนึ่งจำนวนมาก
สุดท้ายสาขาคุณสมบัติก่อสร้างอื่น ๆแล้วควรจะอยู่ในลำดับ หากพวกเขาอยู่และพวกเขาตรงกับอนุสัญญา (ค่าคงที่เริ่มต้นด้วยตัวอักษรพิมพ์ใหญ่ ฯลฯ ) มันชัดเจนอยู่แล้วว่าที่ประเภทขององค์ประกอบหยุดและเริ่มต้นอื่น ๆ ดังนั้นคุณไม่จำเป็นต้องสร้างภูมิภาคสำหรับที่ชัดเจน