ขั้นตอนถัดไป

ข้อมูลเบื้องต้นเกี่ยวกับการเขียนโปรแกรมและ C++

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

ดูตัวอย่างที่ 2

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

ตัวอย่างที่ 1: แนวทางการแยกองค์ประกอบเพิ่มเติม

ลองพิจารณาเอาต์พุตต่อไปนี้จากเกมง่ายๆ

Welcome to Artillery.
You are in the middle of a war and being charged by thousands of enemies.
You have one cannon, which you can shoot at any angle.
You only have 10 cannonballs for this target..
Let's begin...

The enemy is 507 feet away!!!
What angle? 25<
You over shot by 445
What angle? 15
You over shot by 114
What angle? 10
You under shot by 82
What angle? 12
You under shot by 2
What angle? 12.01
You hit him!!!
It took you 4 shots.
You have killed 1 enemy.
I see another one, are you ready? (Y/N) n

You killed 1 of the enemy.

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

StartUp(); // This displays the introductory script.
killed = 0;
do {
  killed = Fire(); // Fire() contains the main loop of each round.
  cout << "I see another one, care to shoot again? (Y/N) " << endl;
  cin >> done;
} while (done != 'n');
cout << "You killed " << killed << " of the enemy." << endl;

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

In case you are a little rusty on physics, here are the calculations:

Velocity = 200.0; // initial velocity of 200 ft/sec Gravity = 32.2; // gravity for distance calculation // in_angle is the angle the player has entered, converted to radians. time_in_air = (2.0 * Velocity * sin(in_angle)) / Gravity; distance = round((Velocity * cos(in_angle)) * time_in_air);

เนื่องจากการเรียก cos() และ sin() คุณจะต้องใส่ math.h ด้วย ลอง ที่เขียนโปรแกรมนี้ขึ้น - เป็นแนวทางปฏิบัติที่ดีในการแยกส่วนปัญหา การตรวจสอบ C++ พื้นฐาน อย่าลืมทำงานเพียงอย่างเดียวในแต่ละฟังก์ชัน นี่คือ โปรแกรมที่ซับซ้อนที่สุดที่เราได้เขียนขึ้นมา ดังนั้น อาจใช้เวลาเล็กน้อย ถึงเวลาดำเนินการแล้วนี่คือวิธีแก้ปัญหา

ตัวอย่างที่ 2: ฝึกฝนด้วยตัวชี้

ข้อควรจำ 4 ข้อเมื่อทำงานกับเคอร์เซอร์มีดังนี้
  1. เคอร์เซอร์คือตัวแปรที่เก็บที่อยู่ของหน่วยความจำ ขณะที่โปรแกรมกำลังดำเนินการ ตัวแปรทั้งหมดจะจัดเก็บไว้ในหน่วยความจำ โดยแต่ละตัวจะอยู่ในที่อยู่หรือตำแหน่งที่ไม่ซ้ำกันของตัวเอง ตัวชี้เป็นตัวแปรประเภทพิเศษที่มีที่อยู่หน่วยความจำ มากกว่าค่าข้อมูล เช่นเดียวกับข้อมูลที่ถูกแก้ไขเมื่อใช้ตัวแปรปกติ ค่าของที่อยู่ที่จัดเก็บไว้ในตัวชี้จะถูกปรับเปลี่ยนเป็นตัวแปรตัวชี้ ถูกชักจูง มีตัวอย่างดังต่อไปนี้
    int *intptr; // Declare a pointer that holds the address
                 // of a memory location that can store an integer.
                 // Note the use of * to indicate this is a pointer variable.
    
    intptr = new int; // Allocate memory for the integer.
    *intptr = 5; // Store 5 in the memory address stored in intptr.
          
  2. เรามักจะบอกว่าตัวชี้ "จุด" กับตำแหน่งที่จัดเก็บอยู่ ("ผู้ชี้ตำแหน่ง") ดังนั้น ในตัวอย่างด้านบน intptr จะชี้ไปที่ Pointee 5.

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

    int *ptr; // Declare integer pointer.
    ptr = new int; // Allocate some memory for the integer.
    *ptr = 5; // Dereference to initialize the pointee.
    *ptr = *ptr + 1; // We are dereferencing ptr in order
                     // to add one to the value stored
                     // at the ptr address.
          

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

  3. การกำหนดตัวชี้ระหว่างตัวชี้ 2 ตัวจะทำให้ชี้ไปที่ผู้ชี้ตำแหน่งเดียวกัน ดังนั้นงาน y = x; ทำให้ y ชี้ไปที่จุดเดียวกับ x การกำหนดเคอร์เซอร์ ไม่สัมผัสผู้ใด เพียงแค่เปลี่ยนตัวชี้ 1 ตัวให้มีตำแหน่งเดียวกัน เป็นตัวชี้อื่น หลังจากกำหนดเคอร์เซอร์แล้ว ตัวชี้ 2 ตัวจะ "แชร์" เวลา Pointee 
  4. void main() {
     int* x; // Allocate the pointers x and y
     int* y; // (but not the pointees).
    
     x = new int; // Allocate an int pointee and set x to point to it.
    
     *x = 42; // Dereference x and store 42 in its pointee
    
     *y = 13; // CRASH -- y does not have a pointee yet
    
     y = x; // Pointer assignment sets y to point to x's pointee
    
     *y = 13; // Dereference y to store 13 in its (shared) pointee
    }
      

