de_DEen_USes_ESfa_IRfr_FRhi_INid_IDjapl_PLpt_PTru_RUvizh_CNzh_TW

Berdasarkan bahan kuliah yang disediakan, panduan ini mencakup konsep dasar, sintaks, dan aplikasi praktis Bahasa Kendala Objek (OCL) dalam konteks pemodelan UML.

Objct Constraint Language (OCL)


1. Pengantar OCL

Mengapa kita membutuhkan OCL?

Meskipun diagram UML (seperti diagram kelas) sangat baik untuk memvisualisasikan struktur, mereka tidak dapat mengekspresikan semua aspek yang relevan dari suatu spesifikasi.

  • Bahasa alami bersifat ambigu.

  • Bahasa formal tradisional tidak ambigu tetapi terlalu sulit bagi modeler sistem rata-rata untuk dibaca dan ditulis.

  • OCL menjembatani kesenjangan ini: ini adalah bahasa formal yang digunakan untuk mengekspresikan kendala yang tepat, namun tetap mudah dibaca dan ditulis.

Karakteristik Utama OCL

  1. Bahasa Ekspresi Murni: Ekspresi tidak memiliki efek samping. Menilai ekspresi OCL mengembalikan nilai tetapi tidak dapat mengubah keadaan sistem. Namun, dapat menggambarkan perubahan keadaan (misalnya, dalam kondisi post).

  2. Bukan Bahasa Pemrograman: Anda tidak dapat menulis logika program, alur kontrol, atau memanggil proses/operasi yang mengubah keadaan.

  3. Bahasa Bertipe: Setiap ekspresi memiliki tipe. Ekspresi yang sah harus mematuhi aturan kesesuaian tipe. Setiap klasifikasi UML adalah tipe OCL.

  4. Penilaian Sesaat: Keadaan objek tidak dapat berubah selama penilaian ekspresi OCL.


2. Sintaksis dan Semantik Dasar

Konteks dan Self

Setiap ekspresi OCL dievaluasi dalam konteks klasifier UML tertentu (kelas, antarmuka, dll.).

  • konteks: Menentukan kelas yang dikenakan batasan.

  • self: Kata cadangan yang mengacu pada contoh khusus dari kelas konteks yang dievaluasi.

konteks Person
-- 'self' mengacu pada contoh khusus dari Person

Komentar dan Operator

  • Komentar: Ditandai dengan -- (contoh, -- ini adalah komentar).

  • Operator Infix: Anda dapat menggunakan operator matematika/logika standar (+-=<). Secara konseptual, a + b identik dengan a.+(b).

Mengakses Properti

Gunakan notasi titik untuk mengakses atribut atau operasi. Tanda kurung wajib digunakan untuk operasi, bahkan jika tidak memiliki parameter.

konteks Person
self.age             -- Mengakses atribut 'age'
self.stockPrice()    -- Memanggil operasi 'stockPrice'

3. Tipe dan Koleksi

Tipe yang Didefinisikan Secara Prima

OCL mencakup tipe standar dengan operasi tertentu:

  • Boolean: benarsalah (Operasi: danatautidakmengimplikasikanxor)

  • Integer / Real: Bilangan (Operasi: +-*/absmaxmindivmod)

  • String: Teks (Operasi: ukuranconcatsubstringkeInteger)

Koleksi

Koleksi adalah tipe abstrak yang digunakan untuk menyimpan beberapa elemen. Mereka diakses menggunakan -> notasi.

  1. Set: Himpunan matematika (tidak ada duplikat, tidak berurutan). Set {1, 2, 5}

  2. OrderedSet: Tidak ada duplikat, tetapi diurutkan berdasarkan posisi. OrderedSet {5, 4, 3}

  3. Kantong: Memungkinkan duplikat, tidak berurutan. Kantong {1, 2, 2, 5}

  4. Urutan: Memungkinkan duplikat, berurutan. Urutan {1, 2, 5, 10}

    • Catatan: The .. notasi membuat urutan bilangan bulat berurutan: Urutan {1..5}.

Operasi Kumpulan Kunci

  • Umum: ukuran()kosong()tidakKosong()mencakup(v)tidakMencakup(v)hitung(v)jumlah().

  • Iterator (Penting untuk OCL):

    • pilih(expr): Mengembalikan elemen-elemen di mana expr adalah benar.

    • reject(expr): Mengembalikan elemen-elemen di mana expr adalah salah.

    • collect(expr): Mengekstrak properti tertentu dari semua elemen ke dalam koleksi baru.

    • forAll(expr): Mengembalikan benar jika expr adalah benar untuk semua elemen.

    • exists(expr): Mengembalikan benar jika expr adalah benar untuk setidaknya satu elemen.

    • iterate(elem; acc = init | expr): Dasar umum untuk semua iterator.

