Skip to content

7. Katmanlı mimari ve arayüzlerle programlama

7.1. Introduction

Bir ortaokul öğrencilerinin notlarını görüntüleyen bir uygulama yazmayı planlıyoruz. Bu uygulama çok katmanlı bir mimariye sahip olacaktır:

  • [présentation] katmanı, uygulamanın kullanıcıyla etkileşimde bulunan katmandır.
  • [metier] katmanı, maaş veya fatura hesaplaması gibi uygulama yönetim kurallarını uygular. Bu katman, [présentation] katmanı aracılığıyla kullanıcıdan gelen verileri ve SGBD katmanından [dao] katmanı aracılığıyla gelen verileri kullanır.
  • [dao] katmanı (Veri Erişim Nesneleri), SGBD'teki verilere erişimi yönetir.

Bu mimariyi basit bir konsol uygulamasıyla açıklayacağız:

  • veritabanı olmayacak,
  • [dao] katmanı, öğrencilerin notlarını yönetmeye olanak tanıyan Eleve, Classe, Matiere ve Note varlıklarını yönetecek,
  • [métier] katmanı, belirli bir öğrencinin notlarına ilişkin göstergeleri hesaplayacak,
  • [présentation] katmanı, [métier] katmanı tarafından hesaplanan sonuçları görüntüleyen bir konsol uygulaması olacaktır.

Uygulamanın Visual Studio projesi şu şekildedir:

  

7.2. Uygulamanın varlıklar

Varlıklar, nesnelerdir. Burada Eleve, Classe, Matiere ve Note varlıklarının her biri için dört sınıfımız olacaktır.

[entites.py] dosyası dört sınıfı içermektedir.

[Classe] sınıfı, bir ortaokul sınıfını temsil eder:


class Classe:
    # yapıcı
    def __init__(self,id,nom):
        # parametreler kaydedilir
        self.id=id
        self.nom=nom

    # toString
    def __str__(self):
        return "Classe[{0},{1}]".format(self.id,self.nom)
  • 3-6. satırlar: Bir sınıf, bir id numarası (5. satır) ve bir nom numarası (6. satır) ile tanımlanır;
  • 9-10. satırlar: sınıfın görüntülenme yöntemi.

[Matiere] sınıfı şu şekildedir:


class Matiere:
    # üretici
    def __init__(self,id,nom,coefficient):
        # parametreler kaydediliyor
        self.id=id
        self.nom=nom
        self.coefficient=coefficient

    # toString
    def __str__(self):
        return "Matiere[{0},{1},{2}]".format(self.id,self.nom,self.coefficient)

  • 3-7. satırlar: bir ders, numarası (5. satır), adı (6. satır) ve katsayısı (7. satır) ile tanımlanır;
  • satır 10-11: dersin görüntüleme yöntemi.

[Eleve] sınıfı şu şekildedir:


class Eleve:
    # üretici
    def __init__(self,id,nom,prenom,classe):
        # parametreler kaydediliyor
        self.id=id
        self.nom=nom
        self.prenom=prenom
        self.classe=classe

    # toString
    def __str__(self):
        return "Eleve[{0},{1},{2},{3}]".format(self.id,self.prenom,self.nom,self.classe)

  • 3-8. satırlar: Bir öğrenci, numarası (5. satır), soyadı (6. satır), adı (7. satır) ve sınıfı (8. satır) ile tanımlanır. Bu son parametre, [Classe] nesnesine bir referanstır;
  • 11-12. satırlar: öğrencinin görüntülenme yöntemi.

[Note] sınıfı şu şekildedir:


class Note:
    # üretici
    def __init__(self,id,valeur,eleve,matiere):
        # parametreler kaydediliyor
        self.id=id
        self.valeur=valeur
        self.eleve=eleve
        self.matiere=matiere

    # toString
    def __str__(self):
        return "Note[{0},{1},{2},{3}]".format(self.id,self.valeur,self.eleve,self.matiere)

  • satır 3-8: bir [Note] nesnesi, numarası (satır 5), not değeri (satır 6), bu nota sahip öğrenciye ait referans (satır 7) ve notun ait olduğu ders referansı (satır 8) ile tanımlanır;
  • 11-12. satırlar: [Note] nesnesinin görüntülenme yöntemi.

7.3. [dao] katmanı

[dao] katmanı, [métier] katmanına aşağıdaki arayüzü sunacaktır:

  • getClasses, ortaokul sınıflarının listesini döndürür;
  • getMatieres, derslerin listesini döndürür;
  • getEleves, öğrenci listesini döndürür;
  • getNotes, notların listesini döndürür.

