อย่าใช้“ คงที่” ใน C # หรือ


109

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

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

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

สถาปนิกรายนี้ถูกต้องหรือไม่?

ปรับปรุง: นี่คือตัวอย่าง:

APIManager - คลาสนี้เก็บพจนานุกรมของ API ของบุคคลที่สามที่ฉันกำลังโทรหาพร้อมกับเวลาที่อนุญาตต่อไป มันบังคับใช้ข้อ จำกัด การใช้ API ที่บุคคลที่ 3 จำนวนมากมีอยู่ในเงื่อนไขการให้บริการ ฉันใช้มันทุกที่ที่ฉันกำลังเรียกบริการของบุคคลที่สามโดยเรียก Thread.Sleep (APIManager.GetWait ("ProviderXYZ")); ก่อนโทรออก ทุกอย่างในที่นี้เป็นเธรดที่ปลอดภัยและใช้งานได้ดีกับ TPL ใน C #


37
staticดี static เขตข้อมูลจำเป็นต้องได้รับการปฏิบัติมากอย่างรอบคอบ
Marc Gravell

2
ทำไมเขาถึงเขียนข้อสอบสำหรับห้องสมุดบุคคลที่สาม? คุณไม่ปกติแค่ทดสอบรหัสของคุณเองโดยสมมติว่าผู้สร้างห้องสมุดบุคคลที่สามทำการทดสอบ
Nope

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

8
เห็นได้ชัดว่าเพื่อนร่วมงานของคุณมีอาการที่ร้ายแรง - OOPus erythematosus เป็นระบบเฉียบพลัน พวกเขาต้องการการรักษาโดยเร็วที่สุด!
SK-logic

6
มีส่วนคำตอบสำหรับการให้คำตอบฉันไม่เข้าใจว่าทำไมผู้คนแสดงความคิดเห็นในความพยายามที่จะให้คำตอบ / คำแนะนำแก่ใครบางคน ... มันเอาชนะวัตถุประสงค์ของ StackExchange แน่นอนว่าความคิดเห็นจะขอข้อมูลเพิ่มเติม
peteski

คำตอบ:


121

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


111
แก้ไข. ฟังก์ชั่นคงที่ไม่มีผลข้างเคียงเป็นฟังก์ชั่นที่ทดสอบได้มากที่สุดของทั้งหมด
Robert Harvey

9
+1 กับสิ่งนั้น แต่ด้วยประโยคความปลอดภัย: เกือบทุกอัลกอริธึมนามธรรมเป็นไร้สัญชาติ แต่นั่นไม่ได้หมายความว่าคุณควรใช้มันเป็นคลาสหรือวิธีการแบบไร้สัญชาติ การใช้งานในลักษณะนี้ทำให้รหัสทั้งหมดที่ใช้เชื่อมโยงกับโค้ดนั้นแทบจะไม่ นี่หมายความว่าตัวอย่างเช่นถ้าคุณใช้การเรียงลำดับอัลกอริทึมเป็นวิธีการแบบคงที่และใช้มันผ่านโครงการ - คุณไม่สามารถแทนที่การเรียงลำดับเป็นอัลกอริทึมอื่นชั่วคราวเพื่อใช้ในการทดสอบและเพื่อ จำกัด พื้นที่ปัญหา นี่เป็นอุปสรรคใหญ่ในหลาย ๆ กรณี นอกจากนั้นสถิตคงตกลงทั้งหมดหากใช้อย่างสมเหตุสมผล

11
กล่าวโดยย่อ: มันเป็นฟังก์ชั่นที่ทดสอบได้มากที่สุด แต่ไม่ใช่ necesarilly พวกเขาทำให้โค้ดอื่น ๆ ทดสอบได้มากขึ้น! นี่คือสิ่งที่สถาปนิกอาจหมายถึง

4
@ tereško: ภาษา C # ต้องใช้วิธีการแบบคงที่จะเป็นส่วนหนึ่งของคลาสแบบคงที่ถ้าคุณไม่ต้องการที่จะสร้างตัวอย่างของชั้นเรียนเพื่อเรียกวิธีการ บางทีคุณอาจหมายถึง "อินสแตนซ์" และไม่ใช่ "คลาส"
Robert Harvey

