UML diyagram türlerinin çoğu ders kitaplarında yaşar. Sınıf diyagramı ise mühendislerin hâlâ çizdiği türdür: bir sistemin konu aldığı şeylerin, her birinin neyi tuttuğunun, neler yapabildiğinin ve birbirleriyle nasıl ilişkilendiğinin resmi. İyi çizildiğinde, kimse bir migration ya da API yazmadan önce alan modeli üzerindeki tartışmaları bitirir.
Bu yazı, küçük bir online kurs platformu için UML sınıf diyagramını her seferinde tek bir kararla kuruyor ve notasyonu yeri geldikçe açıklıyor: görünürlük, çokluk, ilişki, kompozisyon, kalıtım ve arayüzler. Sonunda sınıf diyagramlarını işe yaramaz hale getiren hatalara ve diyagramın koda nasıl karşılık geldiğine kısaca bakıyor.
Sınıf diyagramı ne zaman doğru resimdir
UML 2.5.1 spesifikasyonu diyagramlarını yapı diyagramları ve davranış diyagramları olarak ikiye ayırır. Sınıf diyagramı bir yapı diyagramıdır: türleri, onların niteliklerini ve işlemlerini ve aralarındaki ilişkileri gösterir; zaman kavramı yoktur.
| Yanıtlamanız gereken soru | Diyagram |
|---|---|
| Hangi kavramlar var, ne biliyorlar, nasıl ilişkililer | Sınıf diyagramı |
| Veri nasıl saklanıyor: tablolar, anahtarlar, yabancı anahtarlar | ER diyagramı |
| Katılımcılar arasında hangi sırayla ne oluyor | Sekans diyagramı |
| Bir nesne hangi durumlardan geçiyor | Durum makinesi diyagramı |
Sınıf diyagramı bir alan modeli, bir kütüphanenin herkese açık türleri ya da yapmak üzere olduğunuz bir tasarım için yerini hak eder. Bir depodaki her sınıfın dökümü olarak ise hak etmez: bunu bir IDE daha iyi gösterir ve resim bir sonraki commit'te eskir.
İki tabloda notasyon
Sınıf, en fazla üç bölmeli bir kutudur: ad, nitelikler ve işlemler. Herhangi bir bölme çıkarılabilir.
| Öğe | Notasyon | Anlamı |
|---|---|---|
| Soyut sınıf | Ad italik ya da {abstract} |
Örneklenemez; alt sınıflar onu tamamlar |
| Arayüz | Adın üstünde «interface» |
İşlemlerden oluşan bir sözleşme |
| Görünürlük | + public, - private, # protected, ~ package |
Özelliği kimin kullanabileceği |
| Nitelik | name: Type [multiplicity] = default |
Nesnenin tuttuğu veri |
| İşlem | name(param: Type): ReturnType |
Nesnenin sunduğu davranış |
| Statik özellik | Altı çizili | Bir örneğe değil, sınıfa aittir |
İlişkiler sınıflar arasındaki çizgilerdir ve anlamın çoğunu uçlar taşır:
| İlişki | Çizgi | Okunuşu |
|---|---|---|
| İlişki (association) | Düz çizgi; açık ok başı gezinilebilir ucu gösterir | "ile ilişkilidir" |
| Birleştirme (aggregation) | Düz çizgi, bütünün tarafında içi boş elmas | "gruplar"; parçalar kendi başına yaşar |
| Kompozisyon | Düz çizgi, bütünün tarafında içi dolu elmas | "şunlardan oluşur"; parçalar bütünle birlikte gider |
| Genelleme | Düz çizgi, üst sınıfta içi boş üçgen | "bir türüdür" |
| Gerçekleştirme | Kesikli çizgi, arayüzde içi boş üçgen | "uygular" |
| Bağımlılık | Kesikli çizgi, hedefte açık ok başı | "kullanır"; hedefteki bir değişiklik kaynağı bozabilir |
Katı olan kompozisyondur. Spesifikasyona göre bir parça nesnesi aynı anda en fazla bir bileşik nesneye aittir ve bileşik silindiğinde parçaları da onunla birlikte silinir. Paylaşımlı birleştirme, yani içi boş elmas, belirsiz olandır: spesifikasyonun kendisi, kesin anlamının uygulama alanına ve modelleyene göre değiştiğini söyler. Onu yalnızca ekibiniz ne anlama geldiği konusunda anlaşıyorsa kullanın; düz bir ilişki aynı şeyi daha az tartışmayla söyler.
Çokluk
Çokluk (multiplicity), bir ilişkinin bir ucunda kaç nesne bulunabileceğini söyler: tam bir tane için 1, isteğe bağlı için 0..1, herhangi bir sayı için * (0..* ile aynıdır), en az bir tane için 1..* ve 2..5 gibi aralıklar. Çokluğu, saydığı sınıfın yanındaki uca yazın. Course 1 — 1..* Module ifadesini şöyle okuyun: "bir kursun bir ya da daha fazla modülü vardır ve her modül tam olarak bir kursa aittir."
Eksik çokluklar, bir sınıf diyagramının tartışmaları bitirmek yerine başlatmasının en yaygın nedenidir. Aşağıdaki örnekte her ilişkinin iki ucu da doldurulmuştur.
Örnek: bir online kurs platformu
Kutulardan değil, düz cümlelerden başlayın:
- Eğitmenler kurs yayımlar. Bir kursun bir ya da daha fazla eğitmeni vardır.
- Bir kurs sıralı modüllerden, bir modül de sıralı derslerden oluşur.
- Bir ders bir video, bir quiz ya da bir ödevdir.
- Quiz'ler ve ödevler notlandırılır; videolar notlandırılmaz.
- Öğrenciler kurslara kaydolur. Bir kayıt ilerlemeyi izler ve öğrencinin teslimlerini tutar.
- Bir kursu tamamlayan öğrenci, o kayıt için bir sertifika alır.
- Kurslar kategorilere ayrılır ve bir kurs birden fazla kategoride olabilir.
Kullanıcılar: tek sınıf, iki rol
İlk içgüdü Student ve Instructor'ı User'ın alt sınıfları yapmaktır. Bu, bir eğitmen başka birinin kursuna ilk kez katıldığında bozulur: çoğu programlama dilinde bir nesne tüm ömrü boyunca tek bir sınıfa sahiptir, yani ikisi birden olamaz ya da sonradan değiştiremez. Rolleri bunun yerine ilişki olarak modelleyin: bir User kurs verir ve bir User kayıtlara sahiptir. Kalıtımı, tüm ömrü boyunca bir şeyin türü olan şeylere saklayın; bir quiz'in bir ders türü olması gibi.
Yaşam döngüleri bağlı olduğunda kompozisyon
Bir modül kursunun dışında bir anlam taşımaz, bir ders de modülünün dışında. Bir kursu silmek ikisini de siler. Bu kompozisyondur: Course'ta ve Module'de içi dolu birer elmas, bütün tarafında 1. Kategoriler farklıdır. "Veri mühendisliği" kategorisini silmek kurslarını silmemelidir; bu yüzden Category ile Course arası, iki ucunda da * olan bir birleştirmedir.
Kalıtım artı bir arayüz
Lesson soyuttur; VideoLesson, Quiz ve Assignment onun başlığını ve sırasını kalıtımla alır. Notlandırma bu ağacı enine keser: quiz'ler ve ödevler notlandırılır, videolar notlandırılmaz ve gelecek yıl eklenecek bir akran değerlendirmesi hiç ders olmadan notlandırılabilir. Bu bir arayüzdür: Quiz ve Assignment tarafından gerçekleştirilen Gradable. GradingService somut sınıflara değil Gradable'a bağımlıdır ve kesikli bağımlılık oku tam olarak bunu söyler.
Kendi verisi olan bir ilişki
Enrollment, User ile Course arasında durur. UML'de bunun için özel bir notasyon vardır: çizgiye kesikli bir çizgiyle bağlanan ilişki sınıfı (association class). Çoğu ekip onu bunun yerine iki ilişkisi olan sıradan bir sınıf olarak çizer, çünkü kendi kimliği, verisi ve davranışı vardır: kayıt tarihi, ilerleme, teslimler ve en fazla bir sertifika.
Diyagram içeriği
- Category
- Course
- User
- Module
- Enrollment
- Certificate
- Lesson
- Submission
- VideoLesson
- Quiz
- Assignment
- Gradable
- GradingService
- groups
- teaches
- enrolls
- for
- earns
- holds
- answers
- «use»
Nasıl okunur:
- Sol üst, katalog.
Course,Module'den; o daLesson'dan oluşur; içi dolu elmaslar bütünün tarafındadır.Categorykursları içi boş bir elmasla gruplar. - Sol alt, ders türleri.
Lessonsoyuttur.VideoLesson,QuizveAssignmentona içi boş üçgenlerle işaret eder. - Sağ, öğrenme. Bir
Userkurs verir (kurs başına1..*eğitmen) ve her biri tam olarak bir kursa ait olan, istediği sayıda kayda sahiptir. BirEnrollmentteslimleri tutar ve en fazla birCertificatekazanır. - Sağ alt, notlandırma.
QuizveAssignment, kesikli çizgiler ve içi boş üçgenlerleGradable'ı gerçekleştirir. HerSubmissionnotlandırılabilir bir öğeye yanıt verir veGradingServiceyalnızca arayüze bağımlıdır.
Yaygın hatalar
- Kod tabanındaki her sınıf. Diyagram başına on ile on beş sınıf iyi bir sınırdır. Daha büyük bir modeli alanlara bölün ya da geri kalanını bölmesiz adlar olarak gösterin.
- İşlem olarak getter ve setter'lar. Erişimcileri değil,
publish()ya daenroll()gibi alan anlamı taşıyan işlemleri listeleyin. - Sınıf olarak çizilmiş tablolar.
courseIdgibi nitelikler ER diyagramına aittir. Sınıf diyagramında referans, ilişki çizgisinin kendisidir. - Roller için kalıtım. Bir nesne rolü kazanabiliyor ya da kaybedebiliyorsa, bu bir alt sınıf değil, bir ilişkidir.
- Anlamsız elmaslar. Bütünü silmenin parçalara ne yaptığını kimse söyleyemiyorsa, düz bir ilişki kullanın.
- Alan düzeyinde uygulama ayrıntısı. Diyagram onlarla ilgili değilse framework anotasyonlarını, DTO'ları ve repository'leri dışarıda bırakın.
Diyagramdan koda
Diyagram doğrudan koda karşılık gelir. TypeScript'te:
interface Gradable {
maxScore(): number;
autoGrade(submission: Submission): Score | null;
}
abstract class Lesson {
constructor(public title: string, public position: number) {}
}
class Quiz extends Lesson implements Gradable {
questions: Question[] = [];
maxScore(): number {
return this.questions.length;
}
autoGrade(submission: Submission): Score | null {
return submission.scoreAgainst(this.questions);
}
}
class Course {
private readonly modules: Module[] = []; // composition: owned and removed with the course
categories: Category[] = []; // aggregation: categories exist on their own
addModule(title: string): Module {
const created = new Module(title, this.modules.length);
this.modules.push(created);
return created;
}
}
Kompozisyon sahipliğe dönüşür: kurs modüllerini kendisi oluşturur ve kurs gittikten sonra hiçbir şey bir modülü hayatta tutmaz. Birleştirme, kendi yaşam döngüsü olan bir nesneye referansa dönüşür. Kod ile diyagram uyuşmadığında diyagramı aynı pull request'te güncelleyin; diyagram bir ekran görüntüsü değil de bir bağlantı olduğunda bu çok daha kolaydır, eskimeyen mimari diyagramlar yazısında anlatıldığı gibi.
Archboard'da oluşturun
- Google ya da GitHub ile giriş yapın ve bir diyagram oluşturun.
- UML paketinden her kavram için bir Sınıf ekleyin; nitelikleri ve işlemleri her satıra bir tane gelecek şekilde yazın.
Lesson'ın stereotipiniabstractolarak ayarlayın. Gradableiçin iki işlemiyle birlikte bir Arayüz ekleyin.- Sınıfları bağlayın. Bütünün tarafındaki ok ucunu kompozisyon için Dolu elmas, birleştirme için Elmas; kalıtım için üst sınıf tarafında Üçgen olarak ayarlayın. Gerçekleştirmeyi arayüz tarafında Üçgen olan kesikli bir çizgiyle, bağımlılığı da Açık ok ile biten kesikli bir çizgiyle çizin.
- Çoklukları her ucun yanına metin olarak koyun ve ilişkileri kenar etiketinde "teaches" gibi adlandırın.
- Rollerin neden ilişki olduğu gibi kararlar için bir UML Not ekleyin.
Örnek 13 diyagram düğümü kullanıyor ve Free planın sınırları içinde kalıyor. MCP üzerinden bağlanan bir yapay zekâ ajanıyla gereksinimleri anlatıp sınıfları ona taslak olarak çizdirebilir, sonra çoklukları kendiniz düzeltebilirsiniz.
SSS
Sınıf diyagramı ile nesne diyagramı arasındaki fark nedir? Sınıf diyagramı türleri ve aralarındaki kuralları gösterir. Nesne diyagramı ise belirli bir andaki somut örnekleri, değerleri ve bağlantılarıyla gösterir; bir sınıf diyagramını gerçek bir örnekle sınamak için kullanışlıdır.
Birleştirme ile kompozisyon arasındaki fark nedir? Kompozisyonda bir parça tek bir bütüne aittir ve onunla birlikte silinir. Birleştirmede parçalar kendi başına var olabilir ve paylaşılabilir; kesin anlamı modelleyene bırakıldığı için birçok ekip bunun yerine düz bir ilişki kullanır.
Sınıf diyagramı koda birebir uymalı mı? Bir alan modeli her alana ve metoda değil, kavramlara ve ilişkilere uymalıdır. Onu hem bir ürün yöneticisinin hem de bir mühendisin okuyabileceği düzeyde tutun.
Sınıf diyagramı bir C4 bileşen diyagramından nasıl ayrılır? C4 bileşen diyagramı dağıtılabilir yapı taşlarını ve sorumluluklarını gösterir. Sınıf diyagramı bir seviye daha derine, onların içindeki türlere iner; C4 modeli diyagram örneğine bakın.
Sınıf diyagramını koddan otomatik çizebilir miyim? Birçok IDE bunu yapabilir, ancak sonuç her sınıfı gösterir ve okunması zordur. Alan modelini elle ya da bir yapay zekâ ajanıyla çizin ve önemli sınıflarla sınırlı tutun.