หลักการออกแบบแนวทางปฏิบัติที่ดีที่สุดและรูปแบบการออกแบบสำหรับ C (หรือการเขียนโปรแกรมขั้นตอนโดยทั่วไป)? [ปิด]


92

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

(ฉันเป็นลูกของ 'รุ่นเชิงวัตถุ' และต้องออกแบบโครงการ C ขนาดใหญ่เป็นครั้งแรก)


1
คุณอาจสนใจ anwsers สำหรับคำถามนี้: stackoverflow.com/questions/661307/…
mouviciel

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

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

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

มาตรฐานการเข้ารหัส SEI CERT C มีชุดกฎที่ดีและแนวปฏิบัติที่ดีทั่วไปตลอดจนสิ่งที่คุณควรพยายามหลีกเลี่ยงการใช้
Rami

คำตอบ:


65

การซ่อนข้อมูล - ตามที่ Parnas ดำเนินการ ( Software Fundamentals )

การจัดการส่วนหัวและการมองเห็นอย่างรอบคอบ:

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

    #ifndef HEADER_H_INCLUDED
    #define HEADER_H_INCLUDED
    ...rest of header contents, including other #include lines if necessary
    #endif /* HEADER_H_INCLUDED */
    
  • ออกแบบชุดของฟังก์ชันเพื่อทำงานกับ 'วัตถุ' (โดยปกติจะเป็นโครงสร้าง) - และใช้ฟังก์ชันเหล่านั้นแทนที่จะเจาะเข้าไปรอบ ๆ ด้านในของโครงสร้างในโค้ดที่ใช้มัน คิดว่ามันเป็นการห่อหุ้มด้วยตัวเอง


จุดดีขอบคุณโจนาธาน ประเภทข้อมูลนามธรรมเป็นอีกตัวอย่างที่ดีของการซ่อนข้อมูลโดยแยกการใช้งานและการนำไปใช้อย่างชัดเจน
Dimi

23

คำแนะนำสามประการของฉัน:

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

นี่คือตัวอย่าง:

typedef struct Vector {
  int size;
  int limit;
  int* ints; 
} Vector;

Vector* Vector_new() {
  Vector* res = (Vector*) malloc(sizeof(Vector));
  res->limit = 10;
  res->size = 0;
  res->ints = (int*) malloc(sizeof(int) * res.limit);

  return res;
}


void Vector_destroy(Vector* v) {
  free(v->ints);
  free(v);
}

void Vector_add(Vector* v, int n) {
  if(v->size == v->limit) {
    v->limit = v->limit * 2 + 10;
    v->ints = realloc(v->ints, v->limit);     
  }

  v->ints[v->size] = n;
  ++v->size;
}

int Vector_get(Vector* v, int index) {
  if(index >= 0 && index < v->size)
    return v->ints[index];

  assert false;
}

ขอบคุณ Itay ฉันจะทำตามคำแนะนำของคุณ
Dimi

1
4. อย่าโยนผลลัพธ์ของmalloc.
SS Anne

22

มีหนังสือออนไลน์ที่ดีฟรีชื่อObject-Oriented Programming With ANSI-Cซึ่งครอบคลุมหัวข้อการเขียนโค้ดเชิงวัตถุในภาษา C การค้นหาด้วย Googleสำหรับ "object-oriented C" ยังให้ผลดีอื่น ๆ อีกมากมาย ตัวอย่างและแหล่งข้อมูล

หากโครงการของคุณมีความสำคัญด้านความปลอดภัยMISRA-Cเป็นกฎที่ดี มันมีไว้สำหรับฝัง c เป็นส่วนใหญ่ แต่ก็มีประโยชน์ในด้านอื่น ๆ เช่นกัน

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


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

อ่าความสุขของ C ที่ฝังไว้อย่าลืมว่าคุณต้องประกาศตัวแปรที่ด้านบนสุดของฟังก์ชัน (หรือที่ด้านบนสุดของ{ }บล็อกใด ๆ) คนนั้นมักจะกัดฉันครั้งหรือสองครั้ง:)
เสมอ

6

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

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

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