Sonraki Adımlar

Programlama ve C++'a Giriş

Bu online eğitim daha ileri düzey kavramlarla devam ediyor. Lütfen Bölüm III'ü okuyun. Bu modülde işaretçileri kullanmaya ve nesneleri kullanmaya başlamaya odaklanacağız.

Örnek #2 ile Öğrenin

Bu modülde, ayrıştırmayla ilgili daha fazla alıştırma yapma, işaretçileri anlama ve nesneler ile sınıfları kullanmaya başlama konularına odaklanıyoruz. Aşağıdaki örnekleri inceleyin. İstendiğinde programları kendiniz yazın veya denemeleri yapın. İyi bir programcı olmanın sırrının pratik, pratik ve pratik olduğunu ne kadar vurgulasak azdır.

1. Örnek: Daha Fazla Ayrıştırma Uygulaması

Basit bir oyundan elde edilen aşağıdaki çıkışı düşünün:

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.

İlk gözlem giriş metnidir ve program yürütme başına bir kez gösterilir. Her turda düşmanın mesafesini belirlemek için rastgele bir sayı oluşturucuya ihtiyacımız var. Oyuncudan açı girdisini almak için bir mekanizmaya ihtiyacımız vardır ve bu mekanizma, düşmana vurana kadar tekrarladığı için kesinlikle döngü yapısındadır. Mesafe ve açıyı hesaplamak için de bir işleve ihtiyacımız var. Son olarak, düşmanı vurmak için kaç atış gerektiğini ve programın yürütülmesi sırasında kaç düşman vurduğumuzu takip etmeliyiz. Ana programın olası bir özetini burada bulabilirsiniz.

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;

Yangın prosedürü, oyunun oynanmasını yönetir. Bu fonksiyonda düşmanın uzaklığını almak için rastgele bir sayı üreteci çağırırız. Ardından oyuncunun girdisini almak ve düşmanın üzerine isabet edip etmediğini hesaplamak için döngüyü kurarız. Halkadaki nöbetçi koşulu, düşmana vurmaya ne kadar yaklaştığımızdır.

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() ve sin() çağrıları nedeniyle, matematik.h işlemini dahil etmeniz gerekir. Bu programı yazmayı deneyin. Bu, problemleri ayrıştırma ve temel C++ hakkında iyi bir inceleme için harika bir uygulamadır. Her fonksiyonda yalnızca bir görev yapmayı unutmayın. Bu, şimdiye kadar hazırladığımız en gelişmiş program olduğundan bunu yapmanız biraz zaman alabilir.Çözümümüze buradan ulaşabilirsiniz. 

2. Örnek: İşaretçilerle Pratik Yapma

İşaretçilerle çalışırken unutulmaması gereken dört nokta vardır:
  1. İşaretçiler, bellek adreslerini barındıran değişkenlerdir. Program çalıştırılırken tüm değişkenler, her biri kendi benzersiz adresinde veya konumunda olacak şekilde bellekte depolanır. İşaretçi, veri değeri yerine bellek adresi içeren özel bir değişken türüdür. Normal bir değişken kullanıldığında verilerde değişiklik yapılması gibi, işaretçide depolanan adresin değeri de işaretçi değişkeni değiştirildiğinde değiştirilir. Örnek:
    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. Genellikle işaretçinin depoladığı konumu "işaret ettiğini" ("işaretçi") söyleriz. Dolayısıyla yukarıdaki örnekte, intptr işaretçiyi işaret eder 5.

    Tam sayı işaretçimize bellek ayırmak için "new" operatörünün kullanıldığına dikkat edin. Bu, işaretçiye erişmeden önce yapmamız gereken bir şeydir.

    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.
          

    * operatörü, C'de referans vermek için kullanılır. C/C++ programcılarının işaretçilerle çalışırken en sık yaptığı hatalardan biri, işaretçiyi başlatmayı unutmaktır. Bellekte, bilinmeyen veriler içeren bir konuma erişdiğimiz için bu durum bazen çalışma zamanı kilitlenmesine neden olabilir. Bu verileri değiştirmeye çalışırsak bellekte hafif bir bozulmaya neden olarak, takibi zor bir hataya neden olabiliriz. 

  3. İki işaretçi arasında işaretçi atama, bunların aynı işaretçiyi işaret etmesini sağlar. Dolayısıyla, y = x; y ataması, y üzerinde x ile aynı noktayı işaret eder. İşaretçi ataması, işaretçiye dokunmaz. Yalnızca bir işaretçiyi başka bir işaretçiyle aynı konuma sahip olacak şekilde değiştirir. İşaretçi atamasından sonra, iki işaretçi, işaretçiyi "paylaşır". 
  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
    }
      

Bu kodun bir izini aşağıda verilmiştir:

1. İki işaretçi (x ve y) ayırın. İşaretçileri ayırmak nokta ayırmaz.
2. Bir işaretçi ayırın ve ona işaret edecek şekilde x öğesini ayarlayın.
3. İşaretçisinde 42'ye yer vermek için x referansını kaldırın. Bu, referans kaldırma işleminin temel bir örneğidir. X'ten başlayın, işaretçisine erişmek için oku takip edin.
4. İşaretçisinde 13 değerini kaydetmek için y'nin referansını kaldırmayı deneyin. Bu, y'nin hiç noktası atanmadığı için kilitlenir.
5. y = x değerini atayın. Böylece y, x'in işaretçisini işaret eder. Şimdi x ve y aynı işarete işaret ediyor; bunlar "paylaşılıyor".
6. İşaretçisinde 13 değerini kaydetmek için y'nin referansını kaldırmayı deneyin. Önceki ödev size bir işaretçi verdiği için bu sefer işe yarıyor.

