পরবর্তী পদক্ষেপ

প্রোগ্রামিং এবং C++ এর ভূমিকা

এই অনলাইন টিউটোরিয়ালটি আরও উন্নত ধারণার সাথে চলতে থাকে - অনুগ্রহ করে পার্ট III পড়ুন। এই মডিউলে আমাদের ফোকাস পয়েন্টার ব্যবহার করা এবং বস্তুর সাথে শুরু করা হবে।

উদাহরণ #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.

প্রথম পর্যবেক্ষণ হল পরিচায়ক পাঠ্য যা প্রতি প্রোগ্রাম এক্সিকিউশনে একবার প্রদর্শিত হয়। প্রতিটি রাউন্ডের জন্য শত্রু দূরত্ব নির্ধারণ করতে আমাদের একটি এলোমেলো সংখ্যা জেনারেটর প্রয়োজন। প্লেয়ার থেকে অ্যাঙ্গেল ইনপুট পাওয়ার জন্য আমাদের একটি মেকানিজম দরকার এবং এটি স্পষ্টতই একটি লুপ স্ট্রাকচারে কারণ এটি পুনরাবৃত্তি হয় যতক্ষণ না আমরা শত্রুকে আঘাত করি। দূরত্ব এবং কোণ গণনা করার জন্য আমাদের একটি ফাংশন প্রয়োজন। অবশেষে, আমাদের অবশ্যই ট্র্যাক রাখতে হবে শত্রুকে আঘাত করতে কতগুলি শট লেগেছিল, সেইসাথে প্রোগ্রামটি সম্পাদনের সময় আমরা কতজন শত্রুকে আঘাত করেছি। এখানে মূল প্রোগ্রামের জন্য একটি সম্ভাব্য রূপরেখা রয়েছে।

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;

ফায়ার পদ্ধতি গেম খেলা পরিচালনা করে। সেই ফাংশনে, আমরা শত্রু দূরত্ব পেতে একটি র্যান্ডম নম্বর জেনারেটরকে কল করি এবং তারপরে খেলোয়াড়ের ইনপুট পেতে লুপ সেট আপ করি এবং গণনা করি যে তারা শত্রুকে আঘাত করেছে কিনা। লুপের গার্ড শর্ত হল আমরা শত্রুকে আঘাত করার কতটা কাছাকাছি পৌঁছেছি।

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: পয়েন্টার দিয়ে অনুশীলন করুন

পয়েন্টারগুলির সাথে কাজ করার সময় চারটি জিনিস মনে রাখতে হবে:
  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 পয়েন্টী 5 এর দিকে নির্দেশ করে।

    আমাদের পূর্ণসংখ্যা পয়েন্টীর জন্য মেমরি বরাদ্দ করতে "নতুন" অপারেটরের ব্যবহার লক্ষ্য করুন। পয়েন্টী অ্যাক্সেস করার চেষ্টা করার আগে এটি আমাদের অবশ্যই করা উচিত।

    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++ প্রোগ্রামাররা যে সবচেয়ে সাধারণ ত্রুটিগুলি করে তা হল পয়েন্টার শুরু করতে ভুলে যাওয়া। এটি কখনও কখনও রানটাইম ক্র্যাশের কারণ হতে পারে কারণ আমরা মেমরিতে এমন একটি অবস্থান অ্যাক্সেস করছি যেখানে অজানা ডেটা রয়েছে। আমরা যদি এই ডেটাটি পরিবর্তন করার চেষ্টা করি, তাহলে আমরা সূক্ষ্ম মেমরি দুর্নীতির কারণ হতে পারি যা এটিকে ট্র্যাক করা একটি কঠিন বাগ তৈরি করে।

  3. দুটি পয়েন্টারের মধ্যে পয়েন্টার অ্যাসাইনমেন্ট তাদের একই পয়েন্টারের দিকে নির্দেশ করে। তাই অ্যাসাইনমেন্ট y = x; x এর মতো একই পয়েন্টে y পয়েন্ট করে। পয়েন্টার অ্যাসাইনমেন্ট পয়েন্টীকে স্পর্শ করে না। এটি শুধুমাত্র একটি পয়েন্টার পরিবর্তন করে অন্য পয়েন্টার হিসাবে একই অবস্থান আছে। পয়েন্টার অ্যাসাইনমেন্টের পরে, দুটি পয়েন্টার পয়েন্টারকে "ভাগ" করে।
  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. দুটি পয়েন্টার x এবং y বরাদ্দ করুন। পয়েন্টার বরাদ্দ করা কোনো পয়েন্টী বরাদ্দ করে না
2. একটি পয়েন্টী বরাদ্দ করুন এবং এটিকে নির্দেশ করতে x সেট করুন।
3. ডিরেফারেন্স x এর পয়েন্টে 42 সংরক্ষণ করতে। এটি ডিরেফারেন্স অপারেশনের একটি মৌলিক উদাহরণ। x এ শুরু করুন, এর পয়েন্টে অ্যাক্সেস করতে তীরটি অনুসরণ করুন।
4. এর পয়েন্টিতে 13 সংরক্ষণ করতে y ডিরেফারেন্স করার চেষ্টা করুন। এটি ক্র্যাশ হয়েছে কারণ y এর একটি পয়েন্টী নেই -- এটি কখনই বরাদ্দ করা হয়নি।
5. বরাদ্দ করুন y = x; যাতে y x এর পয়েন্টে নির্দেশ করে। এখন x এবং y একই পয়েন্টের দিকে নির্দেশ করে -- তারা "শেয়ারিং" করছে।
6. এর পয়েন্টিতে 13 সংরক্ষণ করতে y ডিরেফারেন্স করার চেষ্টা করুন। এই সময় এটা কাজ করে, কারণ আগের অ্যাসাইনমেন্ট y একটি পয়েন্টী দিয়েছে.

আপনি দেখতে পাচ্ছেন, চিত্রগুলি পয়েন্টার ব্যবহার বোঝার জন্য খুব সহায়ক। এখানে আরেকটি উদাহরণ।

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;
}

আমরা যদি ডুপ্লিকেট ফাংশন সংজ্ঞায় আর্গুমেন্টগুলিকে &'স অফ ছেড়ে দিই, তাহলে আমরা ভেরিয়েবলগুলিকে "মান অনুসারে" পাস করি, অর্থাৎ, ভেরিয়েবলের মান দিয়ে একটি কপি তৈরি করা হয়। ফাংশনে ভেরিয়েবলে করা যেকোনো পরিবর্তন কপি পরিবর্তন করে। তারা মূল পরিবর্তনশীল পরিবর্তন না.

যখন একটি ভেরিয়েবল রেফারেন্স দ্বারা পাস করা হয় তখন আমরা তার মানের একটি অনুলিপি পাস করছি না, আমরা ভেরিয়েবলের ঠিকানাটি ফাংশনে পাঠাচ্ছি। স্থানীয় ভেরিয়েবলে যে কোনো পরিবর্তন আসলে মূল পরিবর্তনশীলকে পরিবর্তন করে।