8
@quetzalcoatl: เป็นกฎหมายที่สมบูรณ์แบบในการส่งผู้แทน (เช่นอัลกอริทึมที่แตกต่าง) ไปยังวิธีการคงที่; วิธีการขยายใน Linq เป็นวิธีการคงที่ทั้งหมด ตัวอย่าง: msdn.microsoft.com/en-us/library/…
Robert Harvey

93

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

ฉันใช้สแตติกเมื่อฉันจะไม่สร้างอินสแตนซ์ของคลาสเนื่องจากคลาสเป็นคลาสโกลบอลเดี่ยวที่ใช้ตลอดโค้ด

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

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


3
+1 สำหรับการกล่าวถึงว่าคำสั่งเป็นการทั่วไปและกล่าวถึงวิธีการขยายซึ่งยังสามารถทดสอบได้และการอ้างอิงยังสามารถล้อเลียนเท่าที่ฉันรู้
Nope

4
คงที่เป็นอินสแตนซ์ของคลาสเดียวโดยนัยเป็นซิงเกิลที่จะเกิดความยุ่งเหยิง ....

"ต้องการเข้าถึงฐานข้อมูลหรือไม่" ทำไมจะไม่ล่ะ ? ถ้าตารางอะแดปเตอร์ไม่คงที่แล้วสร้าง class @ ชุดข้อมูลที่พิมพ์อย่างรุนแรงไม่มีอะไรผิดปกติกับมัน .... เว้นแต่คุณจะพิสูจน์จุดของคุณด้วยการอ้างอิงหรือตัวอย่าง?
คณิตศาสตร์

27

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

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

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


7
นี่คือจุดที่ทำให้ฉันหลงทางเช่นกัน คลาสสไตล์ 'ผู้ช่วย' คงที่และไร้เดียงสาน้อย แต่ถ้าคุณมี 25% เต็มของคลาสทั้งหมดของคุณเป็นแบบคงที่ก็ไม่น่าเป็นไปได้ที่ความพยายามของคุณจะถูก จำกัด ในกรณีนี้
Matthew Scharley

2
@MatthewScharley - เขาอาจมี 4 ชั้นเรียนและ 1 ในนั้นคือคง :)
Alexus

1
ระวังถ้าคุณใช้ stibcs ดังกล่าว (เช่น singletons) การสร้างอินสแตนซ์เพิ่มเติมในภายหลังอาจเป็นงานที่ยาก เช่นการสร้างแอปพลิเคชันที่จัดการเอกสารคุณพบปัญหาในภายหลังเมื่อคุณต้องการจัดการเอกสารหลายฉบับ
Michel Keijzers

11

คำตอบที่โพสต์แล้วครอบคลุมคะแนนที่ดีมาก ๆ แต่มีคำตอบที่หายไป:

เขตข้อมูลแบบสแตติกจะไม่ถูกรวบรวมขยะ

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

ฟังก์ชั่นแบบคงที่ไม่ได้เลวร้ายนัก แต่คนอื่น ๆ มีรายละเอียดเพียงพอแล้ว


10

ข้อดีอย่างหนึ่งที่คุณได้รับจาก IoC / DI คือการโต้ตอบระหว่างคลาสส่วนใหญ่จะมีการเจรจาระหว่างอินเตอร์เฟส สิ่งนี้ทำให้การทดสอบหน่วยทำได้ง่ายเนื่องจากอินเทอร์เฟซสามารถถูกเยาะเย้ยโดยอัตโนมัติหรือกึ่งอัตโนมัติและดังนั้นแต่ละส่วนสามารถทดสอบได้สำหรับอินพุตและเอาต์พุต นอกจากนี้นอกเหนือจาก testabilty แล้วการเชื่อมต่อระหว่างทุกสิ่งช่วยให้คุณมีรหัสมอดูลาร์มากที่สุด - คุณสามารถแทนที่การใช้งานเพียงครั้งเดียวโดยไม่ต้องกังวลมากนัก