Gördüğünüz gibi resimler, işaretçi kullanımını anlama konusunda oldukça faydalıdır. Bir örnek daha verelim.

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.

Bu örnekte, hiçbir zaman "new" operatörü ile bellek ayırmadığımıza dikkat edin. Normal bir tam sayı değişkeni tanımladık ve işaretçiler aracılığıyla değiştirdik.

Bu örnekte, yığın belleğinin ayrılmasını sağlayan sil operatörünün kullanımını ve daha karmaşık yapılar için paylaştırmayı nasıl yapabileceğimizi gösteriyoruz. Bellek düzenini (yığın ve çalışma zamanı yığını) başka bir derste ele alacağız. Şimdilik yığını, çalışan programların kullandığı boş bir bellek deposu olarak düşünebilirsiniz.

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;

Bu son örnekte, bir fonksiyona referans vererek değer geçirmek için işaretçilerin nasıl kullanıldığını gösteriyoruz. Bir fonksiyon içindeki değişkenlerin değerlerini bu şekilde değiştiririz.

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

Yinelenen işlev tanımındaki & bağımsız değişkenlerini devre dışı bırakırsak, değişkenleri "değere göre" iletiriz. Diğer bir deyişle, değişkenin değerinin bir kopyası oluşturulur. İşlevdeki değişkende yapılan değişiklikler kopyayı değiştirir. Orijinal değişkeni değiştirmezler.

Bir değişken referans yoluyla iletildiğinde, değerinin bir kopyasını iletmeyiz, değişkenin adresini işleve iletiriz. Yerel değişkende yaptığımız herhangi bir değişiklik, aslında aktarılan orijinal değişkeni de değiştirir. 

C programcısıysanız bunun yeni bir olay olduğunu söyleyebiliriz. Aynı işlemi C'de de Duplicate() işlevini Duplicate(int *x) olarak tanımlayarak yapabiliriz. Bu durumda, x bir int'in işaretçisidir, ardından &x bağımsız değişkeniyle Duplicate() (x adresi) çağrılır ve Duplicate() içinde x referansını kaldırarak (aşağıya bakın) kullanabiliriz. Ancak C++, eski "C" yöntemi hâlâ çalışıyor olsa da değerleri referans alarak işlevlere daha basit bir yol sağlar.

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++ referanslarında belirtildiği gibi, bir değişkenin adresini iletmemiz veya çağrılan işlevin içindeki değişkenin referansını kaldırmamız gerekmez.

Aşağıdaki program ne üretiyor? Hafızanın boyutunu öğrenmek için bir anı resmi çizin.

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

Doğru yanıtı bulup bulmadığınızı görmek için programı çalıştırın.

3. Örnek: Değerleri Referansa Göre Aktarma

Aracın hızını ve bir miktarı girdi olarak alan hızlandırılmış() adlı bir fonksiyon yazın. İşlev, aracı hızlandırmak için hızı hıza ekler. Hız parametresi, referans yoluyla ve miktar bazında iletilmelidir. Çözümümüz burada verilmiştir.

4. Örnek: Sınıflar ve Nesneler

Aşağıdaki sınıfı göz önünde bulundurun:

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

Sınıf üyesi değişkenlerinin sonunda alt çizgi olduğuna dikkat edin. Bu, yerel değişkenler ile sınıf değişkenlerini birbirinden ayırt etmek için yapılır.

Bu sınıfa bir azaltma yöntemi ekleyin. Çözümümüz burada verilmiştir.

Bilimin harikaları: Bilgisayar Bilimleri

Egzersizler

Bu kursun ilk modülünde olduğu gibi alıştırmalar ve projeler için çözüm sunmuyoruz.

Unutmayın...

... mantıksal olarak herhangi bir işlevin yalnızca bir görev yaptığı işlevlere bölünür.

... programın ne yapacağını özetleyen bir ana program sunuyor.

... açıklayıcı işlev, sabit ve değişken adlarına sahiptir.

... programda "sihirli" sayılardan kaçınmak için sabit değerler kullanır.

... kolay bir kullanıcı arayüzüne sahiptir.