আপনি যদি একজন সি প্রোগ্রামার হন তবে এটি একটি নতুন মোড়। আমরা সি-তে ডুপ্লিকেট() কে ডুপ্লিকেট(int *x) হিসাবে ঘোষণা করে একই কাজ করতে পারি, এই ক্ষেত্রে x একটি int-এর একটি পয়েন্টার, তারপর ডুপ্লিকেট() কে আর্গুমেন্ট &x (এড্রেস-অফ 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 পর্যন্ত পূর্ণসংখ্যার যোগফল। পরবর্তী সংখ্যা যা একটি নিখুঁত বর্গ এবং একটি সিরিজ 1...n এর যোগফল। এই পরবর্তী সংখ্যাটি 32767-এর থেকে বেশি হতে পারে। আপনার প্রোগ্রামকে দ্রুত চালানোর জন্য আপনি লাইব্রেরি ফাংশনগুলি ব্যবহার করতে পারেন যা আপনি জানেন (বা গাণিতিক সূত্র)। একটি সংখ্যা একটি নিখুঁত বর্গ বা একটি সিরিজের সমষ্টি কিনা তা নির্ধারণ করতে ফর-লুপ ব্যবহার করে এই প্রোগ্রামটি লেখাও সম্ভব। (দ্রষ্টব্য: আপনার মেশিন এবং আপনার প্রোগ্রামের উপর নির্ভর করে, এই নম্বরটি খুঁজে পেতে বেশ সময় লাগতে পারে।)

  • ব্যায়াম 2

    আপনার কলেজের বইয়ের দোকানের আগামী বছরের ব্যবসার অনুমান করতে আপনার সাহায্যের প্রয়োজন। অভিজ্ঞতায় দেখা গেছে যে একটি বই একটি কোর্সের জন্য প্রয়োজনীয় নাকি কেবল ঐচ্ছিক, এবং এটি আগে ক্লাসে ব্যবহার করা হয়েছে কিনা তার উপর বিক্রয় অনেকাংশে নির্ভর করে। একটি নতুন, প্রয়োজনীয় পাঠ্যপুস্তক সম্ভাব্য তালিকাভুক্তির 90%-এর কাছে বিক্রি হবে, কিন্তু যদি এটি আগে ক্লাসে ব্যবহার করা হয়ে থাকে, তবে শুধুমাত্র 65% কিনবে। একইভাবে, সম্ভাব্য তালিকাভুক্তির 40% একটি নতুন, ঐচ্ছিক পাঠ্যপুস্তক কিনবে, কিন্তু যদি এটি ক্লাসে আগে ব্যবহার করা হয় তবে শুধুমাত্র 20% কিনবে। (উল্লেখ্য যে এখানে "ব্যবহৃত" মানে সেকেন্ড-হ্যান্ড বই নয়।)

  • একটি প্রোগ্রাম লিখুন যা ইনপুট হিসাবে বইয়ের একটি সিরিজ গ্রহণ করে (ব্যবহারকারী একজন সেন্টিনেলে প্রবেশ না করা পর্যন্ত)। প্রতিটি বইয়ের জন্য জিজ্ঞাসা করুন: বইটির জন্য একটি কোড, বইটির একক কপি খরচ, হাতে থাকা বইয়ের বর্তমান সংখ্যা, সম্ভাব্য ক্লাস তালিকাভুক্তি, এবং ডেটা যা নির্দেশ করে যে বইটি প্রয়োজনীয়/ঐচ্ছিক, নতুন/অতীতে ব্যবহৃত হয়েছে কিনা। . আউটপুট হিসাবে, কতগুলি বই অর্ডার করতে হবে (যদি থাকে, মনে রাখবেন যে শুধুমাত্র নতুন বই অর্ডার করা হয়েছে), প্রতিটি অর্ডারের মোট খরচ সহ একটি সুন্দর ফর্ম্যাট করা স্ক্রিনে সমস্ত ইনপুট তথ্য দেখান।

    তারপর, সমস্ত ইনপুট সম্পূর্ণ হওয়ার পরে, সমস্ত বইয়ের অর্ডারের মোট খরচ এবং দোকানটি তালিকা মূল্যের 80% প্রদান করলে প্রত্যাশিত লাভ দেখান। যেহেতু আমরা এখনও একটি প্রোগ্রামে আসা ডেটার একটি বৃহৎ সেটের সাথে মোকাবিলা করার কোনো উপায় নিয়ে আলোচনা করিনি (সাথে থাকুন!), শুধু একটি সময়ে একটি বই প্রক্রিয়া করুন এবং সেই বইটির জন্য আউটপুট স্ক্রীন দেখান৷ তারপর, যখন ব্যবহারকারী সমস্ত ডেটা প্রবেশ করা শেষ করে, আপনার প্রোগ্রামটি মোট এবং লাভের মানগুলি আউটপুট করবে।

    আপনি কোড লেখা শুরু করার আগে, এই প্রোগ্রামটির ডিজাইন সম্পর্কে চিন্তা করার জন্য কিছু সময় নিন। ফাংশনগুলির একটি সেটে পচন, এবং একটি প্রধান() ফাংশন তৈরি করুন যা আপনার সমস্যার সমাধানের জন্য একটি রূপরেখার মতো পড়ে। নিশ্চিত করুন যে প্রতিটি ফাংশন একটি কাজ করে।

    এখানে নমুনা আউটপুট আছে:

    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++ প্রোগ্রাম তৈরি করি যা একটি সাধারণ ডাটাবেস অ্যাপ্লিকেশন প্রয়োগ করে।

আমাদের প্রোগ্রাম আমাদেরকে সুরকারদের একটি ডাটাবেস এবং তাদের সম্পর্কে প্রাসঙ্গিক তথ্য পরিচালনা করার অনুমতি দেবে। প্রোগ্রামের বৈশিষ্ট্যগুলির মধ্যে রয়েছে:

  • একটি নতুন সুরকার যোগ করার ক্ষমতা
  • একজন সুরকারকে র‌্যাঙ্ক করার ক্ষমতা (অর্থাৎ, নির্দেশ করে যে আমরা সুরকারের সঙ্গীত কতটা পছন্দ করি বা অপছন্দ করি)
  • ডাটাবেসের সমস্ত কম্পোজার দেখার ক্ষমতা
  • র‌্যাঙ্ক অনুসারে সমস্ত সুরকারকে দেখার ক্ষমতা

"একটি সফ্টওয়্যার ডিজাইন তৈরি করার দুটি উপায় রয়েছে: একটি উপায় হল এটিকে এত সহজ করা যাতে স্পষ্টতই কোনও ঘাটতি থাকে না এবং অন্য উপায়টি হল এটিকে এত জটিল করা যাতে কোনও স্পষ্ট ঘাটতি নেই৷ প্রথম পদ্ধতিটি অনেক বেশি কঠিন৷ " - কার হোয়ারে

আমরা অনেকেই "প্রক্রিয়াগত" পদ্ধতি ব্যবহার করে ডিজাইন এবং কোড করতে শিখেছি। আমরা যে কেন্দ্রীয় প্রশ্নটি দিয়ে শুরু করি তা হল "প্রোগ্রামটি কি করতে হবে?"। আমরা একটি সমস্যার সমাধানকে কার্যগুলিতে বিচ্ছিন্ন করি, যার প্রতিটি সমস্যার একটি অংশ সমাধান করে। এই কাজগুলি আমাদের প্রোগ্রামের ফাংশনগুলির সাথে ম্যাপ করে যাকে ক্রমানুসারে main() বা অন্যান্য ফাংশন থেকে বলা হয়। এই ধাপে ধাপে পদ্ধতি আমাদের সমাধান করতে হবে এমন কিছু সমস্যার জন্য আদর্শ। কিন্তু প্রায়শই নয়, আমাদের প্রোগ্রামগুলি শুধুমাত্র কাজ বা ইভেন্টের রৈখিক ক্রম নয়।

একটি অবজেক্ট-ওরিয়েন্টেড (OO) পদ্ধতির সাথে, আমরা প্রশ্ন দিয়ে শুরু করি "আমি কোন বাস্তব-বিশ্বের বস্তুর মডেলিং করছি?" উপরে বর্ণিত কাজের মধ্যে একটি প্রোগ্রামকে ভাগ করার পরিবর্তে, আমরা এটিকে ভৌত বস্তুর মডেলগুলিতে ভাগ করি। এই ভৌত অবজেক্টগুলির বৈশিষ্ট্যগুলির একটি সেট দ্বারা সংজ্ঞায়িত একটি অবস্থা এবং আচরণ বা ক্রিয়াগুলির একটি সেট যা তারা সম্পাদন করতে পারে। ক্রিয়াগুলি বস্তুর অবস্থা পরিবর্তন করতে পারে, অথবা তারা অন্যান্য বস্তুর ক্রিয়াকলাপকে আহ্বান করতে পারে। মূল ভিত্তি হল যে একটি বস্তু "জানে" কিভাবে নিজে থেকে কাজ করতে হয়।

OO ডিজাইনে, আমরা শ্রেণী এবং বস্তুর পরিপ্রেক্ষিতে ভৌত বস্তুকে সংজ্ঞায়িত করি; বৈশিষ্ট্য এবং আচরণ। একটি OO প্রোগ্রামে সাধারণত প্রচুর পরিমাণে অবজেক্ট থাকে। এই অবজেক্টগুলির অনেকগুলি অবশ্য একই রকম। নিম্নোক্ত বিবেচনা কর.

একটি শ্রেণী হল একটি বস্তুর জন্য সাধারণ বৈশিষ্ট্য এবং আচরণের একটি সেট, যা বাস্তব জগতে শারীরিকভাবে বিদ্যমান থাকতে পারে। উপরের চিত্রে, আমাদের একটি অ্যাপল ক্লাস আছে। সমস্ত আপেল, তাদের প্রকার নির্বিশেষে, রঙ এবং স্বাদ বৈশিষ্ট্য আছে। আমরা এমন একটি আচরণও সংজ্ঞায়িত করেছি যেখানে অ্যাপল তার বৈশিষ্ট্যগুলি প্রদর্শন করে।

এই ডায়াগ্রামে, আমরা দুটি অবজেক্টকে সংজ্ঞায়িত করেছি যা অ্যাপল শ্রেণীর। প্রতিটি বস্তুর ক্লাসের মতো একই বৈশিষ্ট্য এবং ক্রিয়া রয়েছে, তবে বস্তুটি একটি নির্দিষ্ট ধরণের আপেলের বৈশিষ্ট্যগুলিকে সংজ্ঞায়িত করে। এছাড়াও, ডিসপ্লে অ্যাকশন সেই নির্দিষ্ট বস্তুর জন্য বৈশিষ্ট্যগুলি প্রদর্শন করে, যেমন, "সবুজ" এবং "টক"।

একটি OO ডিজাইন ক্লাসের একটি সেট, এই ক্লাসগুলির সাথে সম্পর্কিত ডেটা এবং ক্লাসগুলি সম্পাদন করতে পারে এমন ক্রিয়াগুলির সেট নিয়ে গঠিত। আমাদের বিভিন্ন শ্রেণীর যোগাযোগের উপায়গুলিও সনাক্ত করতে হবে। এই মিথস্ক্রিয়াটি একটি শ্রেণীর বস্তু দ্বারা সঞ্চালিত হতে পারে যা অন্যান্য শ্রেণীর বস্তুর ক্রিয়াকে আহ্বান করে। উদাহরণস্বরূপ, আমাদের একটি AppleOutputer ক্লাস থাকতে পারে যা প্রতিটি অ্যাপল অবজেক্টের Display() পদ্ধতিতে কল করে অ্যাপল অবজেক্টের একটি অ্যারের রঙ এবং স্বাদ আউটপুট করে।

OO ডিজাইন করার জন্য আমরা যে পদক্ষেপগুলি সম্পাদন করি তা এখানে রয়েছে:

  1. ক্লাসগুলি চিহ্নিত করুন এবং সাধারণভাবে সংজ্ঞায়িত করুন যে প্রতিটি ক্লাসের একটি বস্তু ডেটা হিসাবে কী সংরক্ষণ করে এবং একটি বস্তু কী করতে পারে।
  2. প্রতিটি শ্রেণীর ডেটা উপাদান সংজ্ঞায়িত করুন
  3. প্রতিটি শ্রেণীর ক্রিয়া সংজ্ঞায়িত করুন এবং কিভাবে একটি শ্রেণীর কিছু ক্রিয়া অন্যান্য সম্পর্কিত শ্রেণীর ক্রিয়া ব্যবহার করে বাস্তবায়ন করা যেতে পারে।

একটি বৃহৎ সিস্টেমের জন্য, এই পদক্ষেপগুলি পুনরাবৃত্তভাবে বিস্তারিত বিভিন্ন স্তরে ঘটে।

কম্পোজার ডাটাবেস সিস্টেমের জন্য, আমাদের একটি কম্পোজার ক্লাস দরকার যা আমরা একটি পৃথক কম্পোজারে যে সমস্ত ডেটা সংরক্ষণ করতে চাই তা এনক্যাপসুলেট করে। এই শ্রেণীর একটি অবজেক্ট নিজেকে উন্নীত করতে বা অবনমিত করতে পারে (এর পদমর্যাদা পরিবর্তন করতে পারে), এবং তার বৈশিষ্ট্যগুলি প্রদর্শন করতে পারে।

আমাদের কম্পোজার অবজেক্টের একটি সংগ্রহও দরকার। এর জন্য, আমরা একটি ডেটাবেস ক্লাস সংজ্ঞায়িত করি যা পৃথক রেকর্ড পরিচালনা করে। এই শ্রেণীর একটি অবজেক্ট কম্পোজার অবজেক্ট যোগ বা পুনরুদ্ধার করতে পারে এবং একটি কম্পোজার অবজেক্টের ডিসপ্লে অ্যাকশনের মাধ্যমে আলাদা আলাদা প্রদর্শন করতে পারে।

অবশেষে, ডাটাবেসে ইন্টারেক্টিভ ক্রিয়াকলাপ প্রদানের জন্য আমাদের কিছু ধরণের ব্যবহারকারী ইন্টারফেস প্রয়োজন। এটি একটি স্থানধারক শ্রেণী, অর্থাৎ, আমরা সত্যিই জানি না যে ইউজার ইন্টারফেসটি দেখতে কেমন হবে, তবে আমরা জানি আমাদের একটি প্রয়োজন হবে। হয়তো এটা গ্রাফিক্যাল হবে, হয়তো টেক্সট-ভিত্তিক। আপাতত, আমরা একটি স্থানধারক সংজ্ঞায়িত করি যা আমরা পরে পূরণ করতে পারি।

এখন যেহেতু আমরা কম্পোজার ডাটাবেস অ্যাপ্লিকেশনের জন্য ক্লাসগুলি চিহ্নিত করেছি, পরবর্তী ধাপ হল ক্লাসগুলির জন্য বৈশিষ্ট্য এবং ক্রিয়াগুলি সংজ্ঞায়িত করা। আরও জটিল অ্যাপ্লিকেশনে, আমরা ক্লাসের শ্রেণিবিন্যাসের মানচিত্র এবং কীভাবে বস্তুগুলি ইন্টারঅ্যাক্ট করে তা বের করতে পেন্সিল এবং কাগজ বা UML বা CRC কার্ড বা OOD নিয়ে বসব।

আমাদের কম্পোজার ডাটাবেসের জন্য, আমরা একটি কম্পোজার ক্লাস সংজ্ঞায়িত করি যাতে প্রাসঙ্গিক ডেটা থাকে যা আমরা প্রতিটি সুরকারে সংরক্ষণ করতে চাই। এটিতে র‌্যাঙ্কিং ম্যানিপুলেট করার এবং ডেটা প্রদর্শনের পদ্ধতিও রয়েছে।

কম্পোজার অবজেক্ট ধারণ করার জন্য ডেটাবেস ক্লাসের কিছু ধরণের কাঠামো প্রয়োজন। আমাদের কাঠামোতে একটি নতুন কম্পোজার অবজেক্ট যোগ করতে হবে, সেইসাথে একটি নির্দিষ্ট কম্পোজার অবজেক্ট পুনরুদ্ধার করতে হবে। আমরা এন্ট্রির ক্রম অনুসারে বা র‌্যাঙ্কিং অনুসারে সমস্ত বস্তু প্রদর্শন করতে চাই।

ইউজার ইন্টারফেস ক্লাস একটি মেনু-চালিত ইন্টারফেস প্রয়োগ করে, হ্যান্ডলারদের সাথে যা ডাটাবেস ক্লাসে অ্যাকশন কল করে।

যদি ক্লাসগুলি সহজে বোঝা যায় এবং তাদের বৈশিষ্ট্য এবং ক্রিয়াগুলি স্পষ্ট হয়, যেমন কম্পোজার অ্যাপ্লিকেশনে, ক্লাসগুলি ডিজাইন করা তুলনামূলকভাবে সহজ। কিন্তু যদি আপনার মনে কোন প্রশ্ন থাকে যে কিভাবে ক্লাসগুলি সম্পর্কযুক্ত এবং ইন্টারঅ্যাক্ট করে, প্রথমে এটি আঁকুন এবং কোড শুরু করার আগে বিস্তারিতভাবে কাজ করুন।

একবার আমাদের ডিজাইনের একটি পরিষ্কার ছবি পাওয়া গেলে এবং এটি মূল্যায়ন করা হয়ে গেলে (শীঘ্রই এই বিষয়ে আরও), আমরা প্রতিটি ক্লাসের জন্য ইন্টারফেস সংজ্ঞায়িত করব। আমরা এই মুহুর্তে বাস্তবায়নের বিশদ সম্পর্কে চিন্তা করি না - শুধুমাত্র বৈশিষ্ট্যগুলি এবং ক্রিয়াগুলি কী এবং একটি শ্রেণির অবস্থা এবং ক্রিয়াগুলির কোন অংশগুলি অন্যান্য শ্রেণীর জন্য উপলব্ধ।

C++ এ, আমরা সাধারণত প্রতিটি ক্লাসের জন্য একটি হেডার ফাইল নির্ধারণ করে এটি করি। আমরা একজন কম্পোজারে যে সমস্ত ডেটা সঞ্চয় করতে চাই তার জন্য কম্পোজার ক্লাসে ব্যক্তিগত ডেটা সদস্য রয়েছে। আমাদের অ্যাক্সেসর ("পান" পদ্ধতি) এবং মিউটেটর ("সেট" পদ্ধতি), সেইসাথে ক্লাসের জন্য প্রাথমিক ক্রিয়াগুলির প্রয়োজন৷

// 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.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_;
};