นี่คือการติดตามโค้ดนี้

1. จัดสรร 2 ตัวชี้ x และ y การจัดสรรตัวชี้จะ ไม่จัดสรรผู้รับคะแนน
2. จัดสรรผู้ให้คะแนนและตั้ง x ให้ชี้ไปที่จุดนั้น
3. ยกเลิกการอ้างอิง x เพื่อจัดเก็บ 42 ในตำแหน่งที่ชี้ นี่เป็นตัวอย่างพื้นฐาน ของการดำเนินการลดระดับ เริ่มต้นที่ x แล้วตามลูกศรเพื่อไปยังรายการที่ชี้ถึง
4. พยายามยกเลิกการอ้างอิง y เพื่อให้เก็บ 13 ในตัวชี้ เกิดข้อขัดข้องเนื่องจาก Y ไม่มีผู้รับคะแนน - ไม่เคยได้รับมอบหมาย
5. กําหนด y = x; เพื่อให้ y ชี้ไปที่จุดของ x ตอนนี้ x และ y ชี้ไปที่ ผู้รับคะแนนคนเดียวกัน พวกเขา "แชร์"
6. พยายามยกเลิกการอ้างอิง y เพื่อให้เก็บ 13 ในตัวชี้ คราวนี้การทำงาน เนื่องจากงานก่อนหน้านี้มีผู้ให้คะแนน

ดังที่คุณเห็น รูปภาพมีประโยชน์อย่างยิ่งในการทำความเข้าใจการใช้ตัวชี้ นี่คือ อีกตัวอย่างหนึ่ง

int my_int = 46; // Declare a normal integer variable.
                 // Set it to equal 46.

// Declare a pointer and make it point to the variable my_int
// by using the address-of operator.
int *my_pointer = &my_int;

cout << my_int << endl; // Displays 46.

*my_pointer = 107; // Derefence and modify the variable.

cout << my_int << endl; // Displays 107.
cout << *my_pointer << endl; // Also 107.

โปรดสังเกตในตัวอย่างนี้ว่า เราไม่เคยจัดสรรหน่วยความจำด้วยฟิลด์ "ใหม่" เราได้ประกาศตัวแปรจำนวนเต็มปกติและจัดการตัวแปรผ่านตัวชี้

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

int *ptr1; // Declare a pointer to int.
ptr1 = new int; // Reserve storage and point to it.

float *ptr2 = new float; // Do it all in one statement.

delete ptr1; // Free the storage.
delete ptr2;

ในตัวอย่างสุดท้ายนี้ เราจะแสดงวิธีใช้ตัวชี้เพื่อส่งต่อค่าด้วยการอ้างอิง ลงในฟังก์ชัน นี่คือวิธีที่เราแก้ไขค่าของตัวแปรภายในฟังก์ชัน

// Passing parameters by reference.
#include <iostream>
using namespace std;

void Duplicate(int& a, int& b, int& c) {
  a *= 2;
  b *= 2;
  c *= 2;
}

int main() {
  int x = 1, y = 3, z = 7;
  Duplicate(x, y, z);
  // The following outputs: x=2, y=6, z=14.
  cout << "x="<< x << ", y="<< y << ", z="<< z;
  return 0;
}

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

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