Isınma Egzersizleri

  • 1. Alıştırma

    36 tam sayısının kendine has bir özelliği vardır: Tam karedir ve aynı zamanda 1'den 8'e kadar olan tam sayıların toplamıdır. Böyle bir sonraki sayı 1225'tir (352), 1'den 49'a kadar olan tam sayıların toplamıdır. Tam kare olan bir sonraki sayıyı ve 1...n serilerinin toplamını bulun. Bir sonraki sayı, 32.767'den büyük olabilir. Programınızın daha hızlı çalışmasını sağlamak için bildiğiniz kitaplık işlevlerini (veya matematik formüllerini) kullanabilirsiniz. Bu programı, bir sayının tam kare mi yoksa bir dizinin toplamı mı olduğunu belirlemek için for döngüleri kullanarak yazmak da mümkündür. (Not: Makinenize ve programınıza bağlı olarak bu numaranın bulunması uzun sürebilir.)

  • 2. Alıştırma

    Üniversitedeki kitapçınızın, gelecek yıl için işini tahmin ederken yardımınıza ihtiyacı var. Deneyimler, satışların büyük ölçüde bir kitabın bir kurs için gerekli mi yoksa isteğe bağlı mı olmasına ve daha önce sınıfta kullanılıp kullanılmadığına bağlı olduğunu göstermiştir. Zorunlu yeni bir ders kitabını, olası kaydın% 90'ına satabilirsiniz. Ancak bu kitap daha önce sınıfta kullanılmışsa yalnızca% 65'i satın alır. Benzer şekilde, olası kaydın% 40'ı isteğe bağlı yeni bir ders kitabı satın alacak, ancak bu kitap sınıfta kullanılmışsa satın alacak olanların yalnızca% 20'si olacaktır. (Buradaki "kullanılmış" ifadesinin ikinci el kitaplar anlamına gelmediğini unutmayın.)

  • Bir dizi kitabı girdi olarak kabul eden bir program yazın (kullanıcı bir gözcüye girene kadar). Her kitap için şunları isteyin: kitabın kodunu, kitabın tek kopyasının maliyetini, mevcut kitap sayısı, olası sınıf kaydı ve kitabın gerekli/isteğe bağlı, yeni/eskiden kullanılmış olup olmadığını belirten veriler. Çıktı olarak, tüm giriş bilgilerini güzel biçimlendirilmiş bir ekranda, kaç kitabın sipariş edilmesi gerektiği (varsa, yalnızca yeni kitapların sipariş edildiğini) ve her siparişin toplam maliyetiyle birlikte gösterin.

    Ardından, tüm girişler tamamlandıktan sonra tüm kitap siparişlerinin toplam maliyetini ve mağaza liste fiyatının% 80'ini öderse beklenen kârı gösterin. Bir programa gelen büyük veri kümesiyle başa çıkmaya yönelik herhangi bir yöntemden henüz bahsetmediğimizden (bizi takip edin!) her defasında bir kitabı işlemeniz ve o kitabın çıktı ekranını göstermeniz yeterlidir. Ardından, kullanıcı tüm verileri girmeyi bitirdiğinde programınız toplam ve kâr değerlerini verir.

    Kod yazmaya başlamadan önce, biraz bu programın tasarımı üzerine düşünün. Verileri bir fonksiyon grubuna ayırın ve problemin çözümünün ana hatları gibi okunan bir main() işlevi oluşturun. Her işlevin bir görev yaptığından emin olun.

    Örnek çıktıyı aşağıda görebilirsiniz:

    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
    ***************************************************

Veritabanı Projesi

Bu projede, basit bir veritabanı uygulaması uygulayan, tamamen işlevsel bir C++ programı oluşturuyoruz.

Programımız, bestecilerin ve bestecilerle ilgili bilgilerin yer aldığı bir veritabanını yönetmemize olanak tanır. Programın özellikleri arasında şunlar yer alır:

  • Yeni bir besteci ekleyebilme
  • Bir besteciyi sıralama becerisi (ör. bestecinin müziğini ne kadar beğendiğimizi veya beğenmediğimizi belirtme)
  • Veritabanındaki tüm oluşturucuları görüntüleme olanağı
  • Tüm bestecileri sıralamaya göre görüntüleme olanağı

"Yazılım tasarımı oluşturmanın iki yolu vardır: Bunlardan biri, açık bir şekilde hiçbir kusur bulunmayacak kadar basit hale getirmek, diğer yol da açık bir eksiklik olmayacak kadar karmaşık hale getirmektir. İlk yöntem çok daha zor." - C.A.R. Hoare

Birçoğumuz tasarım ve kodlamayı "prosedürel" bir yaklaşımla öğrenmiştik. Asıl sorumuz "Program ne yapmalıdır?" sorusuyla başlar. Bir problemin çözümünü görevlere ayırırız. Her görev, sorunun bir bölümünü çözer. Bu görevler, programımızdaki main() veya diğer işlevlerden sıralı olarak çağrılan işlevlerle eşlenir. Bu adım adım açıklamalı yaklaşım, çözmemiz gereken bazı sorunlar için idealdir. Ancak programlarımız çoğunlukla görevlerin veya olayların doğrusal dizilerinden ibaret değildir.

Nesne odaklı (OO) yaklaşımında, "Hangi gerçek nesnelerin modelini oluşturuyorum?" sorusuyla başlıyoruz. Bir programı yukarıda açıklandığı gibi görevlere ayırmak yerine, fiziksel nesnelerin modellerine böleriz. Bu fiziksel nesnelerin, bir dizi özelliğe göre tanımlanmış bir durumu ve gerçekleştirebilecekleri bir dizi davranış veya eylem vardır. İşlemler, nesnenin durumunu değiştirebilir veya başka nesnelerin eylemlerini çağırabilir. Temel prensip, bir nesnenin bir şeyi kendi başına nasıl yapacağını "bilmesidir".

OO tasarımında, fiziksel nesneleri sınıflar ve nesneler; özellikler ve davranışlar açısından tanımlarız. OO programında genellikle çok sayıda nesne bulunur. Ancak bu nesnelerin birçoğu temelde aynıdır. Aşağıdakileri göz önünde bulundurun.

Sınıf, gerçek dünyada fiziksel olarak var olabilecek bir nesnenin genel özellikleri ve davranışları kümesidir. Yukarıdaki çizimde bir Apple sınıfmız var. Türü ne olursa olsun tüm elmaların renk ve tat özellikleri vardır. Apple'ın özelliklerini gösterdiği bir davranış da tanımladık.

Bu şemada Apple sınıfında iki nesne tanımladık. Her nesne sınıfla aynı özelliklere ve işlemlere sahiptir, ancak nesne belirli bir elma türünün özelliklerini tanımlar. Buna ek olarak, Görüntüleme işlemi, söz konusu nesnenin özelliklerini görüntüler, ör. "Yeşil" ve "Ekşi".