লক্ষ্য করুন কিভাবে আমরা একটি পৃথক ক্লাসে সুরকার-নির্দিষ্ট ডেটা সাবধানে এনক্যাপসুলেট করেছি। আমরা কম্পোজার রেকর্ডের প্রতিনিধিত্ব করার জন্য ডেটাবেস ক্লাসে একটি স্ট্রাকট বা ক্লাস রাখতে পারতাম এবং সরাসরি সেখানে অ্যাক্সেস করতে পারতাম। কিন্তু সেটা হবে "আন্ডার-অবজেক্টিফিকেশন", অর্থাৎ, আমরা যতটা সম্ভব বস্তুর সাথে মডেলিং করছি না।

আপনি কম্পোজার এবং ডাটাবেস ক্লাস বাস্তবায়নের কাজ শুরু করার সাথে সাথে দেখতে পাবেন যে একটি আলাদা কম্পোজার ক্লাস থাকা অনেক বেশি পরিষ্কার। বিশেষ করে, একটি কম্পোজার অবজেক্টে পৃথক পারমাণবিক ক্রিয়াকলাপগুলি ডাটাবেস ক্লাসে Display() পদ্ধতির বাস্তবায়নকে ব্যাপকভাবে সহজ করে তোলে।

অবশ্যই, "ওভার-অবজেক্টিফিকেশন" এর মতো একটি জিনিসও রয়েছে যেখানে আমরা চেষ্টা করি এবং সবকিছুকে একটি শ্রেণীতে পরিণত করি, বা আমাদের প্রয়োজনের চেয়ে বেশি ক্লাস রয়েছে। সঠিক ভারসাম্য খুঁজে পেতে অনুশীলন লাগে এবং আপনি দেখতে পাবেন যে পৃথক প্রোগ্রামারদের ভিন্ন মতামত থাকবে।