หากคุณเป็นโปรแกรมเมอร์ระดับ C นี่ถือเป็นก้าวใหม่เลย เราสามารถทำแบบนั้นใน C ได้โดย กำลังประกาศ Duplicate() เป็น Duplicate(int *x), ซึ่งในกรณีนี้คือ x เป็นตัวชี้ไปยัง int จากนั้นเรียก Duplicate() ที่มีอาร์กิวเมนต์ &x (address-of x) และใช้การยกเลิกการอ้างอิงของ x ภายใน ทำสำเนา() (ดูด้านล่าง) แต่ C++ มีวิธีที่ง่ายกว่าในการส่งค่าไปยังฟังก์ชันโดยอ้างอิง แม้ว่าวิธี "C" แบบเก่าจะยังคงใช้งานได้ก็ตาม

void Duplicate(int *a, int *b, int *c) {
  *a *= 2;
  *b *= 2;
  *c *= 2;
}

int main() {
  int x = 1, y = 3, z = 7;
  Duplicate(&x, &y, &z);
  // The following outputs: x=2, y=6, z=14.
  cout << "x=" << x << ", y=" << y << ", z=" << z;
  return 0;
}

การแจ้งที่มีการอ้างอิง C++ เราไม่จำเป็นต้องส่งที่อยู่ของตัวแปร หรือ เราจำเป็นต้องยกเลิกการอ้างอิงตัวแปรภายในฟังก์ชันที่เรียกไหม

เอาต์พุตโปรแกรมต่อไปนี้คืออะไร ลองวาดภาพความทรงจำเพื่อหาคำตอบ

void DoIt(int &foo, int goo);

int main() {
  int *foo, *goo;
  foo = new int;
  *foo = 1;
  goo = new int;
  *goo = 3;
  *foo = *goo + 3;
  foo = goo;
  *goo = 5;
  *foo = *goo + *foo;
  DoIt(*foo, *goo);
  cout << (*foo) << endl;
}

void DoIt(int &foo, int goo) {
  foo = goo + 3;
  goo = foo + 4;
  foo = goo + 3;
  goo = foo;
} 

เรียกใช้โปรแกรมเพื่อดูว่าคุณตอบถูกหรือไม่

ตัวอย่างที่ 3: การส่งค่าโดยการอ้างอิง

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

ตัวอย่างที่ 4: คลาสและออบเจ็กต์

ลองพิจารณาชั้นเรียนต่อไปนี้

// time.cpp, Maggie Johnson
// Description: A simple time class.

#include <iostream>
using namespace std;

class Time {
 private:
  int hours_;
  int minutes_;
  int seconds_;
 public:
  void set(int h, int m, int s) {hours_ = h; minutes_ = m; seconds_ = s; return;}
  void increment();
  void display();
};

void Time::increment() {
  seconds_++;
  minutes_ += seconds_/60;
  hours_ += minutes_/60;
  seconds_ %= 60;
  minutes_ %= 60;
  hours_ %= 24;
  return;
}

void Time::display() {
  cout << (hours_ % 12 ? hours_ % 12:12) << ':'
       << (minutes_ < 10 ? "0" :"") << minutes_ << ':'
       << (seconds_ < 10 ? "0" :"") << seconds_
       << (hours_ < 12 ? " AM" : " PM") << endl;
}

int main() {
  Time timer;
  timer.set(23,59,58);
  for (int i = 0; i < 5; i++) {
    timer.increment();
    timer.display();
    cout << endl;
  }
}

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

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

ความมหัศจรรย์ของวิทยาศาสตร์: วิทยาการคอมพิวเตอร์

การออกกำลังกาย

ในโมดูลแรกของหลักสูตรนี้ เราไม่มีโซลูชันสำหรับแบบฝึกหัดและโครงงาน

โปรดทราบว่าโปรแกรมที่ดี...

... ถูกแยกย่อยออกเป็นฟังก์ชันต่างๆ ในเชิงตรรกะโดยที่ฟังก์ชันใดก็ตาม ทำงานเพียงงานเดียว

... มีโปรแกรมหลักที่อ่านเหมือนเค้าโครงของสิ่งที่โปรแกรมจะทำ

... มีฟังก์ชันที่สื่อความหมาย ชื่อค่าคงที่ และชื่อตัวแปร

... ใช้ค่าคงที่เพื่อหลีกเลี่ยง "เวทมนตร์" ใดๆ ในโปรแกรมได้อีกด้วย

... มีอินเทอร์เฟซผู้ใช้ที่ใช้งานง่าย