OO tasarımı bir dizi sınıftan, bu sınıflarla ilişkili verilerden ve sınıfların gerçekleştirebileceği işlem kümesinden oluşur. Farklı sınıfların etkileşim yollarını da belirlememiz gerekir. Bu etkileşim, diğer sınıflardaki nesnelerin eylemlerini çağıran bir sınıfın nesneleri tarafından gerçekleştirilebilir. Örneğin, her bir Apple nesnesinin Display() yöntemini çağırarak, bir Apple nesneleri dizisinin rengini ve tadını çıkaran bir AppleExiter sınıfımız olabilir.

OO tasarım sırasında uyguladığımız adımlar şunlardır:

  1. Sınıfları belirleyin ve genel olarak her sınıftaki bir nesnenin neleri veri olarak depoladığını ve bir nesnenin neler yapabileceğini tanımlayın.
  2. Her sınıfın veri öğelerini tanımlama
  3. Her sınıfın işlemlerini ve bir sınıftaki bazı işlemlerin, ilgili diğer sınıfların işlemleri kullanılarak nasıl uygulanabileceğini tanımlayın.

Büyük bir sistemde bu adımlar farklı ayrıntı düzeylerinde yinelemeli olarak gerçekleşir.

Besteci veritabanı sistemi için ayrı bir bestecide depolamak istediğimiz tüm verileri kapsayan bir Composer sınıfına ihtiyacımız vardır. Bu sınıfın bir nesnesi kendini yükseltebilir veya düşürebilir (sıralamasını değiştirebilir) ve özelliklerini gösterebilir.

Ayrıca, Composer nesnelerinden oluşan bir koleksiyona da ihtiyacımız vardır. Bunun için kayıtları ayrı ayrı yöneten bir Database sınıfı tanımlarız. Bu sınıfın bir nesnesi, Composer nesnelerini ekleyebilir veya alabilir ve bir Composer nesnesinin display işlemini çağırarak tek tek nesneleri görüntüleyebilir.

Son olarak, veritabanında etkileşimli işlemler sunmak için bir tür kullanıcı arayüzüne ihtiyacımız vardır. Bu bir yer tutucu sınıftır. Yani kullanıcı arayüzünün nasıl görüneceğini henüz tam olarak bilmiyoruz ama bir taneye ihtiyacımız olacağını biliyoruz. Belki grafik, belki de metin tabanlı olur. Şimdilik, daha sonra doldurabileceğimiz bir yer tutucu tanımlıyoruz.

Artık oluşturucu veritabanı uygulaması için sınıfları belirlediğimize göre, sonraki adım sınıflar için özellikleri ve işlemleri tanımlamaktır. Daha karmaşık bir uygulamada ise sınıf hiyerarşisini ve nesnelerin nasıl etkileşimde bulunduğunu ortaya çıkarmak için kalem ve kağıt, UML, CRC kartları veya OOD kullanırdık.

Composer veritabanımız için, her bir oluşturucuda depolamak istediğimiz ilgili verileri içeren bir Composer sınıfı tanımlarız. Ayrıca, sıralamaları değiştirme ve verileri görüntülemeye yönelik yöntemler de içerir.

Veritabanı sınıfının, Composer nesnelerini tutmak için bir tür yapıya ihtiyacı vardır. Yapıya yeni bir Composer nesnesi eklememizin yanı sıra belirli bir Composer nesnesini alabilmemiz gerekir. Ayrıca tüm nesneleri giriş sırasına veya sıralamaya göre görüntülemek istiyoruz.

Kullanıcı Arayüzü sınıfı, Database sınıfında işlem çağıran işleyicilerle birlikte menü odaklı bir arayüz uygular. 

Sınıflar kolayca anlaşılabiliyorsa ve özellikleri ile işlemleri netse, oluşturucu uygulamasında olduğu gibi bu sınıfları tasarlamak da nispeten kolaydır. Ancak sınıfların ilişkisi ve etkileşimiyle ilgili aklınıza gelen herhangi bir soru varsa en iyi yöntem önce bunu ortaya çıkarmak ve koda başlamadan önce tüm ayrıntıları incelemektir.

Tasarımın net bir resmini elde edip değerlendirdikten sonra (yakında bununla ilgili daha fazla bilgi) her sınıf için arayüzü tanımlarız. Bu aşamada, uygulama ayrıntıları önemli değildir. Yalnızca özellikler ve işlemler nelerdir ve diğer sınıflarda bir sınıfın durum ve işlemlerin hangi bölümlerinin kullanılabilir olduğu da belirtilir.

C++'ta bunu normalde her sınıf için bir üstbilgi dosyası tanımlayarak yaparız. Composer sınıfı, bir oluşturucuda depolamak istediğimiz tüm veriler için özel veri üyelerine sahiptir. Erişimcilerin ("alma" yöntemleri) ve mutatörlerin ("ayarlama" yöntemlerinin) yanı sıra sınıfın birincil işlemlerine ihtiyacımız vardır.

// 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 sınıfı da oldukça basittir.

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

Besteciye özgü verileri dikkatli bir şekilde ayrı bir sınıfa nasıl yerleştirdiğimize dikkat edin. Composer kaydını temsil etmek için Database sınıfına bir struct veya class ekleyip bu kayda doğrudan oradan erişebilirdik. Ancak bu, "nesneliğinin yetersiz olması" anlamına gelir. Yani nesnelerle mümkün olduğunca çok modelleme yapmıyoruz.

Composer ve Database sınıflarının uygulanması üzerinde çalışmaya başladığınızda, ayrı bir Composer sınıfının çok daha kullanışlı olduğunu göreceksiniz. Özellikle bir Composer nesnesinde ayrı atomik işlemlere sahip olmak, Database sınıfında Display() yöntemlerinin uygulanmasını büyük ölçüde basitleştirir.