আপনি যদি অতিরিক্ত- বা কম-আপত্তিকর তা নির্ধারণ করা প্রায়শই আপনার ক্লাসগুলি যত্ন সহকারে ডায়াগ্রাম করে সাজানো যেতে পারে। আগেই উল্লেখ করা হয়েছে, আপনি কোডিং শুরু করার আগে একটি ক্লাস ডিজাইন তৈরি করা গুরুত্বপূর্ণ এবং এটি আপনাকে আপনার পদ্ধতির বিশ্লেষণে সহায়তা করতে পারে। এই উদ্দেশ্যে ব্যবহৃত একটি সাধারণ স্বরলিপি হল UML (ইউনিফাইড মডেলিং ল্যাঙ্গুয়েজ) এখন যেহেতু আমাদের কাছে কম্পোজার এবং ডাটাবেস অবজেক্টের জন্য সংজ্ঞায়িত ক্লাস আছে, আমাদের একটি ইন্টারফেস দরকার যা ব্যবহারকারীকে ডাটাবেসের সাথে ইন্টারঅ্যাক্ট করতে দেয়। একটি সাধারণ মেনু কৌশলটি করবে:

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++ প্রোগ্রামের সবকিছু একটি ক্লাস হতে হবে না। প্রকৃতপক্ষে, এই মেনু প্রোগ্রামের মতো যদি প্রক্রিয়াকরণটি অনুক্রমিক বা টাস্ক-ভিত্তিক হয়, তবে এটি প্রক্রিয়াগতভাবে প্রয়োগ করা ভাল। এটি এমনভাবে বাস্তবায়ন করা গুরুত্বপূর্ণ যে এটি একটি "প্লেসহোল্ডার" থেকে যায়, অর্থাৎ, যদি আমরা কোনও সময়ে একটি গ্রাফিকাল ইউজার ইন্টারফেস তৈরি করতে চাই, তবে আমাদের সিস্টেমে ইউজার ইন্টারফেস ছাড়া অন্য কিছু পরিবর্তন করতে হবে না।