การออกกำลังกายอุ่นเครื่อง

  • แบบฝึกหัด 1

    จำนวนเต็ม 36 มีคุณสมบัติที่แปลกใหม่ นั่นคือ เป็นกำลังสองสมบูรณ์ และ ผลรวมของจำนวนเต็มจาก 1 ถึง 8 ตัวเลขถัดไปคือ 1225 ซึ่ง คือ 352 และผลรวมของจำนวนเต็มตั้งแต่ 1 ถึง 49 ค้นหาตัวเลขถัดไปซึ่งเป็นจำนวนเต็มยกกำลัง 2 และเป็นผลรวมของอนุกรม 1...n ตัวเลขถัดไปนี้อาจมากกว่า 32767 คุณอาจใช้ฟังก์ชันไลบรารีที่คุณรู้จัก (หรือสูตรทางคณิตศาสตร์) เพื่อช่วยให้โปรแกรมของคุณทำงานได้เร็วขึ้น หรืออาจเป็น ในการเขียนโปรแกรมนี้โดยใช้ for- Loop เพื่อระบุว่าหมายเลขใดสมบูรณ์หรือไม่ หรือผลรวมของอนุกรม (หมายเหตุ: ขึ้นอยู่กับเครื่องและโปรแกรมที่ใช้ อาจใช้เวลาสักพักจึงจะพบหมายเลขนี้)

  • แบบฝึกหัด 2

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

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

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

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

    ต่อไปนี้คือตัวอย่างเอาต์พุต

    Please enter the book code: 1221
     single copy price: 69.95
     number on hand: 30
     prospective enrollment: 150
     1 for reqd/0 for optional: 1
     1 for new/0 for used: 0
    ***************************************************
    Book: 1221
    Price: $69.95
    Inventory: 30
    Enrollment: 150
    
    This book is required and used.
    ***************************************************
    Need to order: 67
    Total Cost: $4686.65
    ***************************************************
    
    Enter 1 to do another book, 0 to stop. 0
    ***************************************************
    Total for all orders: $4686.65
    Profit: $937.33
    ***************************************************

โปรเจ็กต์ฐานข้อมูล

ในโครงการนี้ เราได้สร้างโปรแกรม C++ ที่มีฟังก์ชันการทำงานเต็มรูปแบบ ซึ่งจะใช้ แอปพลิเคชันฐานข้อมูล

โปรแกรมของเราจะช่วยให้เราจัดการฐานข้อมูลของผู้ประพันธ์เพลงและข้อมูลที่เกี่ยวข้อง เกี่ยวกับพวกเขา ฟีเจอร์ของโปรแกรมมีดังนี้

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

"มี 2 วิธีในการสร้าง การออกแบบซอฟต์แวร์: วิธีหนึ่งก็คือการทำให้ทุกอย่างเรียบง่ายจนเห็นได้ชัดว่า ไม่มีข้อบกพร่อง อีกวิธีหนึ่งคือ ทำให้ซับซ้อน ไม่มีข้อบกพร่องที่เห็นได้ชัด วิธีแรกนั้นยากกว่ามาก" - C.A.R. Hoare

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

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

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

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

ในแผนภาพนี้ เราได้กำหนดออบเจ็กต์ 2 รายการที่เป็นคลาส Apple แต่ละวัตถุจะมีแอตทริบิวต์และการทำงานเหมือนกับคลาส แต่วัตถุ จะระบุแอตทริบิวต์สำหรับประเภทแอปเปิลที่เจาะจง นอกจากนี้ จอแสดงผล การดำเนินการจะแสดงแอตทริบิวต์สำหรับออบเจ็กต์ที่เฉพาะเจาะจง เช่น "เขียว" และ "Sour"

การออกแบบ OO ประกอบด้วยชุดคลาส ข้อมูลที่เชื่อมโยงกับคลาสเหล่านี้ และชุดการดำเนินการที่ชั้นเรียนทำได้ นอกจากนี้ เรายังต้องระบุวิธีที่คลาสต่างๆ โต้ตอบกันด้วย การโต้ตอบนี้ดำเนินการโดยออบเจ็กต์ ของคลาสที่เรียกใช้การดำเนินการของออบเจ็กต์ของคลาสอื่น ตัวอย่างเช่น เราอาจมีคลาส AppleOutputer ที่แสดงสีและรสชาติของอาร์เรย์ออบเจ็กต์ Apple โดยการเรียกใช้เมธอด Display() ของออบเจ็กต์ Apple แต่ละรายการ

ขั้นตอนการออกแบบ OO ที่เราทำมีดังนี้

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

สำหรับระบบขนาดใหญ่ ขั้นตอนเหล่านี้จะเกิดขึ้นซ้ำๆ ในระดับรายละเอียดที่ต่างกัน

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