Elbette "aşırı nesneleştirme" diye bir şey de var. Her şeyi bir sınıf haline getirmeye çalışıyoruz veya ihtiyacımız olandan daha fazla sınıflarımız var. Doğru dengeyi bulmak için pratik yapmak gerekir. Her bir programcının farklı görüşlere sahip olduğunu göreceksiniz.

Nesnenizi gereğinden fazla mı yoksa yetersiz mi belirlediğinizi belirlemek genellikle sınıflarınızı dikkatli bir şekilde diyagramlarla çözebilir. Daha önce de belirtildiği gibi, kodlamaya başlamadan önce bir sınıf tasarımı oluşturmak önemlidir. Bu, yaklaşımınızı analiz etmenize yardımcı olabilir. Bu amaç için yaygın olarak kullanılan bir gösterim olan UML (Birleşik Modelleme Dili) Artık Composer ve Database nesneleri için tanımlanmış sınıflara sahip olduğumuza göre kullanıcının veritabanıyla etkileşim kurmasına olanak tanıyan bir arayüze ihtiyacımız vardır. Basit bir menü yeterli olacaktır:

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

Kullanıcı arayüzünü bir sınıf veya prosedürel program olarak uygulayabiliriz. C++ programlarında her şeyin sınıf olması gerekmez. Aslında, bu menü programında olduğu gibi işleme sıralısa veya görev odaklıysa, bunu prosedüre göre uygulamakta bir sakınca yoktur. URL'nin "yer tutucu" olarak kalacağı şekilde uygulanması önemlidir. Örneğin, herhangi bir noktada grafik kullanıcı arayüzü oluşturmak istersek sistemde kullanıcı arayüzü dışında herhangi bir şeyi değiştirmek zorunda kalmamalıyız.

Başvuruyu tamamlamamız gereken son şey, sınıfları test edecek bir programdır. Composer sınıfı için giriş alan, bir oluşturucu nesnesini dolduran ve ardından sınıfın düzgün çalıştığından emin olmak için görüntüleyen bir main() programı istiyoruz. Ayrıca, Composer sınıfının tüm yöntemlerini çağırmak istiyoruz.

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

Database sınıfı için de benzer bir test programına ihtiyacımız var.

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

Bu basit test programları iyi bir ilk adım olsa da programın doğru çalıştığından emin olmak için çıktıyı manuel olarak incelememizi gerektirirler. Sistem büyüdükçe, çıktıları manuel olarak incelemek kısa sürede elverişsiz hale gelir. Sonraki derste, birim testleri şeklinde öz kontrol test programlarını tanıtacağız.

Uygulamamızın tasarımı tamamlandı. Sonraki adım, .cpp dosyalarını sınıflar ve kullanıcı arayüzü için uygulamaktır.Başlamak için devam edip yukarıdaki .h kodunu ve test sürücüsü kodunu kopyalayıp dosyalara yapıştırın ve derleyin.Sınıflarınızı test etmek için test sürücülerini kullanın. Ardından, aşağıdaki arayüzü uygulayın:

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

Kullanıcı arayüzünü uygulamak için Database sınıfında tanımladığınız yöntemleri kullanın. Yöntemlerinizi hatasız hale getirin. Örneğin, bir sıralama her zaman 1-10 aralığında olmalıdır. Veritabanı sınıfındaki veri yapısını değiştirmeyi planlamadığınız sürece kimsenin 101 oluşturucuları eklemesine de izin vermeyin.

Tüm kodunuzun, size kolaylık sağlamak için burada tekrarlanan kodlama kurallarımıza uyması gerektiğini unutmayın:

  • Yazdığımız her program bir başlık yorumuyla başlar. Bu yorum yazarın adını, iletişim bilgilerini, kısa bir açıklamasını ve kullanımını (gerekirse) sağlar. Her işlev/yöntem, çalışma ve kullanımla ilgili bir açıklamayla başlar.
  • Açıklayıcı yorumlar eklemek için tam cümleler kullanılır. Bu tür yorumlarda kodun kendini belgelememesi durumunda (ör. işlemin yanıltıcı, bariz olmayan, ilginç veya önemli olduğu durumlarda) yer veririz.
  • Her zaman açıklayıcı adlar kullanın: Değişkenler, kendi_değişkenim gibi, _ ile ayrılan küçük harfli kelimelerdir. İşlev/yöntem adlarında, MyExciteFunction() işlevinde olduğu gibi, kelimeleri büyük harfle işaretleriz. Sabitler "k" ile başlar ve kDaysInWeek gibi, kelimeleri işaretlemek için büyük harfler kullanır.
  • Girinti, ikinin katlarıdır. İlk düzey iki boşluktur; daha fazla girinti gerekirse dört boşluk, altı boşluk vb. kullanırız.

Gerçek Dünya'ya hoş geldiniz!

Bu modülde, çoğu yazılım mühendisliği kuruluşunda kullanılan iki önemli aracı tanıtacağız. Bunlardan ilki bir derleme aracıdır, ikincisi ise yapılandırma yönetimi sistemidir. Çoğu mühendis genellikle tek bir büyük sistem üzerinde çalıştığı endüstriyel yazılım mühendisliğinde bu araçların her ikisi de vazgeçilmezdir. Bu araçlar, kod tabanındaki değişikliklerin koordine edilmesine ve kontrol edilmesine yardımcı olur. Ayrıca, birçok program ve başlık dosyasından bir sistemi derleyip bağlamak için etkili bir yol sunar.