আমাদের অ্যাপ্লিকেশনটি সম্পূর্ণ করার জন্য শেষ জিনিসটি হল ক্লাস পরীক্ষা করার জন্য একটি প্রোগ্রাম। কম্পোজার ক্লাসের জন্য, আমরা একটি main() প্রোগ্রাম চাই যা ইনপুট নেয়, একটি কম্পোজার অবজেক্ট পপুলেট করে এবং তারপর ক্লাসটি সঠিকভাবে কাজ করছে কিনা তা নিশ্চিত করতে এটি প্রদর্শন করে। আমরা কম্পোজার ক্লাসের সমস্ত পদ্ধতিকেও কল করতে চাই।

// 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();
}

মনে রাখবেন যে এই সাধারণ পরীক্ষা প্রোগ্রামগুলি একটি ভাল প্রথম পদক্ষেপ, কিন্তু প্রোগ্রামটি সঠিকভাবে কাজ করছে তা নিশ্চিত করার জন্য আমাদের আউটপুটটি ম্যানুয়ালি পরিদর্শন করতে হবে। একটি সিস্টেম বড় হওয়ার সাথে সাথে আউটপুটের ম্যানুয়াল পরিদর্শন দ্রুত অব্যবহারিক হয়ে ওঠে। পরবর্তী পাঠে, আমরা ইউনিট পরীক্ষার আকারে স্ব-পরীক্ষামূলক পরীক্ষার প্রোগ্রামগুলি প্রবর্তন করব।

আমাদের আবেদনের নকশা এখন সম্পূর্ণ। পরবর্তী ধাপ হল ক্লাস এবং ইউজার ইন্টারফেসের জন্য .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

ইউজার ইন্টারফেস বাস্তবায়ন করতে ডেটাবেস ক্লাসে আপনার সংজ্ঞায়িত পদ্ধতিগুলি ব্যবহার করুন। আপনার পদ্ধতি ত্রুটি-প্রমাণ করুন. উদাহরণস্বরূপ, একটি র‌্যাঙ্কিং সর্বদা 1-10-এর মধ্যে হওয়া উচিত। কাউকে 101 কম্পোজার যোগ করতে দেবেন না, যদি না আপনি ডেটাবেস ক্লাসে ডেটা কাঠামো পরিবর্তন করার পরিকল্পনা করেন।

মনে রাখবেন - আপনার সমস্ত কোড আমাদের কোডিং নিয়মাবলী অনুসরণ করতে হবে, যা আপনার সুবিধার জন্য এখানে পুনরাবৃত্তি করা হয়েছে:

  • আমরা লিখি প্রতিটি প্রোগ্রাম একটি শিরোনাম মন্তব্য দিয়ে শুরু হয়, লেখকের নাম, তাদের যোগাযোগের তথ্য, একটি সংক্ষিপ্ত বিবরণ এবং ব্যবহার (যদি প্রাসঙ্গিক হয়) প্রদান করে। প্রতিটি ফাংশন/পদ্ধতি অপারেশন এবং ব্যবহারের উপর একটি মন্তব্য দিয়ে শুরু হয়।
  • আমরা পূর্ণ বাক্য ব্যবহার করে ব্যাখ্যামূলক মন্তব্য যোগ করি, যখনই কোডটি নিজেই নথিভুক্ত করে না, উদাহরণস্বরূপ, যদি প্রক্রিয়াকরণটি জটিল, অ-স্পষ্ট, আকর্ষণীয় বা গুরুত্বপূর্ণ হয়।
  • সর্বদা বর্ণনামূলক নাম ব্যবহার করুন: ভেরিয়েবল হল _ দ্বারা পৃথক করা ছোট হাতের শব্দ, যেমন my_variable-এ। ফাংশন/পদ্ধতির নামগুলি শব্দগুলি চিহ্নিত করতে বড় হাতের অক্ষর ব্যবহার করে, যেমন MyExcitingFunction()। ধ্রুবকগুলি একটি "k" দিয়ে শুরু হয় এবং kDaysInWeek-এর মতো শব্দগুলি চিহ্নিত করতে বড় হাতের অক্ষর ব্যবহার করে।
  • ইন্ডেন্টেশন দুইটির গুণে। প্রথম স্তর দুটি স্পেস; যদি আরও ইন্ডেন্টেশন প্রয়োজন হয়, আমরা চারটি স্পেস, ছয়টি স্পেস ইত্যাদি ব্যবহার করি।

বাস্তব জগতে স্বাগতম!

এই মডিউলে, আমরা বেশিরভাগ সফ্টওয়্যার ইঞ্জিনিয়ারিং সংস্থাগুলিতে ব্যবহৃত দুটি অত্যন্ত গুরুত্বপূর্ণ সরঞ্জাম উপস্থাপন করি। প্রথমটি একটি বিল্ড টুল, এবং দ্বিতীয়টি একটি কনফিগারেশন ম্যানেজমেন্ট সিস্টেম। এই দুটি টুলই ইন্ডাস্ট্রিয়াল সফটওয়্যার ইঞ্জিনিয়ারিংয়ে অপরিহার্য, যেখানে অনেক প্রকৌশলী প্রায়ই একটি বড় সিস্টেমে কাজ করে। এই টুলগুলি কোড বেসে পরিবর্তনগুলি সমন্বয় ও নিয়ন্ত্রণ করতে সাহায্য করে এবং অনেকগুলি প্রোগ্রাম এবং হেডার ফাইল থেকে একটি সিস্টেমকে কম্পাইল এবং লিঙ্ক করার জন্য একটি কার্যকর উপায় প্রদান করে।

মেকফাইলস

একটি প্রোগ্রাম তৈরির প্রক্রিয়া সাধারণত একটি বিল্ড টুল দিয়ে পরিচালিত হয়, যা সঠিক ক্রমে প্রয়োজনীয় ফাইলগুলিকে সংকলন করে এবং লিঙ্ক করে। প্রায়শই, C++ ফাইলের নির্ভরতা থাকে, উদাহরণস্বরূপ, একটি প্রোগ্রামে বলা ফাংশন অন্য প্রোগ্রামে থাকে। অথবা, সম্ভবত বিভিন্ন .cpp ফাইলের জন্য একটি হেডার ফাইলের প্রয়োজন হয়। একটি বিল্ড টুল এই নির্ভরতা থেকে সঠিক কম্পাইল অর্ডার বের করে। এটি শুধুমাত্র শেষ বিল্ড থেকে পরিবর্তিত ফাইলগুলিকে কম্পাইল করবে। এটি কয়েকশ বা হাজার হাজার ফাইল সমন্বিত সিস্টেমে অনেক সময় বাঁচাতে পারে।

মেক নামে একটি ওপেন সোর্স বিল্ড টুল সাধারণত ব্যবহৃত হয়। এটি সম্পর্কে জানতে, এই নিবন্ধটি পড়ুন। আপনি কম্পোজার ডাটাবেস অ্যাপ্লিকেশনের জন্য একটি নির্ভরতা গ্রাফ তৈরি করতে পারেন কিনা দেখুন, এবং তারপর এটি একটি মেকফাইলে অনুবাদ করুন। এখানে আমাদের সমাধান.

কনফিগারেশন ম্যানেজমেন্ট সিস্টেম