เนื่องจาก C # ไม่มี metaclasses คลาสจึงไม่สามารถใช้อินเตอร์เฟสโดยใช้คุณลักษณะแบบคงที่ดังนั้นคุณลักษณะแบบคงที่ของคลาสจึงปิดท้ายความพยายามใด ๆ ในการใช้โมเดลวัตถุ IoC / DI บริสุทธิ์ กล่าวคือคุณไม่สามารถสร้างจำลองเพื่อทดสอบพวกเขาและคุณต้องสร้างการอ้างอิงที่แท้จริง

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


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


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

1
คุณและฉันเห็นด้วยกับประเด็นทั่วไปที่คุณควรเลือกเครื่องมือที่เหมาะสมสำหรับงานที่เหมาะสมและไม่ได้ถูกใส่กุญแจมือตามปรัชญา อย่างไรก็ตามหากมีวัฒนธรรมที่จัดตั้งขึ้นอย่างดีในโครงการของคุณบริสุทธิ์ IoC / DI มันจะเจ็บมากเท่ากับการแปลงวิธีการแก้ปัญหาจากบนลงล่างแบบดั้งเดิมเป็น IoC / DI วิศวกรรมจำเป็นต้องพิจารณาต้นทุนการนำส่ง โดยที่ฉันไม่คิดว่าวิธีการขยายค่อนข้างคุณได้มี ฉันคิดว่าทางออกที่ดีกว่าสำหรับพฤติกรรม IoCish กับคลาสแบบสแตติกจะมีหลายคลาสให้เลือกระหว่างเวลาคอมไพล์ด้วยคำสั่ง preprocessor, สไตล์ C
jwrush

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

ใช่คุณถูกต้องถ้ามีวัฒนธรรมที่มีอยู่ในโครงการที่มีอยู่มันจะเป็นอันตรายมากกว่าการช่วยเปลี่ยนวิธีการทำสิ่งต่าง ๆ
Nope

5

คลาสแบบคงที่บางครั้งใช้ในทางที่ผิดและควรเป็น:

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

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


@topomorto เนื่องจากคลาสไม่ควรถูกสร้างอินสแตนซ์โดย Constructor ของมันส่งผลให้หลายวัตถุ / อินสแตนซ์ แต่โดยวิธีพิเศษ (โดยปกติเช่น ()) <ซึ่งจะส่งกลับอินสแตนซ์เดียวกันเสมอ
Michel Keijzers

@topomorto ฉันเพิ่งตรวจสอบและคุณถูกต้องสมบูรณ์มีเพียงสมาชิกบางคนเท่านั้นที่คงที่ (ตัวแปรอินสแตนซ์และฟังก์ชั่นอินสแตนซ์) ฉันจะเปลี่ยนคำตอบของฉันตาม; ขอบคุณที่พูดถึง
Michel Keijzers

4

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


1

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

ตัวอย่าง PHP:

class vendorData {
  private static $data_table = 'vendor_data'; // static property
  private $id; // instance property
  private $name; // instance property

  public function __construct($vendor_id) {
    if(!self::validId($vendor_id) ) { // static method
      return false; // id doesn't exist
    }
    $this->id = $vendor_id; // id has been validated
    $this->getProperties(); // object method
  }

  private static function validId($vendor_id) {
    // send db query to find if the id exists
    // return true/false;
  }

  private function getProperties() { // object method
    $sql = "SELECT * FROM `{self::$data_table}` // using static property
        WHERE `id` = {$this->id}"; // using object instance property
    // get resultset
    foreach($result as $property => $value) {
      $this->$property = $value; // object instance properties all set
    }
  }

  // and here
  public static function getBy($property,$value) { // static method to return object array
    $sql = "SELECT `id` FROM `{self::$data_table}` // using static property
      WHERE `$property` = '$value'";
    // send query, get $ids array
    $obj_array = array();
    foreach($ids as $id) {
      // create array of current class objects using special static keyword
      // meaning of static here is different than in property/method declarations
      $obj_array[$id] = new static($id);
    }
    return $obj_array;
  }
}

1

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


-3

ชั้นเรียนที่มีวิธีการคงที่ใช้หลักการของ OOP ยังไม่ได้เป็น OOP แต่เป็น COP - การเขียนโปรแกรมเชิงระดับ

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

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

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

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

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

สิ่งที่จะใช้แทนวิธีการคงที่? คำแนะนำของฉันคือ OOP ทำไมไม่ลองดูล่ะ?

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