Dosya oluşturma

Program oluşturma süreci genellikle gerekli dosyaları doğru sırada derleyen ve birbirine bağlayan bir derleme aracıyla yönetilir. C++ dosyalarının çoğunlukla bağımlılıkları vardır. Örneğin, bir programda çağrılan işlev başka bir programda yer alır. Veya birkaç farklı .cpp dosyası için bir başlık dosyasına ihtiyaç duyuluyor olabilir. Derleme aracı, bu bağımlılıklar arasından doğru derleme sırasını belirler. Ayrıca yalnızca son derlemeden sonra değişen dosyaları derler. Bu da yüzlerce veya binlerce dosyadan oluşan sistemlerde önemli ölçüde zaman tasarrufu sağlayabilir.

Make adlı açık kaynaklı bir derleme aracı yaygın olarak kullanılır. Uygulama hakkında bilgi edinmek için bu makaleyi okuyun. Composer Database uygulaması için bir bağımlılık grafiği oluşturup oluşturamayacağınıza bakın ve ardından bunu bir makefile dosyasına çevirin.Çözümümüz burada verilmiştir.

Yapılandırma Yönetim Sistemleri

Endüstriyel yazılım mühendisliğinde kullanılan ikinci araç, Yapılandırma Yönetimi'dir (CM). Bu, değişikliği yönetmek için kullanılır. İbrahim ve Susan'ın teknoloji yazarları olduğunu ve ikisinin de teknik bir kılavuzda güncellemeler üzerinde çalıştığını varsayalım. Toplantı sırasında yöneticisi, güncelleme yapmaları için her bir kişiye aynı belgenin bir bölümünü atar.

Teknik kılavuz, hem İbrahim hem de Susan'ın erişebildiği bir bilgisayarda saklanır. Herhangi bir CM aracı veya süreci kullanılmadığında bir dizi sorun ortaya çıkabilir. Olası bir senaryo, belgeyi saklayan bilgisayarın hem İbrahim hem de Susan'ın aynı anda kılavuz üzerinde çalışamayacağı şekilde ayarlanmış olmasıdır. Bu da onları önemli ölçüde yavaşlatır.

Depolama bilgisayarı, dokümanın hem İbrahim hem de Susan tarafından aynı anda açılmasına izin verdiğinde daha tehlikeli bir durum ortaya çıkar. Aşağıdakiler gerçekleşebilir:

  1. Bora, dokümanı bilgisayarında açar ve kendi bölümünde çalışır.
  2. Suzan dokümanı bilgisayarında açar ve bölümünde çalışmaya devam eder.
  3. Bora, değişiklikleri tamamlar ve dokümanı depolama bilgisayarına kaydeder.
  4. Susan değişiklikleri tamamlar ve dokümanı depolama bilgisayarına kaydeder.

Bu görselde, teknik kılavuzun tek kopyası üzerinde kontrol olmadığında oluşabilecek sorun gösterilmektedir. Susan değişiklikleri kaydettiğinde İbrahim tarafından yapılanların üzerine yazar.

Bu, tam olarak bir CM sisteminin kontrol edebileceği türde bir durumdur. CM sistemiyle hem Bob hem de Susan teknik kılavuzun kendi kopyasını "inceler" ve bu kopya üzerinde çalışır. Bob, değişiklikleri tekrar kontrol ettiğinde sistem, Susan'ın kendi kopyası için ödeme yaptığını bilir. Susan kendi kopyasını kontrol ettiğinde sistem hem Bob hem de Susan'ın yaptığı değişiklikleri analiz eder ve iki değişiklik grubunu birleştiren yeni bir sürüm oluşturur.

CM sistemleri, yukarıda açıklandığı gibi eşzamanlı değişiklikleri yönetmenin ötesinde çeşitli özelliklere sahiptir. Birçok sistem, bir belgenin ilk oluşturulduğu andan itibaren tüm sürümlerinin arşivlerini depolar. Teknik kılavuzda ise bu, kullanıcı kılavuzun eski bir sürümüne sahip olduğunda ve teknoloji yazarına sorular sorduğunda çok yararlı olabilir. CM sistemi, teknoloji yazarının eski sürüme erişmesine ve kullanıcının gördüklerini görmesine olanak tanır.

CM sistemleri özellikle yazılımda yapılan değişiklikleri kontrol etmede yararlıdır. Bu tür sistemlere Yazılım Yapılandırma Yönetimi (SCM) sistemleri denir. Büyük bir yazılım mühendisliği kuruluşunda çok sayıda bağımsız kaynak kodu dosyasını ve bunlarda değişiklik yapması gereken mühendis sayısının çok fazla olduğunu göz önünde bulundurursak, SCM sisteminin kritik bir sorun olduğu ortadadır.

Yazılım Yapılandırma Yönetimi

SCM sistemleri basit bir fikre dayanır: Dosyalarınızın kesin kopyaları merkezi bir depoda saklanır. Kullanıcılar, veri havuzundaki dosyaların kopyalarını kontrol eder, bu kopyalar üzerinde çalışır ve dosyaları bittiğinde tekrar kontrol eder. SCM sistemleri, birden fazla kişi tarafından yapılan düzeltmeleri tek bir ana kümeye göre yönetir ve izler. 

Tüm SCM sistemleri aşağıdaki temel özellikleri sunar:

  • Eşzamanlılık Yönetimi
  • Sürüm oluşturma
  • Senkronizasyon

Şimdi bu özelliklerin her birine daha yakından bakalım.

Eşzamanlılık Yönetimi