นอกจากนี้ เรายังต้องการคอลเล็กชันออบเจ็กต์ Composer ด้วย สำหรับกรณีนี้ เรากำหนดคลาสฐานข้อมูล ซึ่งจะจัดการระเบียนแต่ละรายการ ออบเจ็กต์ของคลาสนี้เพิ่มหรือเรียกข้อมูลได้ ออบเจ็กต์คอมโพสเซอร์ และแสดงแต่ละรายการโดยเรียกใช้การดำเนินการแสดงผล ออบเจ็กต์ Composer

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

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

สำหรับฐานข้อมูล Composer ของเรา เรากำหนดคลาส Composer ซึ่งมี ที่เราต้องการจัดเก็บไว้ในผู้แต่งแต่ละคน รวมถึงมีวิธีการจัดการการจัดอันดับและแสดงข้อมูล

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

คลาสอินเทอร์เฟซผู้ใช้ใช้อินเทอร์เฟซที่ขับเคลื่อนด้วยเมนู โดยมีตัวจัดการที่ ในคลาสฐานข้อมูล

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

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

โดยปกติใน C++ นั้น เราจะกำหนดไฟล์ส่วนหัวสำหรับแต่ละคลาส คลาส Composer มีสมาชิกข้อมูลส่วนตัวสำหรับข้อมูลทั้งหมดที่เราต้องการจัดเก็บในคอมโพเซอร์ เราต้องการตัวรับค่า ("เมธอด get") และตัวเปลี่ยนค่า ("เมธอด set") รวมถึงการดำเนินการหลักสำหรับคลาส

// composer.h, Maggie Johnson
// Description: The class for a Composer record.
// The default ranking is 10 which is the lowest possible.
// Notice we use const in C++ instead of #define.
const int kDefaultRanking = 10;

class Composer {
 public:
  // Constructor
  Composer();
  // Here is the destructor which has the same name as the class
  // and is preceded by ~. It is called when an object is destroyed
  // either by deletion, or when the object is on the stack and
  // the method ends.
  ~Composer();

  // Accessors and Mutators
  void set_first_name(string in_first_name);
  string first_name();
  void set_last_name(string in_last_name);
  string last_name();
  void set_composer_yob(int in_composer_yob);
  int composer_yob();
  void set_composer_genre(string in_composer_genre);
  string composer_genre();
  void set_ranking(int in_ranking);
  int ranking();
  void set_fact(string in_fact);
  string fact();

  // Methods
  // This method increases a composer's rank by increment.
  void Promote(int increment);
  // This method decreases a composer's rank by decrement.
  void Demote(int decrement);
  // This method displays all the attributes of a composer.
  void Display();

 private:
  string first_name_;
  string last_name_;
  int composer_yob_; // year of birth
  string composer_genre_; // baroque, classical, romantic, etc.
  string fact_;
  int ranking_;
};

คลาส Database จะเป็นแบบตรงไปตรงมาเช่นกัน

// database.h, Maggie Johnson
// Description: Class for a database of Composer records.
#include  <iostream>
#include "Composer.h"

// Our database holds 100 composers, and no more.
const int kMaxComposers = 100;

class Database {
 public:
  Database();
  ~Database();

  // Add a new composer using operations in the Composer class.
  // For convenience, we return a reference (pointer) to the new record.
  Composer& AddComposer(string in_first_name, string in_last_name,
                        string in_genre, int in_yob, string in_fact);
  // Search for a composer based on last name. Return a reference to the
  // found record.
  Composer& GetComposer(string in_last_name);
  // Display all composers in the database.
  void DisplayAll();
  // Sort database records by rank and then display all.
  void DisplayByRank();

 private:
  // Store the individual records in an array.
  Composer composers_[kMaxComposers];
  // Track the next slot in the array to place a new record.
  int next_slot_;
};

โปรดสังเกตวิธีที่เราสรุปข้อมูลเฉพาะของนักแต่งเพลงไว้ใน เราสามารถใส่ Struct หรือคลาส ไว้ในคลาสฐานข้อมูลเพื่อแสดง ระเบียนคอมโพสเซอร์และเข้าถึงไฟล์ดังกล่าวจากที่นั่นโดยตรง แต่นั่นคงเป็น "under-objectification" กล่าวคือ เราไม่ได้สร้างแบบจำลองด้วยวัตถุมากนัก เท่าที่เราจะทำได้

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

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

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