Variasi Sintaks Iterator:

self.employee->select(age > 50)
self.employee->select(p | p.age > 50)
self.employee->select(p: Person | p.age > 50)

4. Menjelajahi Model UML

OCL banyak digunakan sebagai bahasa navigasi untuk menelusuri asosiasi.

Navigasi Standar

Navigasi menggunakan nama peran yang berlawanan. Tipe kembalian tergantung pada kelipatan:

  • Kelipatan 1 atau 0..1: Mengembalikan satu objek.

  • Kelipatan *: Mengembalikan Set (atau OrderedSet jika ditandai {ordered}).

konteks Company
self.manager.age > 40               -- manager adalah 0..1, mengembalikan objek Person
self.employee->notEmpty()           -- employee adalah 0..*, mengembalikan Set

Jika nama peran tidak tersedia, gunakan nama huruf kecil dari kelas di ujung lainnya (misalnya self.bank).

Kelas Asosiasi

Navigasi ke kelas asosiasi menggunakan nama huruf kecil dari kelas asosiasi.

konteks Person
self.job->size() >= 1  -- 'job' adalah kelas asosiasi antara Person dan Company

Asosiasi Rekursif

Ketika suatu asosiasi kembali ke kelas yang sama (misalnya, pernikahan antara dua orang), Anda harus menentukan arah menggunakan tanda kurung siku [namaPeran].

konteks Person
self.perkawinan[wife]->select(m | m.berakhir = false)->size() = 1

Asosiasi yang Dikualifikasi

Gunakan atribut penentu dalam tanda kurung siku untuk memilih objek tertentu.

konteks Bank
self.customer[12345]  -- Mengembalikan orang tertentu dengan accountNo 12345
self.customer         -- Mengembalikan Himpunan semua pelanggan

Pengubahan Tipe (Pengubahan Tipe)

Gunakan oclAsType() untuk mengakses properti dari subjenis atau properti kelas induk yang tersembunyi.

self.oclAsType(Sub).p       -- Mengakses properti 'p' yang didefinisikan dalam Subjenis
self.oclAsType(Super).p     -- Mengakses properti 'p' yang didefinisikan dalam Kelas Induk

5. Menulis Kendala

1. Invarian (inv)

Kondisi yang harus selalu benar untuk semua contoh kelas.

konteks Perusahaan 
inv: self.jumlahKaryawan <= 50
inv SME: self.hargaSaham() > 0  -- 'SME' adalah nama opsional untuk kendala

2. Kondisi Pra dan Pasca (prepost)

Kendala yang terkait dengan operasi.

  • pre: Harus benar sebelum eksekusi.

  • post: Harus benar setelah eksekusi. Gunakan hasil untuk nilai kembalian.

  • @pre: Digunakan dalam kondisi pasca untuk merujuk nilai suatu properti sebelum operasi dimulai.

konteks Person::income(): Integer
pre dewasa: self.usia >= 18
post hasilOK: hasil < 5000

konteks Company::rekrutKaryawan(p: Person)
post: karyawan = karyawan@pre->termasuk(p)

3. Ekspresi Body, Awal, dan Turunan

  • body: Menentukan hasil dari operasi kueri (dapat bersifat rekursif).

  • init: Menentukan nilai awal dari suatu atribut.

  • turunkan: Menentukan atribut turunan.

konteks Person::income(): Integer
body: self.pekerjaan.gaji->jumlah()

konteks Person::menikah: Boolean
init: salah

konteks Company::jumlahKaryawan: Integer
turunkan: self.karyawan->ukuran()

6. Fitur OCL Lanjutan

Ekspresi Let dan Def

  • let: Menentukan variabel lokal dalam satu ekspresi tunggal.

    let totJam: Integer = self.bekerjaPada->kumpulkan(jam)->jumlah() dalam totJam >= 30
    
  • def: Menentukan variabel atau operasi yang dapat digunakan kembali dan terkait dengan sebuah klasifikasi.

    def: memilikiJabatan(t: String): Boolean = self.pekerjaan->ada(jabatan = t)
    

Nilai Tidak Terdefinisi (Logika 3 Nilai)

Jika suatu sub-ekspresi tidak terdefinisi (misalnya, menavigasi asosiasi null), maka seluruh ekspresi menjadi tidak terdefinisi. Namun, operator Boolean memiliki pengecualian short-circuit:

  • benar atau <tidak terdefinisi> = benar

  • salah dan <tidak terdefinisi> = salah

  • salah menyiratkan <tidak terdefinisi> = benar

  • <tidak terdefinisi> menyiratkan benar = benar

Fitur Kelas & Properti Objek yang Didefinisikan Sebelumnya

  • allInstances(): Mengembalikan Set dari semua contoh yang ada dari sebuah kelas.

    Person.allInstances()->size() <= 100
    
  • Properti objek: oclIsTypeOf(Tipe)oclIsKindOf(Tipe)oclInState(State)oclIsNew (digunakan dalam kondisi pasca).