Eşzamanlılık, bir dosyanın birden fazla kişi tarafından aynı anda düzenlenmesini ifade eder. Büyük bir depo olduğunda, kullanıcıların bunu yapabilmesini isteriz ancak bu durum bazı sorunlara yol açabilir.

Mühendislik alanında basit bir örnek düşünün: Mühendislerin, aynı dosyayı merkezi bir kaynak kodu deposunda eş zamanlı olarak değiştirmelerine izin verdiğimizi varsayalım. İstemci1 ve İstemci2'nin bir dosyada aynı anda değişiklik yapması gerekir:

  1. İstemci1, bar.cpp'yi açar.
  2. İstemci2, bar.cpp'yi açar.
  3. İstemci1, dosyayı değiştirir ve kaydeder.
  4. İstemci2, dosyayı değiştirir ve İstemci1'in değişikliklerinin üzerine yazar.

Elbette bunun olmasını istemeyiz. İki mühendisin doğrudan ana set yerine ayrı kopyalar üzerinde çalışmasını sağlayarak durumu kontrol etmiş olsak da (aşağıdaki resimde olduğu gibi) kopyalar bir şekilde uzlaştırılmalıdır. Çoğu SCM sistemi, birden fazla mühendisin bir dosyayı kontrol etmesine ("senkronize etme" veya "güncelleme") ve gerektiğinde değişiklik yapmasına izin vererek bu sorunu çözer. SCM sistemi, daha sonra dosyalar tekrar depoya gönderilirken ("gönder" veya "kaydet") değişiklikleri birleştirmek için algoritmalar çalıştırır.

Bu algoritmalar basit olabilir (mühendislerden çelişen değişiklikleri çözmelerini isteyin) veya o kadar basit olmayabilir (çakışan değişikliklerin akıllı bir şekilde nasıl birleştirileceğini belirleyin ve yalnızca sistem gerçekten takılıp kalırsa bir mühendise soru sorun). 

Sürüm oluşturma

Sürüm oluşturma, dosyanın önceki bir sürümünün yeniden oluşturulmasını (veya geri döndürülmesini) mümkün kılan dosya düzeltmelerinin takip edilmesini ifade eder. Bu işlem, depoya giriş yapıldığında her dosyanın arşiv kopyası oluşturularak veya dosyada yapılan her değişiklik kaydedilerek yapılır. Dilediğimiz zaman arşivleri kullanabilir veya bilgileri değiştirerek eski bir sürümü oluşturabiliriz. Sürüm oluşturma sistemleri, değişiklikleri kimin yaptığı, ne zaman giriş yaptıkları ve değişikliklerin ne olduğuna dair günlük raporları da oluşturabilir.

Senkronizasyon

Bazı SCM sistemlerinde her bir dosya depoya giriş ve çıkış yapar. Daha güçlü sistemler, aynı anda birden fazla dosyaya göz atmanıza olanak tanır. Mühendisler, deponun (veya bir kısmının) kendilerine ait, eksiksiz bir kopyasını kontrol eder ve gerektiğinde dosyalar üzerinde çalışırlar. Daha sonra değişiklikleri düzenli aralıklarla tekrar ana depoya uygular ve diğer kullanıcıların yaptığı değişikliklerden haberdar olmak için kendi kişisel kopyalarını güncellerler. Bu işleme, senkronizasyon veya güncelleme denir.

Yıkım

Subversion (SVN) açık kaynaklı bir sürüm kontrol sistemidir. Yukarıda açıklanan özelliklerin tümüne sahiptir.

SVN, çatışmalar meydana geldiğinde basit bir metodoloji kullanır. İki veya daha fazla mühendis, kod tabanının aynı alanında farklı değişiklikler yapıp ardından değişikliklerini gönderdiğinde çakışma meydana gelir. SVN yalnızca mühendislere bir çakışma olduğu konusunda uyarı verir. Bu sorunun çözümü mühendislere aittir.

Bu kurs boyunca, yapılandırma yönetimini tanımanıza yardımcı olmak için SVN'yi kullanacağız. Bu tür sistemler endüstride çok yaygındır.

İlk adım, sisteminize SVN'yi yüklemektir. Talimatlar için burayı tıklayın. İşletim sisteminizi bulun ve uygun ikili dosyayı indirin.

Bazı SVN Terminolojileri

  • Düzeltme: Bir dosya veya dosya grubunda yapılan bir değişiklik. Düzeltme, sürekli olarak değişen bir projenin önemli anlarından biridir.
  • Depo: SVN'nin bir projenin tüm düzeltme geçmişini depoladığı ana kopya. Her projenin bir deposu vardır.
  • Çalışan Kopya: Bir mühendisin projede değişiklik yaptığı kopya. Belirli bir projenin her biri ayrı bir mühendise ait olan birçok çalışan kopyası olabilir.
  • Çıkış: Kod deposundan çalışan bir kopya istemek için. Çalışan bir kopya, projenin kontrol edildiği zamanki durumuna eşittir.
  • Kaydetme: Çalışma kopyanızdaki değişiklikleri merkezi depoya göndermek için. Check-in veya gönderme olarak da bilinir.
  • Güncelleme: Diğer kullanıcıların değişiklikleri depodaki çalışma kopyanıza aktarmak veya çalışan kopyanızda kaydedilmemiş değişiklikler olup olmadığını belirtmek için. Bu işlem, yukarıda açıklandığı gibi senkronizasyonla aynıdır. Bu nedenle, güncelleme/senkronizasyon, çalışan kopyanızı depo kopyasıyla güncel tutar.
  • Uyuşmazlık: İki mühendisin dosyanın aynı alanında değişiklikler uygulamaya çalışması durumudur. SVN çakışmaları gösterir ancak mühendislerin bunları çözmesi gerekir.
  • Günlük mesajı: Bir düzeltmeye gittiğinizde eklediğiniz ve değişikliklerinizi açıklayan bir yorum. Günlük, projede olan bitenlerin özetini sağlar.