শিল্প সফটওয়্যার ইঞ্জিনিয়ারিং-এ ব্যবহৃত দ্বিতীয় টুল হল কনফিগারেশন ম্যানেজমেন্ট (CM)। এটি পরিবর্তন পরিচালনা করতে ব্যবহৃত হয়। ধরা যাক বব এবং সুসান উভয়ই প্রযুক্তি লেখক এবং উভয়েই একটি প্রযুক্তিগত ম্যানুয়াল আপডেটের জন্য কাজ করছেন। একটি মিটিং চলাকালীন, তাদের ম্যানেজার তাদের প্রত্যেককে একই নথির একটি বিভাগ আপডেট করার জন্য বরাদ্দ করে।

প্রযুক্তিগত ম্যানুয়ালটি এমন একটি কম্পিউটারে সংরক্ষণ করা হয় যা বব এবং সুসান উভয়েই অ্যাক্সেস করতে পারে। কোনো সিএম টুল বা প্রক্রিয়া না থাকলে বেশ কিছু সমস্যা দেখা দিতে পারে। একটি সম্ভাব্য দৃশ্য হল নথি সংরক্ষণ করা কম্পিউটার সেট আপ করা যেতে পারে যাতে বব এবং সুসান উভয়ই একই সময়ে ম্যানুয়ালটিতে কাজ করতে না পারে। এই তাদের যথেষ্ট ধীর হবে.

একটি আরও বিপজ্জনক পরিস্থিতি দেখা দেয় যখন স্টোরেজ কম্পিউটার একই সময়ে বব এবং সুসান উভয়ের দ্বারা নথিটি খোলার অনুমতি দেয়। এখানে যা ঘটতে পারে:

  1. বব তার কম্পিউটারে নথিটি খোলে এবং তার বিভাগে কাজ করে।
  2. সুসান তার কম্পিউটারে ডকুমেন্টটি খোলে এবং তার বিভাগে কাজ করে।
  3. বব তার পরিবর্তনগুলি সম্পূর্ণ করে এবং স্টোরেজ কম্পিউটারে নথিটি সংরক্ষণ করে৷
  4. সুসান তার পরিবর্তনগুলি সম্পূর্ণ করে এবং স্টোরেজ কম্পিউটারে নথিটি সংরক্ষণ করে৷

এই দৃষ্টান্তটি দেখায় যে সমস্যাটি ঘটতে পারে যদি প্রযুক্তিগত ম্যানুয়ালটির একক অনুলিপিতে কোনো নিয়ন্ত্রণ না থাকে। যখন সুসান তার পরিবর্তনগুলি সংরক্ষণ করে, তখন সে ববের করা পরিবর্তনগুলিকে ওভাররাইট করে।

এটি ঠিক এমন পরিস্থিতি যা একটি সিএম সিস্টেম নিয়ন্ত্রণ করতে পারে। একটি সিএম সিস্টেমের সাথে, বব এবং সুসান উভয়েই তাদের প্রযুক্তিগত ম্যানুয়ালটির নিজস্ব কপি "চেক আউট" করে এবং সেগুলিতে কাজ করে৷ যখন বব তার পরিবর্তনগুলি আবার চেক করে, সিস্টেম জানে যে সুসানের নিজের কপি চেক আউট করা হয়েছে৷ যখন সুসান তার অনুলিপি চেক করে, সিস্টেমটি বব এবং সুসান উভয়েরই করা পরিবর্তনগুলি বিশ্লেষণ করে এবং একটি নতুন সংস্করণ তৈরি করে যা দুটি পরিবর্তনের সেটকে একত্রিত করে।

সিএম সিস্টেমে উপরে বর্ণিত সমসাময়িক পরিবর্তনগুলি পরিচালনার বাইরেও বেশ কয়েকটি বৈশিষ্ট্য রয়েছে। অনেক সিস্টেম একটি নথির সমস্ত সংস্করণের সংরক্ষণাগার সংরক্ষণ করে, প্রথমবার এটি তৈরি করা হয়েছিল। একটি প্রযুক্তিগত ম্যানুয়ালের ক্ষেত্রে, এটি খুব সহায়ক হতে পারে যখন একজন ব্যবহারকারীর কাছে ম্যানুয়ালটির একটি পুরানো সংস্করণ থাকে এবং একজন প্রযুক্তি লেখককে প্রশ্ন জিজ্ঞাসা করে। একটি সিএম সিস্টেম প্রযুক্তি লেখককে পুরানো সংস্করণ অ্যাক্সেস করার অনুমতি দেবে এবং ব্যবহারকারী কী দেখছে তা দেখতে সক্ষম হবে।

সিএম সিস্টেমগুলি সফ্টওয়্যারে করা পরিবর্তনগুলি নিয়ন্ত্রণ করতে বিশেষভাবে কার্যকর। এই ধরনের সিস্টেমকে বলা হয় সফটওয়্যার কনফিগারেশন ম্যানেজমেন্ট (এসসিএম) সিস্টেম। আপনি যদি একটি বৃহৎ সফ্টওয়্যার ইঞ্জিনিয়ারিং প্রতিষ্ঠানে বিপুল সংখ্যক পৃথক সোর্স কোড ফাইল এবং বিপুল সংখ্যক প্রকৌশলী যাদের সেগুলিতে পরিবর্তন আনতে হবে বিবেচনা করেন তবে এটি স্পষ্ট যে একটি SCM সিস্টেম গুরুত্বপূর্ণ।

সফটওয়্যার কনফিগারেশন ম্যানেজমেন্ট

SCM সিস্টেমগুলি একটি সাধারণ ধারণার উপর ভিত্তি করে: আপনার ফাইলগুলির নির্দিষ্ট অনুলিপিগুলি একটি কেন্দ্রীয় সংগ্রহস্থলে রাখা হয়। লোকেরা সংগ্রহস্থল থেকে ফাইলগুলির অনুলিপিগুলি পরীক্ষা করে, সেই অনুলিপিগুলিতে কাজ করে এবং তারপরে সেগুলি শেষ হয়ে গেলে সেগুলি আবার পরীক্ষা করে। SCM সিস্টেমগুলি একটি একক মাস্টার সেটের বিপরীতে একাধিক লোকের দ্বারা সংশোধন পরিচালনা এবং ট্র্যাক করে।

সমস্ত SCM সিস্টেম নিম্নলিখিত প্রয়োজনীয় বৈশিষ্ট্যগুলি প্রদান করে:

  • কনকারেন্সি ম্যানেজমেন্ট
  • সংস্করণ করা
  • সিঙ্ক্রোনাইজেশন

আসুন আরো বিস্তারিতভাবে এই বৈশিষ্ট্য প্রতিটি তাকান.

কনকারেন্সি ম্যানেজমেন্ট

একযোগে একাধিক ব্যক্তির দ্বারা একটি ফাইলের একযোগে সম্পাদনাকে বোঝায়। একটি বড় সংগ্রহস্থলের সাথে, আমরা চাই যে লোকেরা এটি করতে সক্ষম হোক, তবে এটি কিছু সমস্যার কারণ হতে পারে।