[métier] katmanı yalnızca bu yöntemleri kullanacaktır. Bu yöntemlerin nasıl uygulandığını bilmesi gerekmez. Bu veriler, [métier] katmanını etkilemeden farklı kaynaklardan (sabit veriler, veritabanı, metin dosyaları vb.) gelebilir. Buna arayüz tabanlı programlama denir.

[Dao] sınıfı bu arayüzü şu şekilde uygular:


# -*- coding=utf-8 -*-

# nesne modülünü içe aktarma
from entites import *

class Dao:
    # yapıcı
    def __init__(self):
        # sınıfları örneklendiriyoruz
        classe1=Classe(1,"classe1")
        classe2=Classe(2,"classe2")
        self.classes=[classe1,classe2]
        # dersler
        matiere1=Matiere(1,"matiere1",1)
        matiere2=Matiere(2,"matiere2",2)
        self.matieres=[matiere1,matiere2]
        # öğrenciler
        eleve11=Eleve(11,"nom1","prenom1",classe1)
        eleve21=Eleve(21,"nom2","prenom2",classe1)
        eleve32=Eleve(32,"nom3","prenom3",classe2)
        eleve42=Eleve(42,"nom4","prenom4",classe2)
        self.eleves=[eleve11,eleve21,eleve32,eleve42]
        # notlar
        note1=Note(1,10,eleve11,matiere1)
        note2=Note(2,12,eleve21,matiere1)
        note3=Note(3,14,eleve32,matiere1)
        note4=Note(4,16,eleve42,matiere1)
        note5=Note(5,6,eleve11,matiere2)
        note6=Note(6,8,eleve21,matiere2)
        note7=Note(7,10,eleve32,matiere2)
        note8=Note(8,12,eleve42,matiere2)
        self.notes=[note1,note2,note3,note4,note5,note6,note7,note8]

    #-----------
    # arayüz
    #-----------
    
    # sınıf listesi
    def getClasses(self):
        return self.classes

    # ders listesi
    def getMatieres(self):
        return self.matieres
    
    # öğrenci listesi
    def getEleves(self):
        return self.eleves

    # not listesi
    def getNotes(self):
        return self.notes

    1. satır: [dao] katmanı tarafından işlenen varlıkları içeren modül içe aktarılır;
    1. satır: Oluşturucunun parametresi yoktur. Dört listeyi sabit olarak oluşturur:
    2. satır 10-12: sınıflar listesi;
    3. 14-16. satırlar: ders listesi;
    4. satır 18-22: öğrenci listesi;
    5. 24-32. satırlar: not listesi.
  • satır 39-52: [dao] katmanının arayüzündeki dört yöntem, oluşturucu tarafından oluşturulan dört listeye bir referans döndürmekle yetinir.

Bir test programı şu şekilde olabilir:


# -*- coding=utf-8 -*-

# varlık modülünün ve katmanın içe aktarılması [dao]
from entites import *
from dao import *

# [dao] katmanının örneklenmesi
dao=Dao()

# sınıf listesi
for classe in dao.getClasses():
    print classe

# sınıf listesi
for matiere in dao.getMatieres():
    print matiere

# sınıf listesi
for eleve in dao.getEleves():
    print eleve

# sınıf listesi
for note in dao.getNotes():
    print note

Yorumlar kendiliğinden anlaşılır niteliktedir. 11-24. satırlar, [dao] katman arayüzünü kullanır. Burada, katmanın gerçek uygulaması hakkında herhangi bir varsayım yoktur. 8. satırda, [dao] katmanının bir örneği oluşturulur. Burada bazı varsayımlarda bulunulur: sınıf adı ve yapıcı türü. Bu bağımlılığı ortadan kaldıran çözümler mevcuttur.

Bu komut dosyasının çalıştırılması sonucunda elde edilen sonuçlar şunlardır:

Classe[1,classe1]
Classe[2,classe2]
Matiere[1,matiere1,1]
Matiere[2,matiere2,2]
Eleve[11,prenom1,nom1,Classe[1,classe1]]
Eleve[21,prenom2,nom2,Classe[1,classe1]]
Eleve[32,prenom3,nom3,Classe[2,classe2]]
Eleve[42,prenom4,nom4,Classe[2,classe2]]
Note[1,10,Eleve[11,prenom1,nom1,Classe[1,classe1]],Matiere[1,matiere1,1]]
Note[2,12,Eleve[21,prenom2,nom2,Classe[1,classe1]],Matiere[1,matiere1,1]]
Note[3,14,Eleve[32,prenom3,nom3,Classe[2,classe2]],Matiere[1,matiere1,1]]
Note[4,16,Eleve[42,prenom4,nom4,Classe[2,classe2]],Matiere[1,matiere1,1]]
Note[5,6,Eleve[11,prenom1,nom1,Classe[1,classe1]],Matiere[2,matiere2,2]]
Note[6,8,Eleve[21,prenom2,nom2,Classe[1,classe1]],Matiere[2,matiere2,2]]
Note[7,10,Eleve[32,prenom3,nom3,Classe[2,classe2]],Matiere[2,matiere2,2]]
Note[8,12,Eleve[42,prenom4,nom4,Classe[2,classe2]],Matiere[2,matiere2,2]]