Artık SVN hizmetini yüklediğinize göre bazı temel komutları inceleyeceğiz. İlk olarak belirli bir dizinde depo kurulumu yapmanız gerekir. Komutlar şunlardır:

$ 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 komutu, dizin mytree içeriğini depodaki dizin projesine kopyalar. Depodaki dizine list komutuyla göz atabiliriz

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

İçe aktarma işlemi, çalışan bir kopya oluşturmaz. Bunu yapmak için svn payment komutunu kullanmanız gerekir. Bu işlem, dizin ağacının çalışan bir kopyasını oluşturur. Şimdi bunu yapalım:

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

Artık çalışan bir kopyanız olduğuna göre, buradaki dosya ve dizinlerde değişiklik yapabilirsiniz. Çalışan kopyanız, diğer dosya ve dizin koleksiyonları gibidir. Yenilerini ekleyebilir veya düzenleyebilir, taşıyabilir, hatta çalışan kopyanın tamamını silebilirsiniz. Çalışan kopyanızdaki dosyaları kopyalayıp taşırsanız işletim sistemi komutlarınız yerine svn kopyası ve svn taşıma komutlarını kullanmanın önemli olduğunu unutmayın. Yeni bir dosya eklemek için svn add, bir dosyayı silmek için ise svn sil işlevini kullanın. Tek yapmanız gereken düzenleme yapmaksa dosyayı düzenleyicinizle açıp düzenleyin.

Subversion ile sıklıkla kullanılan bazı standart dizin adları vardır. "Gövde" dizini, projenizin ana geliştirme hattını barındırır. "Şubeler" dizini, üzerinde çalışıyor olabileceğiniz tüm dal sürümlerini içerir.

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

Çalışan kopyanızda gerekli tüm değişiklikleri yaptığınızı ve bu kopyayı depoyla senkronize etmek istediğinizi varsayalım. Deponun bu alanında çok sayıda mühendis çalışıyorsa çalışma kopyanızı güncel tutmanız önemlidir. Yaptığınız değişiklikleri görüntülemek için svn status komutunu kullanabilirsiniz.

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

Durum komutunda bu çıkışı kontrol etmek için çok sayıda işaret bulunduğunu unutmayın. Değiştirilen bir dosyadaki belirli değişiklikleri görüntülemek istiyorsanız svn diff işlevini kullanın.

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

Son olarak, kod deposundaki çalışan kopyanızı güncellemek için svn update komutunu kullanın.

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

Burası çatışmaların yaşanabileceği yerlerden biridir. Yukarıdaki çıkıştaki "U", bu dosyaların depo sürümlerinde herhangi bir değişiklik yapılmadığını ve güncellemenin yapıldığını gösterir. "G", birleştirme işleminin gerçekleştiği anlamına gelir. Depo sürümü değiştirilmiş ancak değişiklikler sizinkiyle çelişmemiştir. "C", çakışmayı gösterir. Bu, depodaki değişikliklerin sizinkilerle çakıştığı ve artık bunlar arasında seçim yapmanız gerektiği anlamına gelir.

Subversion, çakışan her dosya için çalışan kopyanıza üç dosya yerleştirir:

  • file.mine: Bu, çalışma kopyanızı güncellemeden önce çalışan kopyanızda bulunduğu haliyle dosyanızdır.
  • file.rOLDREV: Bu, değişikliklerinizi yapmadan önce depodan çıkış yaptığınız dosyadır.
  • file.rNEWREV: Bu dosya, depodaki geçerli sürümdür.

Çakışmayı gidermek için aşağıdaki üç işlemden birini yapabilirsiniz:

  • Dosyaların üzerinden geçin ve birleştirme işlemini manuel olarak yapın.
  • SVN tarafından oluşturulan geçici dosyalardan birini çalışan kopya sürümünüzün üzerine kopyalayın.
  • Yaptığınız tüm değişiklikleri kaldırmak için svn refund komutunu çalıştırın.

Çakışmayı çözdükten sonra svn çözümlendi komutunu çalıştırarak SVN'yi bilgilendirirsiniz. Bu işlem, üç geçici dosyayı kaldırır ve SVN dosyayı artık çakışma durumunda görüntülemez.

Yapmanız gereken son şey, son sürümünüzü kod deposuna kaydetmektir. Bu işlem svn kaydetme komutuyla gerçekleştirilir. Bir değişiklik yaptığınızda, değişikliklerinizi açıklayan bir günlük mesajı sağlamanız gerekir. Bu günlük mesajı, oluşturduğunuz düzeltmeye eklenir.

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

SVN ve büyük yazılım mühendisliği projelerini nasıl destekleyebileceği hakkında öğrenilecek çok daha fazla şey var. Web'de çok sayıda kaynak bulabilirsiniz. Bunun için Google'da "Subversion" araması yapmanız yeterlidir.

Pratik yapmak istiyorsanız Composer Database sisteminiz için bir depo oluşturun ve tüm dosyalarınızı içe aktarın. Ardından, çalışan bir kopyaya ödeme yapın ve yukarıda açıklanan komutları uygulayın.

Referanslar

Online Dönüş Kitabı

SVN ile ilgili Wikipedia makalesi

Alt sürüme geçiş web sitesi

Uygulama: Anatomi Çalışması

University of Texas at Austin'den eSkeletons'a göz atın