ইঞ্জিনিয়ারিং ডোমেনের একটি সাধারণ উদাহরণ বিবেচনা করুন: ধরুন আমরা প্রকৌশলীদের একই ফাইল একই সাথে সোর্স কোডের কেন্দ্রীয় সংগ্রহস্থলে পরিবর্তন করার অনুমতি দিই। ক্লায়েন্ট 1 এবং ক্লায়েন্ট 2 উভয়কেই একই সময়ে একটি ফাইলে পরিবর্তন করতে হবে:

  1. ক্লায়েন্ট1 bar.cpp খোলে।
  2. ক্লায়েন্ট2 bar.cpp খোলে।
  3. ক্লায়েন্ট 1 ফাইল পরিবর্তন করে এবং এটি সংরক্ষণ করে।
  4. Client2 ফাইল পরিবর্তন করে এবং Client1 এর পরিবর্তন ওভাররাইট করে সংরক্ষণ করে।

স্পষ্টতই, আমরা এটি ঘটতে চাই না। এমনকি যদি আমরা দুই প্রকৌশলীকে একটি মাস্টার সেটে সরাসরি না করে আলাদা কপিতে কাজ করে পরিস্থিতি নিয়ন্ত্রণ করি (নিচের চিত্রের মতো), তবে কপিগুলিকে কোনো না কোনোভাবে মিটমাট করতে হবে। বেশিরভাগ এসসিএম সিস্টেম একাধিক প্রকৌশলীকে একটি ফাইল চেক আউট ("সিঙ্ক" বা "আপডেট") এবং প্রয়োজন অনুসারে পরিবর্তন করার অনুমতি দিয়ে এই সমস্যাটি মোকাবেলা করে। SCM সিস্টেম তখন পরিবর্তনগুলিকে মার্জ করার জন্য অ্যালগরিদম চালায় যেহেতু ফাইলগুলি রিপোজিটরিতে আবার চেক ইন করা হয় ("জমা দিন" বা "কমিট")।

এই অ্যালগরিদমগুলি সহজ হতে পারে (প্রকৌশলীদেরকে বিরোধপূর্ণ পরিবর্তনগুলি সমাধান করতে বলুন) বা সহজ নয় (বিরোধপূর্ণ পরিবর্তনগুলিকে কীভাবে বুদ্ধিমত্তার সাথে একত্রিত করবেন তা নির্ধারণ করুন এবং সিস্টেমটি সত্যিই আটকে গেলে শুধুমাত্র একজন প্রকৌশলীকে জিজ্ঞাসা করুন)।

সংস্করণ করা

ভার্সনিং বলতে ফাইল রিভিশনের ট্র্যাক রাখা বোঝায় যা ফাইলের পূর্ববর্তী সংস্করণ পুনরায় তৈরি করা (বা ফিরে আসা) সম্ভব করে। এটি সংগ্রহস্থলে চেক করার সময় প্রতিটি ফাইলের একটি সংরক্ষণাগার কপি তৈরি করে বা ফাইলে করা প্রতিটি পরিবর্তন সংরক্ষণ করে করা হয়। যেকোনো সময়, আমরা আগের সংস্করণ তৈরি করতে সংরক্ষণাগার ব্যবহার করতে পারি বা তথ্য পরিবর্তন করতে পারি। ভার্সনিং সিস্টেমগুলি কে পরিবর্তনগুলি চেক করেছে, কখন তারা চেক ইন করা হয়েছিল এবং কী পরিবর্তনগুলি হয়েছিল তার লগ রিপোর্টও তৈরি করতে পারে।

সিঙ্ক্রোনাইজেশন

কিছু SCM সিস্টেমের সাথে, পৃথক ফাইলগুলি সংগ্রহস্থলে চেক ইন এবং আউট করা হয়। আরও শক্তিশালী সিস্টেম আপনাকে একবারে একাধিক ফাইল চেক আউট করার অনুমতি দেয়। ইঞ্জিনিয়াররা তাদের নিজস্ব, সম্পূর্ণ, সংগ্রহস্থলের অনুলিপি (বা এর অংশ) পরীক্ষা করে এবং প্রয়োজন অনুসারে ফাইলগুলিতে কাজ করে। তারপরে তারা তাদের পরিবর্তনগুলি পর্যায়ক্রমে মাস্টার রিপোজিটরিতে ফেরত পাঠায়, এবং অন্য লোকেদের করা পরিবর্তনগুলির সাথে আপ-টু-ডেট থাকার জন্য তাদের নিজস্ব ব্যক্তিগত কপি আপডেট করে। এই প্রক্রিয়াটিকে সিঙ্কিং বা আপডেট বলা হয়।

বিদ্রোহ

সাবভার্সন (SVN) একটি ওপেন সোর্স সংস্করণ নিয়ন্ত্রণ ব্যবস্থা। এটিতে উপরে বর্ণিত সমস্ত বৈশিষ্ট্য রয়েছে।

সংঘাত ঘটলে SVN একটি সহজ পদ্ধতি গ্রহণ করে। একটি বিরোধ হল যখন দুই বা ততোধিক প্রকৌশলী কোড বেসের একই এলাকায় বিভিন্ন পরিবর্তন করে এবং তারপর উভয়ই তাদের পরিবর্তনগুলি জমা দেয়। SVN শুধুমাত্র ইঞ্জিনিয়ারদের সতর্ক করে যে একটি দ্বন্দ্ব আছে - এটি সমাধান করা ইঞ্জিনিয়ারদের উপর নির্ভর করে।

আপনাকে কনফিগারেশন ম্যানেজমেন্টের সাথে পরিচিত হতে সাহায্য করার জন্য আমরা এই কোর্স জুড়ে SVN ব্যবহার করতে যাচ্ছি। এই ধরনের সিস্টেম শিল্পে খুব সাধারণ।

প্রথম ধাপ হল আপনার সিস্টেমে SVN ইনস্টল করা। নির্দেশাবলীর জন্য এখানে ক্লিক করুন. আপনার অপারেটিং সিস্টেম খুঁজুন এবং উপযুক্ত বাইনারি ডাউনলোড করুন।

কিছু SVN পরিভাষা

  • পুনর্বিবেচনা: একটি ফাইল বা ফাইলের সেটে পরিবর্তন। একটি সংশোধন একটি ক্রমাগত পরিবর্তিত প্রকল্পের একটি "স্ন্যাপশট"।
  • সংগ্রহস্থল: মাস্টার কপি যেখানে SVN একটি প্রকল্পের সম্পূর্ণ পুনর্বিবেচনার ইতিহাস সংরক্ষণ করে। প্রতিটি প্রকল্পের একটি সংগ্রহস্থল আছে।
  • ওয়ার্কিং কপি: যে কপিতে একজন প্রকৌশলী একটি প্রকল্পে পরিবর্তন করেন। একটি প্রদত্ত প্রকল্পের অনেকগুলি কার্যকারী অনুলিপি হতে পারে যার প্রতিটি মালিক একজন পৃথক প্রকৌশলী।
  • চেক আউট: সংগ্রহস্থল থেকে একটি কার্যকরী অনুলিপি অনুরোধ করতে. একটি কার্যকরী অনুলিপি প্রকল্পের অবস্থার সমান হয় যখন এটি পরীক্ষা করা হয়েছিল।
  • প্রতিশ্রুতিবদ্ধ: কেন্দ্রীয় সংগ্রহস্থলে আপনার কার্যকারী অনুলিপি থেকে পরিবর্তন পাঠাতে। চেক-ইন বা জমা হিসাবেও পরিচিত।
  • আপডেট: আপনার কাজের অনুলিপিতে সংগ্রহস্থল থেকে অন্যদের পরিবর্তন আনার জন্য, অথবা আপনার কার্যকারী অনুলিপিতে কোনো অনিয়মিত পরিবর্তন আছে কিনা তা নির্দেশ করতে। উপরে বর্ণিত হিসাবে এটি একটি সিঙ্ক হিসাবে একই। সুতরাং, আপডেট/সিঙ্ক আপনার কাজের অনুলিপি সংগ্রহস্থলের অনুলিপি সহ আপ-টু-ডেট নিয়ে আসে।
  • দ্বন্দ্ব: পরিস্থিতি যখন দুই ইঞ্জিনিয়ার একটি ফাইলের একই এলাকায় পরিবর্তন করার চেষ্টা করে। 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 কমান্ড রিপোজিটরির ডিরেক্টরি প্রকল্পে ডিরেক্টরি mytree-এর বিষয়বস্তু কপি করে। আমরা তালিকা কমান্ডের সাহায্যে সংগ্রহস্থলের ডিরেক্টরিটি দেখতে পারি