7.4. [métier] katmanı

[métier] katmanı aşağıdaki arayüzü uygular:

  • getClasses, ortaokul sınıflarının listesini görüntüler;
  • getMatieres, derslerin listesini görüntüler;
  • getEleves, öğrenci listesini görüntüler;
  • getNotes, notların listesini döndürür;
  • getStatsForEleve, idEleve numaralı öğrencinin notlarını ve bunlarla ilgili bilgileri (ağırlıklı ortalama, en düşük not, en yüksek not) döndürür.

[présentation] katmanı yalnızca bu yöntemleri kullanacaktır. Bu yöntemlerin nasıl uygulandığını bilmesi gerekmez.

getStatsForEleve yöntemi, aşağıdaki [StatsForEleve] türünde bir nesne döndürür:


class StatsForEleve:
    # yapıcı
    def __init__(self, eleve, notes):
        # parametreleri kaydedilir
        self.eleve=eleve
        self.notes=notes
        # not yoksa durulur
        if len(notes)==0:
            return
        # notların işlenmesi
        sommePonderee=0
        sommeCoeff=0
        self.max=-1
        self.min=21
        for note in notes:
            valeur=note.valeur
            coeff=note.matiere.coefficient
            sommeCoeff+=coeff
            sommePonderee+=valeur*coeff
            if valeur<self.min:
                self.min=valeur
            if valeur>self.max:
                self.max=valeur
        # öğrencinin ortalamasının hesaplanması
        self.moyennePonderee=float(sommePonderee)/sommeCoeff
        
    # toString
    def __str__(self):
        # notu olmayan öğrenci durumu
        if len(self.notes)==0:
            return "Eleve={0}, notes=[]".format(self.eleve)
        # notları olan öğrenci durumu
        str=""
        for note in self.notes:
            str+="{0} ".format(note.valeur)
        return "Eleve={0}, notes=[{1}], max={2}, min={3}, moyenne={4}".format(self.eleve, str, self.max, self.min, self.moyennePonderee)

    1. satır: Oluşturucu iki parametre alır:
    2. göstergelerin hesaplanacağı [Eleve] türündeki öğrenciye ait bir referans;
    3. notlarına ilişkin bir referans, bir [Note] nesneleri listesi.
  • 8-9. satırlar: not listesi boşsa, işlem devam etmez.
  • Aksi takdirde 11-25. satırlarda aşağıdaki göstergeler hesaplanır:
    • self.moyennePonderee: ders katsayılarıyla ağırlıklandırılmış öğrencinin ortalaması;
    • self.min: öğrencinin en düşük notu;
    • self.max: öğrencinin en yüksek notu.
    1. satır: sınıfın 36. satırda belirtilen biçimde görüntülenmesi yöntemi.

Bu sınıf için bir test komut dosyası şu şekilde olabilir:


# -*- coding=utf-8 -*-

# varlık modüllerinin, [dao] katmanının ve [metier] katmanının içe aktarılması
from entites import *
from dao import *
from metier import *

# bir sınıf
classe1=Classe(1,"6e A")
# bu sınıftaki bir öğrenci
paul_durand=Eleve(1,"durand","paul",classe1)
# üç ders
maths=Matiere(1,"maths",1)
francais=Matiere(2,"francais",2)
anglais=Matiere(3,"anglais",3)
# öğrencinin bu derslerdeki notları
note_maths=Note(1,10,paul_durand,maths)
note_francais=Note(2,12,paul_durand,francais)
note_anglais=Note(3,14,paul_durand,anglais)
# göstergeler görüntülenir
print StatsForEleve(paul_durand,[note_maths, note_francais,note_anglais])

Ekran çıktısı şu şekildedir:

Eleve=Eleve[1,paul,durand,Classe[1,6e A]], notes=[10 12 14 ], max=14, min=10, moyenne=12.6666666667

Katmanlı mimarimize geri dönelim:

[Metier] sınıfı, [métier] katmanını şu şekilde uygular:


class Metier:
    # oluşturucu
    def __init__(self,dao):
        # referans, [dao] katmanına kaydedilir
        self.dao=dao

    #-----------
    # arayüz
    #-----------

    # notlardaki göstergeler
    def getStatsForEleve(self,idEleve):
        # idEleve numaralı öğrencinin istatistikleri
        # öğrenci arama
        trouve=False
        i=0
        eleves=self.getEleves()
        while not trouve and i<len(eleves):
            trouve=eleves[i].id==idEleve
            i+=1
        # bulundu mu?
        if not trouve:
            raise RuntimeError("L'eleve [{0}] n'existe pas".format(idEleve))
        else:
            eleve=eleves[i-1]
        # Tüm notların listesi
        notes=[]
        for note in self.getNotes():
            # Öğrencinin tüm notları notlar listesine ekleniyor
            if note.eleve.id==idEleve:
                notes.append(note)
        # sonuç verilir
        return StatsForEleve(eleve,notes)

    # sınıf listesi
    def getClasses(self):
        return self.dao.getClasses()
    
    # derslerin listesi
    def getMatieres(self):
        return self.dao.getMatieres()
    
    # öğrenci listesi
    def getEleves(self):
        return self.dao.getEleves()
    
    # not listesi
    def getNotes(self):
        return self.dao.getNotes()

  • 3-5. satırlar: Oluşturucu, [dao] katmanına bir referans alır. [métier] katmanı bu referansa sahip olmalıdır. Burada, bu referans ona yapıcı aracılığıyla verilir. Başka çözümler de düşünülebilir. Yatay olarak gösterilen bir katman mimarisinde, her katman sağındaki katmana bir referansa sahip olmalıdır;
  • satır 36-49: getClasses, getMatieres, getEleves, getNotes yöntemleri, çağrıyı [dao] katmanındaki aynı isimli yöntemlere devretmekle yetinir;
    1. satır: getStatsForEleve yöntemi, göstergelerin verilmesi gereken öğrencinin numarasını parametre olarak alır.
    1. satır: Öğrenci, tüm öğrenciler listesinde aranacaktır;
  • 18-20. satırlar: arama döngüsü;
    1. satır: Öğrenci bulunamazsa bir istisna atılır;
  • aksi takdirde 25. satırda, bulunan öğrenci kaydedilir;
  • 28-31. satırlar: Ortaokul notları kümesinden, saklanan öğrenciye ait notlar aranır;
  • bulunduklarında, istenen StatsForEleve nesnesi oluşturulabilir.

7.5. [console] katmanı

[console] katmanı aşağıdaki komut dosyası ile uygulanır:


# -*- coding=utf-8 -*-

# varlık modülünün, [dao] modülünün ve [metier] modülünün içe aktarılması
from entites import *
from dao import *
from metier import *

# ----------- [console] katmanı
# [metier] katmanının örneklenmesi
metier=Metier(Dao())
# istek / yanıt
fini=False
while not fini:
    # soru
    print "numero de l'eleve (>=1 et * pour arreter) : "
    # yanıt
    reponse=raw_input()
    # bitti mi?
    if reponse.strip()=="*":
        break
    # giriş doğru mu?
    ok=False
    try:
        idEleve=int(reponse,10)
        ok=idEleve>=1
    except:
        pass
    # veri doğru mu?
    if not ok:
        print "Saisie incorrecte. Recommencez..."
        continue
    # hesaplama
    try:
        print metier.getStatsForEleve(idEleve)
    except RuntimeError,erreur:
        print "L'erreur suivante s'est produite : {0}".format(erreur)

    1. satır: Hem [dao] hem de [métier] katmanlarının örneklenmesi. Bu, kodumuzun bu katmanların uygulamasıyla ilgili tek bağımlılığıdır;
    1. satır: [métier] katmanının arayüzü kullanılır;
    1. satır: strip yöntemi, dizgenin başındaki ve sonundaki boşlukları kaldırır;
    1. satır: break, döngüden çıkmamızı sağlar;
    1. satır: Girilen dizeyi ondalık tamsayıya dönüştürmeye çalışılır;
    1. satır: ok, yalnızca 25. satırdan geçilmişse doğrudur;
  • satır 31: continue, döngünün ortasından yeniden döngüye girilmesini sağlar;
  • satır 34: göstergelerin hesaplanması;
  • satır 35: [métier] katmanından çıkabilecek RuntimeError istisnasını yakalar.

İşte bir yürütme örneği:

numero de l'eleve (>=1 et * pour arreter) :
xx
Saisie incorrecte. Recommencez...
numero de l'eleve (>=1 et * pour arreter) :
-4
Saisie incorrecte. Recommencez...
numero de l'eleve (>=1 et * pour arreter) :
11
Eleve=Eleve[11,prenom1,nom1,Classe[1,classe1]], notes=[10 6 ], max=10, min=6, mo
yenne=7.33333333333
numero de l'eleve (>=1 et * pour arreter) :
111
L'erreur suivante s'est produite : L'eleve [111] n'existe pas
numero de l'eleve (>=1 et * pour arreter) :
*