7. Studi Kasus Komprehensif: Model Perusahaan

Untuk memperkuat konsep-konsep ini, berikut adalah kumpulan batasan integritas yang diterapkan pada model basis data Perusahaan (Karyawan, Departemen, Proyek, Tanggungan).

Batasan Atribut Dasar

-- Usia harus >= 18
konteks Karyawan inv: self.usia() >= 18

-- Tanggal rekrutmen harus setelah tanggal lahir
konteks Karyawan inv: self.tanggalRekrutmen > self.tanggalLahir

-- Tanggal mulai manajer harus setelah tanggal rekrutmen
konteks Karyawan inv: self.manages->tidakKosong() menyiratkan self.manages.tanggalMulai > self.tanggalRekrutmen

Batasan Navigasi dan Hubungan

-- Atasan harus lebih tua dan dibayar lebih banyak daripada karyawan
konteks Karyawan
inv: self.atasan->tidakKosong() menyiratkan self.usia() > self.atasan.usia()
inv: self.atasan->tidakKosong() menyiratkan self.gaji < self.atasan.gaji

-- Atasan harus direkrut sebelum bawahan mereka
konteks Karyawan
inv: self.bawahan->tidakKosong() menyiratkan self.bawahan->untukSemua(e | e.tanggalRekrutmen > self.tanggalRekrutmen)

-- Manajer departemen harus merupakan karyawan dari departemen tersebut
konteks Departemen
inv: self.bekerjaDi->mencakup(self.manajer.karyawan)

-- Lokasi proyek harus salah satu lokasi departemen
konteks Proyek
inv: self.mengendalikan.lokasi->mencakup(self.lokasi)

-- Karyawan hanya boleh bekerja pada proyek yang dikendalikan oleh departemennya
konteks Karyawan
inv: self.bekerjaDi.mengendalikan->mencakupSemua(self.bekerjaPada.proyek)

Batasan Koleksi dan Iterator

-- Nomor SSN harus menjadi pengenal unik di seluruh karyawan
konteks Karyawan
inv: Employee.allInstances->untukSemua(e1, e2 | e1 <> e2 menyiratkan e1.SSN <> e2.SSN)

-- Nama tanggungan dan hubungan harus unik per karyawan
konteks Karyawan
inv: self.tanggungan->untukSemua(e1, e2 |
      e1 <> e2 menyiratkan (e1.nama <> e2.nama atau e1.hubungan <> e2.hubungan))

-- Atribut turunan: jumlah karyawan dalam sebuah departemen
konteks Departemen
inv: self.jumlahKaryawan = self.bekerjaDi->ukuran()

-- Karyawan hanya boleh bekerja pada paling banyak 4 proyek
konteks Karyawan
inv: self.bekerjaPada->ukuran() <= 4

Logika yang Rumit menggunakan mengizinkan dan pilih

-- Jumlah jam kerja total harus antara 30 dan 50 per minggu
konteks Karyawan 
inv: biarkan totJam: Integer = self.bekerjaPada->kumpulkan(jam)->jumlah() 
     dalam totJam >= 30 dan totJam <= 50

-- Paling banyak 2 karyawan yang dapat bekerja < 10 jam pada proyek tertentu
konteks Proyek 
inv: self.bekerjaPada->pilih(jam < 10)->ukuran() <= 2

-- Hanya manajer departemen yang boleh bekerja < 5 jam pada proyek
konteks Karyawan 
inv: self.bekerjaPada->pilih(jam < 5)->tidakKosong() mengimplikasikan 
     self.bekerjaUntuk.mengelola.karyawan = self

-- Karyawan tanpa bawahan harus bekerja >= 10 jam pada SEMUA proyek mereka
konteks Karyawan 
inv: self.bawahan->kosong() mengimplikasikan self.bekerjaPada->untukSemua(jam >= 10)

-- Manajer departemen harus bekerja >= 5 jam pada semua proyek yang dikendalikan oleh departemen
konteks Departemen 
inv: self.mengendalikan->untukSemua(p: Proyek | 
      self.mengelola.karyawan.bekerjaPada->pilih(jam >= 5)->mengandung(p))

Mencegah Siklus (Logika Rekursif)

-- Seorang karyawan tidak dapat mengawasi dirinya sendiri
konteks Karyawan inv: self.bawahan->menghindari(self)

-- Hierarki pengawasan tidak boleh berupa siklus
konteks Karyawan 
def: semuaBawahan = self.bawahan->gabung(
          self.bawahan->kumpulkan(e: Karyawan | e.semuaBawahan))
inv: self.semuaBawahan->menghindari(self)