$ 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.

এখন আপনার কাছে একটি কার্যকরী অনুলিপি রয়েছে, আপনি সেখানে ফাইল এবং ডিরেক্টরিতে পরিবর্তন করতে পারেন। আপনার কাজের অনুলিপিটি ফাইল এবং ডিরেক্টরিগুলির অন্য যেকোন সংগ্রহের মতোই - আপনি নতুনগুলি যুক্ত করতে পারেন বা সেগুলি সম্পাদনা করতে পারেন, সেগুলিকে ঘুরিয়ে দিতে পারেন, এমনকি আপনি সম্পূর্ণ কার্যকারী অনুলিপি মুছে ফেলতে পারেন৷ মনে রাখবেন যে আপনি যদি আপনার কাজের অনুলিপিতে ফাইলগুলি অনুলিপি এবং সরান তবে আপনার অপারেটিং সিস্টেম কমান্ডের পরিবর্তে svn অনুলিপি এবং svn মুভ ব্যবহার করা গুরুত্বপূর্ণ। একটি নতুন ফাইল যোগ করতে, svn add ব্যবহার করুন এবং একটি ফাইল মুছতে svn delete ব্যবহার করুন। আপনি যদি কেবল সম্পাদনা করতে চান তবে আপনার সম্পাদকের সাথে ফাইলটি খুলুন এবং সম্পাদনা করুন!

কিছু স্ট্যান্ডার্ড ডিরেক্টরির নাম রয়েছে যা প্রায়শই সাবভার্সনের সাথে ব্যবহৃত হয়। "ট্রাঙ্ক" ডিরেক্টরিটি আপনার প্রকল্পের উন্নয়নের প্রধান লাইন ধারণ করে। একটি "শাখা" ডিরেক্টরিতে আপনি কাজ করছেন এমন যেকোনো শাখা সংস্করণ ধারণ করে।

$ 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

নোট করুন যে এই আউটপুট নিয়ন্ত্রণ করার জন্য স্ট্যাটাস কমান্ডে প্রচুর পতাকা রয়েছে। আপনি যদি একটি পরিবর্তিত ফাইলে নির্দিষ্ট পরিবর্তনগুলি দেখতে চান, 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 আপডেট কমান্ডটি ব্যবহার করুন।

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

এটি এমন একটি জায়গা যেখানে সংঘর্ষ হতে পারে। উপরের আউটপুটে, "U" নির্দেশ করে যে এই ফাইলগুলির সংগ্রহস্থল সংস্করণে কোন পরিবর্তন করা হয়নি এবং একটি আপডেট করা হয়েছে। "G" মানে একটি মার্জ হয়েছে। সংগ্রহস্থল সংস্করণ পরিবর্তন করা হয়েছে, কিন্তু পরিবর্তনগুলি আপনার সাথে বিরোধপূর্ণ নয়। "C" একটি দ্বন্দ্ব নির্দেশ করে। এর মানে হল যে রিপোজিটরি থেকে পরিবর্তনগুলি আপনার সাথে ওভারল্যাপ করা হয়েছে, এবং এখন আপনাকে তাদের মধ্যে বেছে নিতে হবে।

দ্বন্দ্ব আছে এমন প্রতিটি ফাইলের জন্য, সাবভার্সন আপনার কার্যকারী অনুলিপিতে তিনটি ফাইল রাখে:

  • file.mine: এটি আপনার ফাইল যেহেতু এটি আপনার কাজের অনুলিপি আপডেট করার আগে বিদ্যমান ছিল।
  • file.rOLDREV: এই ফাইলটি আপনি আপনার পরিবর্তন করার আগে সংগ্রহস্থল থেকে চেক আউট করেছেন।
  • file.rNEWREV: এই ফাইলটি সংগ্রহস্থলের বর্তমান সংস্করণ।

দ্বন্দ্ব সমাধানের জন্য আপনি তিনটি জিনিসের মধ্যে একটি করতে পারেন:

  • ফাইলগুলির মধ্য দিয়ে যান এবং ম্যানুয়ালি মার্জ করুন।
  • আপনার কাজের অনুলিপি সংস্করণে SVN দ্বারা তৈরি অস্থায়ী ফাইলগুলির একটি অনুলিপি করুন।
  • আপনার সমস্ত পরিবর্তনগুলি ফেলে দিতে svn রিভার্ট চালান।

একবার আপনি বিরোধের সমাধান করে ফেললে, আপনি svn সমাধান করে চালিয়ে SVN কে জানাবেন। এটি তিনটি অস্থায়ী ফাইলগুলি সরিয়ে দেয় এবং এসভিএন আর কোনও সংঘাতের অবস্থায় ফাইলটি দেখে না।

শেষ কাজটি হ'ল আপনার চূড়ান্ত সংস্করণটি সংগ্রহস্থলে প্রতিশ্রুতিবদ্ধ করা। এটি এসভিএন কমিট কমান্ড দিয়ে করা হয়। আপনি যখন কোনও পরিবর্তন করেন, আপনাকে একটি লগ বার্তা সরবরাহ করতে হবে, যা আপনার পরিবর্তনগুলি বর্ণনা করে। এই লগ বার্তাটি আপনার তৈরি পুনর্বিবেচনার সাথে সংযুক্ত রয়েছে।

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

এসভিএন সম্পর্কে আরও অনেক কিছু শিখতে হবে এবং এটি কীভাবে বড় সফ্টওয়্যার ইঞ্জিনিয়ারিং প্রকল্পগুলিকে সমর্থন করতে পারে। ওয়েবে বিস্তৃত সংস্থান রয়েছে - কেবল "সাবভার্সন" এ একটি গুগল অনুসন্ধান করুন।

অনুশীলনের জন্য, আপনার সুরকার ডাটাবেস সিস্টেমের জন্য একটি সংগ্রহস্থল তৈরি করুন এবং আপনার সমস্ত ফাইল আমদানি করুন। তারপরে একটি কার্যনির্বাহী অনুলিপি চেকআউট করুন এবং উপরে বর্ণিত কমান্ডগুলি দিয়ে যান।

তথ্যসূত্র

অনলাইন সাবভার্সন বই

এসভিএন সম্পর্কিত উইকিপিডিয়া নিবন্ধ

সাবভার্সন ওয়েবসাইট

আবেদন: শারীরবৃত্তিতে একটি গবেষণা

অস্টিনের টেক্সাস বিশ্ববিদ্যালয় থেকে এসকেলেটনগুলি দেখুন