Pemodelan Sistem Perangkat Lunak Andronicus Riyono, M.T. Universitas Kristen Duta Wacana
Analisis & Desain Pemodelan Sistem Perangkat Lunak Pertemuan 3
Bagian I: Analisis Pemodelan Sistem Perangkat Lunak Pertemuan 3
Apakah analisis itu?* • penguraian suatu pokok atas berbagai
bagiannya dan penelaahan bagian itu sendiri serta hubungan antarbagian untuk memperoleh pengertian yg tepat dan pemahaman arti keseluruhan;
• analisis sistem: pengamatan mengenai suatu
kegiatan, metode, prosedur, atau teknik untuk menentukan manfaat kegiatan tsb dan cara terbaik untuk memperolehnya;
* Kamus Besar Bahasa Indonesia dalam jaringan
Apakah analisis itu?
Apa hubungannya dengan membuat perangkat lunak?
• Metode untuk mempelajari sifat sesuatu atau menentukan fitur esensial serta keterkaitan antar fiturnya
• Metode untuk menguraikan konsep-konsep yang kompleks menjadi bagian-bagian atau fungsi-fungsi yang lebih mendasar
• Evaluasi kegiatan untuk mengidentifikasi tujuan-
tujuan yang dikehendaki dan menentukan prosedur untuk mencapai tujuan-tujuan tersebut secara efisien
Apakah kita sudah menganalisis masalah? The Perfect World
DogDoor.java
Apakah kita sudah menganalisis masalah?
The Real World
The Real World
DogDoor.java
Kita tidak bisa menghabiskan waktu menelusuri semua kemungkinan yang ada Betul, tapi kita bisa mengidentifikasi mana fitur-fitur penting dan bagaimana keterkaitannya?
Menurut Anda, apa sajakah yang membuat sebuah fitur menjadi penting? ________________________________ ________________________________ ________________________________ ________________________________
Suatu fitur menjadi penting bila •
pembeli tidak akan mau membeli software Anda jika fitur tersebut tidak ada
• •
sistem tidak akan bekerja tanpa fitur tersebut Banyak bagian lain dari sistem bergantung pada fitur tersebut
Tidak disebutkan mengenai hewan-hewan lain di dalam spesifikasi sistem Tapi kita sudah membuat sesuai dengan requirements yang ada
Bagaimana kita bisa tahu bahwa hal itu penting?
Mengapa pembeli tidak memberitahukannya?
Menuju ke hal penting Kumpulkan requirements
Analisis dalam konteks nyata
Bangun arsitekturnya
Penting bagiku, belum tentu penting bagimu. Mau ambil garam, koq adanya sekop?
Mau gali lubang di kebun, ada yang punya sekop nggak ya? cape deh...
Skenario-skenario, menyatukan pandangan Programmer Software Management
End-user Functionality Classes Objects Behaviors
Logical View
Development View
Code structure Packages Libraries
Physical View
Processors Data stores
Skenarioskenario Threads Processes Communications
Progress View Integrators Performance Scalability
System engineers Topology Communications
Mari berpikir...
bagaimana kalau...
Sequence diagram Object
Message
Control focus Lifeline
memikirkan alur menjadi lebih mudah
usahakan agar umum
Jadi, semua anjing dapat membuka pintunya? Sepertinya tidak benar.
• •
Apa yang harus diubah? Coba ubah sequence diagram-nya
Kita dapat memberikan komentar, tetapi hal ini tidak terlalu membantu untuk mendapatkan desain yang benar
Solusinya
Beberapa panduan ‣ Buat classes sederhana yang bekerjasama dengan mengirimkan dan merespon pesan-pesan ‣ Setiap class seharusnya memiliki satu tanggungjawab (responsibility) ‣ Kebanyakan persistent object (misal, Bark) memiliki class yang mengelolanya ‣ Create ‣ Retrieve ‣ Update ‣ Delete
Bagaimana bisa tahu bahwa kita perlu class BarkManager?
Beberapa panduan Bagaimana dengan use case-nya?
Use case harus diperbaharui juga
Perubahan ≠ Ubah Use Case Jadi, tiap kali membuat perubahan, use case harus diubah juga?
‣ Tidak harus ‣ Ubah use case jika memang perubahannya berpengaruh pada use case ‣ Tidak semua perubahan terjadi pada tingkat use case ‣ Kalau use case bersifat umum dan menjelaskan sebuah urutan untuk mencapai tujuan tunggal, tidak akan banyak berubah ‣ Beberapa use cases bahkan tidak perlu banyak detil
Use cases lainnya • • • •
Pengelolaan Bark (CRUD) Inisialisasi sistem Nonaktifkan (mengunci) pintu Membuat laporan aktivitas
Sepertinya untuk menjaga agar use cases tetap lengkap dan up to date, diperlukan usaha yang tidak sedikit. Apakah tidak ada cara lain untuk mendeskripsikan sistem dengan cara yang lebih mudah?
Ada, kita bisa menggunakan user stories atau dengan CRC cards
CRC Cards • • • • •
Class-Responsibility-Collaborator
•
Beck & Cunningham
Teknologi rendah
•
Kertas dan pensil
Mudah dibuat dan dihancurkan Menggunakan penempatan untuk menunjukkan keterkaitan antara yang satu dan yang lain Baik untuk belajar berpikir Object Oriented
Class DogDoor dalam sebuah CRC Class name
Optional
Responsibilities of this class
Classes this class work with to perform its responsibilities
Use case utama kita dalam CRC Cards
Sepertinya mudah. Adakah hal lain yang perlu saya ketahui?
Panduan CRC ‣ Jangan takut untuk menyobek salah satu kertas dan menggantinya (jika salah) ‣ Jika sebuah class memiliki lebih dari satu tanggungjawab primer, atau tanggungjawabnya tidak muat dalam sebuah kartu, mungkin perlu dipecah menjadi lebih dari satu class ‣ Pastikan Anda dapat membuat susunan kartu dan menjalankan skenario-skenario dengan kartu-kartu yang ada ‣ Tulislah dengan jelas ‣ Jangan masukkan kartu-kartu ke dalam bak mandi, kertas akan rusak bila terendam air terlalu lama.
User Stories •
Mendeskripsikan sebuah fitur atau unit pekerjaan bagi programmer
•
Memberikan informasi yang cukup untuk mendefinisikan kebutuhan dasar
•
Harus dapat diuji dengan unit test
Contoh user story Open the dog door The DogDoor will receive a signal to open the door from some peripheral device, such as the remote button or the bark recognizer. When the signal to open the door is received, the door will open and remain open for 5 seconds Bagaimana kalau pintunya sudah terbuka?
Contoh user story (perbaikan) Open the dog door The DogDoor will receive a signal to open the door from some peripheral device, such as the remote button or the bark recognizer. When the signal to open the door is received, the door will open and remain open for 5 seconds. At the end of 5 seconds, the door will close. If the door is already open, then the door will not close until 5 seconds from the current signal reception has elapsed.
User stories untuk use case utama kita Saya suka ini. Ada petuah bijak lain?
Panduan user story ‣
Jika user story menjadi terlalu panjang, pecahkan agar menjadi dua atau lebih
‣
Jika terlalu sederhana, mungkin perlu digabungkan dengan yang lain
‣
Pastikan ada informasi yang cukup untuk programmer dapat mengimplementasikannya atau setidaknya menanyakan pertanyaan yang penting tentang itu
‣
Jika sebuah user story perlu waktu lama (relatif) untuk diimplementasi, pecah menjadi beberapa user stories
Membandingkan ketiga teknik
Fokus pada Bark Sepertinya tidak ada yang salah dengan String untuk Bark.
Kamu pasti baru belajar Object Oriented, ya?
Apa yang salah dengan pendekatan Randy?
Bandingkan kodenya
String mungkin tidak masalah untuk sekarang, karena kita belum punya hardware untuk mengenali suara anjing. Namun, ketika kita punya hardware semacam itu, format asli suara anjing akan diperlukan dan kita pasti harus mengubah program. Dengan class Bark, kita cukup mengubah di satu tempat saja.
Jadi, mengapa Maria memenangkan laptop? Tidak terlalu penting sebenarnya. Saya hanya melakukan analisis dengan lebih baik dibandingkan mereka yang lain.
Ya, tetapi sebenarnya pekerjaanmu kurang lengkap. Kamu melewatkan sebuah kemungkinan yang sangat penting.
Apa yang dilewatkan Maria dan bagaimana seharusnya ia menyelesaikan pekerjaannya?
Tiga hal yang mungkin kita temukan • • •
Mungkin ada lebih dari satu anjing Mungkin ada lebih dari satu pintu Mungkin ada lebih dari satu remote control
Tanya! Dari manakah datangnya class?
‣ Analisis kata-kata ‣ kata benda pada requirements dan dokumen-dokumen lain ‣ Entities dan konsep-konsep ‣ Dari lingkup aplikasi ‣ Pengalaman ‣ Dari sistem yang telah dibuat sebelumnya
Analisis kata-kata 1. Cari semua kata benda dalam dokumen (use case) 2. Hilangkan perulangan 3. Jika ada beberapa nama (untuk hal yang sama) pilih nama yang paling tepat dan hilangkan yang lainnya
Contoh analisis kata-kata Berikut ini adalah kata benda yang didapat dari use case yang ada: ‣ the (owner's) dog ‣ the owner ‣ the button ‣ bark recognizer ‣ request ‣ inside/outside ‣ dog door ‣ remote control ‣ bark
Apakah pasti ada class untuk setiap kata benda?
‣ Tidak semua kata adalah penting ‣ Bahkan ketika kita pikir penting ‣ Beberapa kata benda merujuk pada hal-hal di luar sistem ‣ Pemilik, anjing ‣ Beberapa kata benda merujuk pada hal yang tidak perlu dibuat atau dimodelkan ‣ Request
Jika kata benda adalah class, maka kata kerja...
...adalah methods
Analisis membuat kita yakin bahwa kita telah mengerti bagaimana sistem akan bekerja. Sekarang mari belajar desain.
Akhir dari Bagian Pertama
Bagian 2: Desain Pemodelan Sistem Perangkat Lunak Pertemuan 3
Kukira dari tadi kita sudah melakukan desain? Apakah ada perbedaan yang besar antara analisis dan desain? Kapan kita masuk ke pemrogramannya?
Bagaimana menurut Anda?
Apa itu desain?
Saya coba cari 'design' di Internet, dan mendapat banyak sekali definisi yang berbeda. Mana yang benar?
Karakteristik umum desain ‣ Desain memiliki tujuan ‣ Desain mendeskripsikan bagaimana sesuatu akan bekerja (dalam sebuah konteks) ‣ Desain memiliki informasil yang cukup sehingga seseorang dapat melakukan implementasi ‣ Ada banyak macam desain ‣ Seperti ada banyak macam arsitektur rumah ‣ Desain dapat disajikan dalam berbagai tingkat detil ‣ Sebuah rumah anjing butuh jauh lebih sedikit detil dibandingkan dengan gedung pencakar langit
Definisi yang kita gunakan •
Desain perangkat lunak adalah proses merencanakan bagaimana memecahkan masalah dengan perangkat lunak.
•
Sebuah desain perangkat lunak memiliki informasi yang cukup sehingga tim pengembangan dapat membuat implementasi solusi. Ini adalah perwujudan dari rencana (cetak biru untuk solusi perangkat lunak).
Desain disebut bagus bila • • •
mudah dimengerti fleksibel, mudah diubah memenuhi requirements (sekarang dan di masa depan)
Ingat Rick?
(dari Bab 1 buku HFOOA&D) Bisnis berjalan lancar. Sekarang saya siap untuk mengembangkan usaha saya dan saya perlu beberapa perubahan pada aplikasi
Aplikasi yang telah dibuat
Requirement baru! Saya sekarang berjualan Mandolin juga. Nama toko saya juga berubah, dari Rick's Guitars menjadi Rick's Music. Bisakah Anda mengubah aplikasi agar dapat menangani perubahan-perubahan ini?
Saya pikir kita sebaiknya membuat class Instrument dengan property "type" Kita tambahkan saja class Mandolin. Lalu copy-paste dari class Guitar.
Kalau kita menggunakan desain O-O yang baik, saya pikir class Instrument harus menjadi parent class dari Guitar dan Mandolin.
Membandingkan ketiga solusi yang mungkin... Solution
Advantages
Disadvantages
class Mandolin
Mudah diimplementasi
•Duplikasi kode •Sulit dikelola
class Instrument dengan property "type"
Tidak ada duplikasi kode
Instrument sebagai base class
•solusi O-O
•Bukan solusi O-O •Perlu memeriksa "type" dari setiap object yang ada
•Tidak perlu memeriksa "type" dari tiap object •Tidak ada duplikasi kode
Apakah Rick memiliki Instrument di gudangnya?
Karena tidak mungkin ada object Instrument, kita harus membuatnya menjadi sebuah abstract class
Abstract classes membungkus perilaku yang sama dan mendefinisikan protokol untuk seluruh subclasses-nya
Tidak sesederhana itu ‣ Masih banyak hal yang harus dikerjakan ‣ Buat agar class Inventory menggunakan Instrument dan bukan lagi Guitar ‣ Tambahkan class InstrumentSpec ‣ Abstract base class untuk GuitarSpec ‣ Tambahkan MandolinSpec yang diturunkan dari Instrument Spec ‣ Buat agar class Inventory menggunakan InstrumentSpec dan bukan lagi GuitarSpec
Aplikasi setelah direvisi
Kode setelah direvisi
Kode setelah direvisi
Kode setelah direvisi
Tiga langkah menuju Great Software 1. Pastikan bahwa perangkat lunak yang Anda buat bekerja sesuai dengan apa yang diperlukan oleh customer 2. Terapkan prinsip-prinsip object-oriented yang baik 3. Usahakan desain yang dapat dikelola dan dapat digunakan kembali
Bagaimana dengan yang kita lakukan tadi?
Apakah program pencarian di Rick's Music sudah dapat disebut 'great software'? 1.
Apakah program tersebut sudah melakukan apa yang seharusnya dilakukannya? Ya. Aplikasi dapat menemukan gitar dan mandolin, meski tidak pada waktu yang sama. Mungkin sudah tepat. Untuk lebih pastinya, tanya pada Rick.
2.
Apakah sudah menggunakan prinsip-prinsip OO seperti enkapsulasi untuk menghindari duplikasi kode dan membuat software menjadi mudah untuk dikembangkan lebih lanjut? Sudah menggunakan enkapsulasi ketika membuat class InstrumentSpec, dan juga inheritance ketika membuat Instrument dan InstrumentSpec sebagai abstract superclass. Tapi masih perlu mengerjakan banyak hal untuk menambahkan jenis instrumen baru
3.
Seberapa mudah untuk menggunakan kembali aplikasi pencarian ini? Apakah perubahan di satu bagian memaksa Anda mengubah banyak bagian lain? Apakah sudah loosely coupled? Agak sulit menggunakan kembali bagian-bagian dari aplikasi ini. Semua masih sangat erat terkait, dan InstrumentSpec seharusnya merupakan bagian dari Instrument (aggregation)
Saya telah menggunakan prinsip-prinsip OO dan aplikasi saya lebih baik dari sebelumnya, tapi apakah desainnya sudah baik?
• • •
mudah dimengerti fleksibel, mudah diubah memenuhi requirements
Bertanyalah, bagaimana jika... ‣ Bagaimana jika Rick menjual lebih banyak macam Instrument ‣ Bagaimana jika Rick ingin menjual tidak hanya Instruments saja? ‣ Asesoris ‣ CD ‣ Bagaimana jika sistem inventory harus diintegrasikan dengan program akuntansi dan penjualan
Menambah Instruments
Jadi, duplikasi kode dan perlunya membuat perubahan di berbagai tempat untuk memenuhi sebuah perubahan requirement menunjukkan desain yang masih jelek?
Betul! Kita akan melihat ide-ide ini disebutkan sebagai prinsip-prinsip desain nantinya, tapi Anda sudah pada jalur yang benar untuk memahami prinsip-prinsip dasar dari desain Object-Oriented yang baik.
Setiap akan menambahkan Instrument baru, kita harus mengubah class Inventory. Juga harus menambahkan sebuah method search baru. Ini sepertinya bukan desain yang bagus
Tepat. Jika kita mendeskripsikan pencarian inventory dalam kata-kata, kemungkinan kita akan mengatakan bahwa kita ingin untuk "mencari di seluruh inventori untuk melihat apakah ada instrumen yang cocok dengan yang dicari oleh pembeli"
Kode pencarian yang ada public List search(GuitarSpec searchSpec) { List matchingGuitars = new LinkedList(); for (Iterator i = inventory.iterator(); i.hasNext(); ) { Guitar guitar = (Guitar)i.next(); if (guitar.getSpec().matches(searchSpec)) matchingGuitars.add(guitar); } return matchingGuitars; } public List search(MandolinSpec searchSpec) { List matchingMandolins = new LinkedList(); for (Iterator i = inventory.iterator(); i.hasNext(); ) { Mandolin mandolin = (Mandolin)i.next(); if (mandolin.getSpec().matches(searchSpec)) matchingMandolins.add(mandolin); } return matchingMandolins; }
Kode ditujukan pada interface (atau abstract class) public List
search(InstrumentSpec searchSpec) { List matchingInstruments = new LinkedList(); for (Iterator i = inventory.iterator(); i.hasNext(); ) { Instrument instrument = i.next(); if (instrument.getSpec().matches(searchSpec)) matchingInstruments.add(instrument); } return matchingInstruments; }
Tapi class InstrumentSpec kan masih abstract. Apakah benar harus begitu? Yang menginginkan InstrumentSpec untuk menjadi sebuah concrete class, silakan berdiri.
Oke, saya benar-benar bingung sekarang. Apakah kita perlu concrete Instrument class atau tidak? Bagaimana menurut Anda? Apa yang dapat menolong kita untuk menentukan apakah perlu ada concrete Instrument class atau tidak?
Selamat datang di Analisis Kesamaan •
•
Carilah apa saja properties dan methods yang sama-sama dimiliki oleh semua object dari tipe yang sedang dipertimbangkan
• •
Apa yang dimiliki oleh semua Instruments Dalam konteks permasalahannya
Buatlah sebuah class yang membungkus semua elemen yang umum
• •
Kita tetap tidak tahu apakah harus abstract atau concrete Biasanya dimulai dengan bentuk abstract Kalau dilihatlihat, kita mirip juga ya!
Perbedaan, pelengkap kesamaan •
•
Carilah apa saja properties dan methods yang berbeda dari semua object dari tipe yang sedang dipertimbangkan
• •
Beda perilaku mungkin berarti beda implementasi Mungkin ada kesamaan dalam perbedaan
Buat abstraksi dan bungkus perbedaan yang ada dalam abstraksi yang dibuat
•
Abstraksi yang dimaksud adalah interface atau class abstract Sepertinya ada yang berbeda denganmu...
•
Cobalah untuk Instruments Persamaan
•
Perbedaan
1.
1.
2.
2.
3.
3.
4.
4.
•
Kesamaan/Perbedaan Instruments Persamaan
•
Perbedaan
1. Pembuat (Builder)
1. Kesamaan
2.
Jenis (Guitar, Mandolin)
2. Cocok dengan spesifikasi
3.
Harga
3. Berbagai properties
4.
Nomor seri
5. Spesifikasi
Jadi tipe adalah sebuah property yang umum dan kita melihat implementasi dengan subclasses. Sepertinya semua baik-baik saja.
Saya tidak terlalu yakin. Jika tipe adalah property, mengapa kita perlu inheritance. Tidak bisakah kita buat sebuah property dengan nama tipe atau mungkin enum?
Jenis instrumen tidak terkait dengan perilaku. Itu hanyalah sebuah property dan dapat diimplementasikan dengan mudah di class Instrument.
Bagaimana dengan mencocokkan dua instrument? Bukankah itu sebuah perilaku!
Anda benar, namun kita telah mengurusnya dengan membungkus perilaku tersebut dan property yang mungkin berbeda dengan membuat InstrumentSpec. Kita cukup menyebutkan sebuah InstrumentSpec ketika membuat Instrument baru.
Change is good Terkadang Anda harus merelakan sebuah desain lama untuk desain baru yang lebih fleksibel
Desain yang baru
Tapi kita masih punya banyak macam classes untuk setiap spesifikasi instrumen. Jadi apa yang sebenarnya kita perbaiki?
Selesaikan satu masalah pada satu waktu. Iterate! Increment!
Tetapi, tidak ada yang mirip diantara spesifikasi instrumen yang berbeda. Jadi kita tidak bisa menerapkan solusi yang sama. Kita sudah melakukan enkapsulasi terhadap perbedaan yang ada.
Apakah teman kita ini benar? Atau kita di ambang kehancuran dengan memisahkan classes untuk setiap tipe instrument specification? Silakan diskusikan...
Struktur data, sang penyelamat Jangan takut, saya Kapten Strukdat, siap menolong Anda dengan sebuah Map untuk membantumu menemukan solusi
Perbaikan berikutnya
instrument
getSpec ()
getProperty("builder")
Masih ada satu masalah dengan 'kesamaan'. Apakah semua ini sama untuk instrumen-instrumen yang berbeda? Atau baru sebagian masalah saja yang terselesaikan?
Apakah kami sama?
Tergantung apa yang disebut 'sama' public boolean matches(InstrumentSpec otherSpec) { for (Iterator i = otherSpec.getProperties().keySet().iterator(); i.hasNext(); ) { String propertyName = (String)i.next(); if (!properties.get(propertyName).equals( otherSpec.getProperty(propertyName))) { return false; } } return true; } Di sini sama berarti bahwa sebuah spesifikasi cocok dengan spesifikasi yang lain jika spesifikasi tersebut memiliki semua properti dari spesifikasi kedua dan setiap property keduanya memiliki nilai yang sama
Strategy Design Pattern
Singletons
Uji 'Kemudahan Pengubahan' 1.
Berapa banyak class yang harus ditambahkan agar pada aplikasi dapat ditambahkan instrumen jenis baru? ___________________________________________________________ ___________________________________________________________
2.
Berapa banyak class yang harus ditambahkan agar pada aplikasi dapat ditambahkan instrumen jenis baru? ___________________________________________________________ ___________________________________________________________
3.
Jika Rick menginginkan untuk mencatat tahun pembuatan setiap instrumen, berapa banyak class yang harus diubah untuk mendukung pencatatan informasi baru ini? ___________________________________________________________ ___________________________________________________________
4.
Jika Rick ingin menambah property baru neckWood, berapa banyak class yang harus diubah untuk pencatatan informasi baru ini? ___________________________________________________________ ___________________________________________________________
Hasil 'pengujian' 1.
Berapa banyak class yang harus ditambahkan agar pada aplikasi dapat ditambahkan instrumen jenis baru? Kita harus menambahkan MatchStrategy interface dan classes. (Sebenarnya tidak harus, namun itu membuat aplikasinya menjadi semakin fleksibel) Kita harus menambahkan beberapa enum seperti InstrumentType, dan seterusnya.
2.
Berapa banyak class yang harus ditambahkan agar pada aplikasi dapat ditambahkan instrumen jenis baru? Kita perlu mengubah class Instrument dan Instrument Spec
3.
Jika Rick menginginkan untuk mencatat tahun pembuatan setiap instrumen, berapa banyak class yang harus diubah untuk mendukung pencatatan informasi baru ini? Kita menambahkan sebuah pasangan key-value baru pada property map
4.
Jika Rick ingin menambah property baru neckWood, berapa banyak class yang harus diubah untuk pencatatan informasi baru ini? Kita mungkin perlu mengubah value yang terdapat pada enum Wood
Fleksibilitas dan Kohesi
The design life cycle
Ilmu baru di kotak Anda
Ilmu baru di kotak Anda
Ilmu baru di kotak Anda
Ilmu baru di kotak Anda
Ilmu baru di kotak Anda
Keren! Sekarang kita punya aplikasi yang sangat hebat. Rick pasti suka dengan apa yang telah kita buat. Sangat fleksibel. Bahkan tidak terpikirkan satu masalahpun!
Teman-teman, Rick barusan memberitahukan bahwa tokonya akan segera menjual buku, asesoris, dan juga kartu ucapan. Dia ingin agar semua segera dapat masuk dalam inventorinya.
Tugas Kelompok •
Cari tahu bagaimana untuk memenuhi requirements yang baru dari Rick
•
Ubahlah desain agar memenuhi requirements yang baru tersebut
•
Buatlah diagram UML untuk menunjukkan desain yang dibuat
• •
Sertakan dokumentasi lain yang sekiranya diperlukan
Lakukan uji 'kemudahan pengubahan' terhadap desain baru yang telah dibuat
Perhatian! • Margin: (atas, kanan, bawah, kiri) 3, 3, 4, 4cm • Font: Verdana 12pt, Spacing: 1,5 spasi • Filetype: PDF • Subject: PSPL-TUGAS-KEL1 • Body: Nama Kelompok (satu kata, misal: Singleton), NIM para anggota, dan nama panggilan para anggota kelompok
• Nama File: PSPL-TUGAS-KEL1-namakelompok.pdf • dikirim ke: [email protected]