Composer Database
---------------------------------------------
1) Add a new composer
2) Retrieve a composer's data
3) Promote/demote a composer's rank
4) List all composers
5) List all composers by rank
0) Quit

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

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

// test_composer.cpp, Maggie Johnson
//
// This program tests the Composer class.

#include <iostream>
#include "Composer.h"
using namespace std;

int main()
{
  cout << endl << "Testing the Composer class." << endl << endl;

  Composer composer;

  composer.set_first_name("Ludwig van");
  composer.set_last_name("Beethoven");
  composer.set_composer_yob(1770);
  composer.set_composer_genre("Romantic");
  composer.set_fact("Beethoven was completely deaf during the latter part of "
    "his life - he never heard a performance of his 9th symphony.");
  composer.Promote(2);
  composer.Demote(1);
  composer.Display();
}

เราต้องการโปรแกรมทดสอบที่คล้ายกันสำหรับคลาสฐานข้อมูล

// test_database.cpp, Maggie Johnson
//
// Description: Test driver for a database of Composer records.
#include <iostream>
#include "Database.h"
using namespace std;

int main() {
  Database myDB;

  // Remember that AddComposer returns a reference to the new record.
  Composer& comp1 = myDB.AddComposer("Ludwig van", "Beethoven", "Romantic", 1770,
    "Beethoven was completely deaf during the latter part of his life - he never "
    "heard a performance of his 9th symphony.");
  comp1.Promote(7);

  Composer& comp2 = myDB.AddComposer("Johann Sebastian", "Bach", "Baroque", 1685,
    "Bach had 20 children, several of whom became famous musicians as well.");
  comp2.Promote(5);

  Composer& comp3 = myDB.AddComposer("Wolfgang Amadeus", "Mozart", "Classical", 1756,
    "Mozart feared for his life during his last year - there is some evidence "
    "that he was poisoned.");
  comp3.Promote(2);

  cout << endl << "all Composers: " << endl << endl;
  myDB.DisplayAll();
}

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

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

Composer Database
---------------------------------------------
1) Add a new composer
2) Retrieve a composer's data
3) Promote/demote a composer's rank
4) List all composers
5) List all composers by rank
0) Quit

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

โปรดทราบว่าโค้ดทั้งหมดต้องเป็นไปตามรูปแบบการเขียนโค้ดของเรา ซึ่งเราได้แสดงไว้อีกครั้งที่นี่เพื่อความสะดวก

  • โปรแกรมทุกโปรแกรมที่เราเขียนจะขึ้นต้นด้วยความคิดเห็นส่วนหัว ซึ่งระบุชื่อผู้เขียน ข้อมูลติดต่อ คำอธิบายสั้นๆ และการใช้งาน (หากเกี่ยวข้อง) ฟังก์ชัน/เมธอดทั้งหมดจะเริ่มด้วยความคิดเห็นเกี่ยวกับการดำเนินการและการใช้งาน
  • เราจะเพิ่มความคิดเห็นอธิบายโดยใช้ประโยคที่สมบูรณ์ทุกครั้งที่โค้ดไม่ได้อธิบายถึงตัวมันเอง เช่น ในกรณีที่การประมวลผลมีความซับซ้อน ไม่ชัดเจน น่าสนใจ หรือสำคัญ
  • ใช้ชื่อที่สื่อความหมายเสมอ: ตัวแปรคือคําที่พิมพ์เล็กคั่นด้วย _ เช่น my_variable ชื่อฟังก์ชัน/เมธอดใช้ตัวอักษรตัวพิมพ์ใหญ่เพื่อกำกับคำ เช่น MyExcitingFunction() ค่าคงที่ขึ้นต้นด้วย "k" และใช้ตัวอักษรตัวพิมพ์ใหญ่เพื่อกำกับคำ เช่น kDaysInWeek
  • การเยื้องต้องเพิ่มขึ้นทีละ 2 ระดับ ระดับแรกคือช่องว่าง 2 ช่อง หากต่อไป ต้องมีการเยื้อง เราใช้เว้นวรรค 4 ช่อง เว้นวรรค 6 ช่อง ฯลฯ

ยินดีต้อนรับสู่โลกแห่งความจริง!

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

Makefiles

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

เครื่องมือสร้างแบบโอเพนซอร์สชื่อmake มีการใช้งานกันโดยทั่วไป อ่านข้อมูลเกี่ยวกับเรื่องนี้ได้ในบทความนี้ ดูว่าคุณสามารถสร้างกราฟทรัพยากร Dependency สําหรับแอปพลิเคชันฐานข้อมูล Composer ได้หรือไม่ แล้วแปลงเป็นไฟล์ make นี่คือวิธีแก้ปัญหาของเรา

ระบบจัดการการกำหนดค่า

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

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

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

  1. ปวิชเปิดเอกสารในคอมพิวเตอร์และทำงานในส่วนของตัวเอง
  2. สิตาเปิดเอกสารในคอมพิวเตอร์ทำงานในส่วนของเธอ
  3. อานนท์ทำการเปลี่ยนแปลงเสร็จและบันทึกเอกสารลงในคอมพิวเตอร์จัดเก็บข้อมูล
  4. สิตาดำเนินการเปลี่ยนแปลงและบันทึกเอกสารลงในคอมพิวเตอร์จัดเก็บข้อมูล

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

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

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

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

การจัดการการกำหนดค่าซอฟต์แวร์

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

ระบบ SCM ทั้งหมดมีฟีเจอร์ที่สำคัญดังต่อไปนี้

  • การจัดการการเกิดขึ้นพร้อมกัน
  • การกำหนดเวอร์ชัน
  • การซิงโครไนซ์

เรามาดูรายละเอียดเพิ่มเติมของแต่ละฟีเจอร์กัน

การจัดการการเกิดขึ้นพร้อมกัน

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

ลองพิจารณาตัวอย่างง่ายๆ ในโดเมนวิศวกรรม นั่นคือสมมติว่าเราอนุญาตให้วิศวกร แก้ไขไฟล์เดียวกันพร้อมกันได้ในที่เก็บซอร์สโค้ดส่วนกลาง ทั้ง Client1 และ Client2 ต้องแก้ไขไฟล์พร้อมกัน

  1. Client1 เปิด bar.cpp
  2. Client2 เปิด bar.cpp
  3. Client1 เปลี่ยนแปลงไฟล์และบันทึก
  4. Client2 จะแก้ไขไฟล์และบันทึกไฟล์ที่เขียนทับการเปลี่ยนแปลงของ Client1

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

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

การกำหนดเวอร์ชัน

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

การซิงโครไนซ์

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

การโค่น

Subversion (SVN) เป็นระบบควบคุมเวอร์ชันโอเพนซอร์ส โดยจะมีฟีเจอร์ทั้งหมดที่อธิบายไว้ข้างต้น

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

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

ขั้นตอนแรกคือการติดตั้ง SVN ในระบบ คลิก ที่นี่สำหรับ วิธีทำ ค้นหาระบบปฏิบัติการของคุณและดาวน์โหลดไบนารีที่เหมาะสม

คำศัพท์ SVN บางรายการ

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

เมื่อคุณติดตั้ง SVN แล้ว เราจะมาดูคำสั่งพื้นฐานกัน สิ่งแรกที่ต้องทำคือตั้งค่าที่เก็บในไดเรกทอรีที่ระบุ ต่อไปนี้คือ คำสั่ง:

$ svnadmin create /usr/local/svn/newrepos
$ svn import mytree file:///usr/local/svn/newrepos/project -m "Initial import"
Adding         mytree/foo.c
Adding         mytree/bar.c
Adding         mytree/subdir
Adding         mytree/subdir/foobar.h

Committed revision 1.

คำสั่ง import จะคัดลอกเนื้อหาของไดเรกทอรีไดเรกทอรีไปยัง ในที่เก็บได้อีกด้วย เราสามารถดูไดเรกทอรีใน ที่เก็บที่มีคำสั่ง list

$ svn list file:///usr/local/svn/newrepos/project
bar.c
foo.c
subdir/

การนำเข้าไม่สร้างสำเนาที่ใช้งานได้ โดยต้องใช้คำสั่ง svn checkout การดำเนินการนี้จะสร้างสำเนาโครงสร้างไดเรกทอรีที่ใช้งานได้ มา ลองทำดูเลย

$ svn checkout file:///usr/local/svn/newrepos/project
A    foo.c
A    bar.c
A    subdir
A    subdir/foobar.h
…
Checked out revision 215.

เมื่อคุณมีสำเนาที่ใช้งานได้แล้ว คุณจะทําการเปลี่ยนแปลงไฟล์และไดเรกทอรีในนั้น สำเนาสำหรับทํางานจะเหมือนกับคอลเล็กชันไฟล์และไดเรกทอรีอื่นๆ ทั้งหมด คุณสามารถเพิ่มไฟล์ใหม่หรือแก้ไขไฟล์ ย้ายไฟล์ไปมา หรือแม้แต่ลบสำเนาสำหรับทํางานทั้งหมดได้ โปรดทราบว่าหากคุณคัดลอกและย้ายไฟล์ในสำเนาที่ใช้งานได้ คุณจำเป็นต้องใช้สำเนา SVG และ ย้าย svn แทน คำสั่งของระบบปฏิบัติการ หากต้องการเพิ่มไฟล์ใหม่ ให้ใช้ svn add แล้วลบ ไฟล์ให้ใช้ svn delete หากต้องการแก้ไขอย่างเดียว ให้เปิดไฟล์ด้วยเครื่องมือแก้ไขแล้วเริ่มแก้ไขได้เลย

มีชื่อไดเรกทอรีมาตรฐานบางชื่อที่มักใช้กับ Subversion "ลำต้น" ไดเรกทอรี เป็นสายหลักของการพัฒนาสำหรับโครงการของคุณ "กิ่งก้าน" ไดเรกทอรี เป็น Branch เวอร์ชันที่คุณอาจใช้งานอยู่

$ svn list file:///usr/local/svn/repos
/trunk
/branches

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

A       subdir/new.h      # file is scheduled for addition
D       subdir/old.c        # file is scheduled for deletion
M       bar.c                  # the content in bar.c has local modifications

โปรดทราบว่ามี Flag จำนวนมากในคำสั่งสถานะเพื่อควบคุมเอาต์พุตนี้ หากต้องการดูการเปลี่ยนแปลงที่เฉพาะเจาะจงในไฟล์ที่แก้ไข ให้ใช้ svn diff

$ svn diff bar.c
Index: bar.c
===================================================================
--- bar.c	(revision 5)
+++ bar.c	(working copy)
## -1,18 +1,19 ##
+#include
+#include

 int main(void) {
-  int temp_var;
+ int new_var;
...

สุดท้าย หากต้องการอัปเดตสำเนาการทำงานจากที่เก็บ ให้ใช้คำสั่ง svn update

$ svn update
U  foo.c
U  bar.c
G  subdir/foobar.h
C  subdir/new.h
Updated to revision 2.

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

สำหรับทุกไฟล์ที่มีข้อขัดแย้ง Subversion จะวาง 3 ไฟล์ในการทำงาน ข้อความ:

  • file.min: นี่คือไฟล์ของคุณตามที่มีอยู่ในสำเนาการทำงานก่อนที่คุณจะ อัปเดตสำเนาการทำงานของคุณ
  • file.rOLDREV: นี่คือไฟล์ที่คุณตรวจสอบออกจากที่เก็บก่อนที่จะทําการเปลี่ยนแปลง
  • file.rNEWREV: ไฟล์นี้เป็นเวอร์ชันปัจจุบันในที่เก็บ

คุณจะทำอย่างใดอย่างหนึ่งต่อไปนี้เพื่อแก้ไขความขัดแย้งได้

  • ตรวจสอบไฟล์ต่างๆ และผสานด้วยตนเอง
  • คัดลอกไฟล์ชั่วคราวรายการใดรายการหนึ่งซึ่ง SVN สร้างขึ้นไปยังเวอร์ชันสำเนาที่ใช้งานอยู่
  • เรียกใช้ svn revert เพื่อทิ้งการเปลี่ยนแปลงทั้งหมด

เมื่อแก้ไขความขัดแย้งแล้ว ให้แจ้ง SVN โดยเรียกใช้ svn resolved ซึ่งจะนําไฟล์ชั่วคราว 3 ไฟล์ออก และ SVN จะไม่แสดงไฟล์ในสถานะทับซ้อนอีกต่อไป

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

svn commit -m "Update files to include new headers."  

ยังมีอะไรอีกมากมายให้เรียนรู้เกี่ยวกับ SVN และวิธีรองรับซอฟต์แวร์ขนาดใหญ่ ด้านวิศวกรรม มีแหล่งข้อมูลมากมายบนเว็บ - ก็ใช้ Google ค้นหา "Subversion" นะ

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

ข้อมูลอ้างอิง

หนังสือซับเวอร์ชันออนไลน์

บทความวิกิพีเดียเกี่ยวกับ SVN

เว็บไซต์ Subversion

การประยุกต์ใช้: การศึกษากายวิภาคศาสตร์

ดู eSkeletons จาก University แห่งเท็กซัสที่ออสติน