diff --git a/CMakeLists.txt b/CMakeLists.txt index d61a6df..80e740e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ add_subdirectory(artificial_intelligence) add_subdirectory(strings) add_subdirectory(geometri) add_subdirectory(graf) +add_subdirectory(design_patterns/singleton) # FIXME: ada beberapa fungsi header math terkhusus untuk # windows, oleh karena itu maka setting kompilasi diff --git a/design_patterns/readme.md b/design_patterns/readme.md new file mode 100644 index 0000000..056baf0 --- /dev/null +++ b/design_patterns/readme.md @@ -0,0 +1,38 @@ +# Design patterns + +Seiring waktu yang terus berjalan, software pun akan terus mengalami +pengembangan, agar komunikasi antar *objects*, pembuatan *objects*, +dan struktur atau relasi antar *classes* dalam aplikasi tersebut +berjalan lancar, *elegant*, dan mudah di-*track*, maka ada yang +dinamakan **Design Patterns**. + +## Apa itu Design patterns + +Adalah sebuah solusi agar saat kamu mengemmbangkan sebuah software, +*code* software tersebut mudah dipahami, mudah dikelola, dan memiliki +skalabilitas yang tinggi, bisa buat jaga-jaga jika kamu ingin serius +dengan pengembangan software tersebut. **Design patterns** bukanlah +sebuah *code* yang bisa kamu *plek-ketiplek* salin layaknya algoritma, +melainkan sebuah *template* yang bisa kamu gunakan sebagai acuan saat +mengembangkan software-mu. + +## Kategori Design patterns + +Sebenarnya banyak sekali *patterns* yang digunakan saat proses pengembangan +software, namun ada 23 jenis *patterns* yang umumnya digunakan sebagai pondasi +dari *patterns* lainnya, 23 *patterns* itulah yang umum disebut dengan istilah +**The Gang of Four (GoF)**, **GoF** memiliki 3 kategori, yaitu: + +- *Creational*, adalah bagaimana cara *objects* dibuat. + - [Singleton](singleton/) + - *... TODO* +- *Structural*, adalah bagaimana cara menyusun dan hubungan dari *class-class* mu. + - *... TODO* +- *Behavioral*, adalah bagaimana cara komunikasi antar *objects*. + - *... TODO* + +Bagus dan buruknya *patterns* bersifat *relative* terhadap sesuatu yang kamu +ingin buat atau apa yang ingin kamu selesaikan. + +> [!NOTE] +> disarankan lihat terlebih dahulu [OOP](../object_oriented_programming/) diff --git a/design_patterns/singleton/CMakeLists.txt b/design_patterns/singleton/CMakeLists.txt new file mode 100644 index 0000000..3fb7c23 --- /dev/null +++ b/design_patterns/singleton/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.1) +set (CMAKE_CXX_STANDARD 11) +file( GLOB APP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp ) +foreach( testsourcefile ${APP_SOURCES} ) + string( REPLACE ".cpp" "" testname ${testsourcefile} ) + add_executable( ${testname} ${testsourcefile} ) + + set_target_properties(${testname} PROPERTIES LINKER_LANGUAGE CXX) + if(OpenMP_CXX_FOUND) + target_link_libraries(${testname} OpenMP::OpenMP_CXX) + endif() + install(TARGETS ${testname} DESTINATION "bin/singleton") + +endforeach( testsourcefile ${APP_SOURCES} ) + +file( GLOB FILE_SCOPE_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/static_file_scope/*.cpp" ) +add_executable( "static_file_scope" ${FILE_SCOPE_SOURCES} ) +set_target_properties( "static_file_scope" PROPERTIES LINKER_LANGUAGE CXX ) +if(OpenMP_CXX_FOUND) + target_link_libraries( "static_file_scope" OpenMP::OpenMP_CXX ) +endif() +install( TARGETS "static_file_scope" DESTINATION "bin/singleton" ) diff --git a/design_patterns/singleton/readme.md b/design_patterns/singleton/readme.md new file mode 100644 index 0000000..25ba8f1 --- /dev/null +++ b/design_patterns/singleton/readme.md @@ -0,0 +1,114 @@ +# Singleton + +Adalah salah satu **Design Pattern** di kategori *Creational*. Sederhananya +*pattern* ini hanya membolehkan sebuah *class* memiliki +**hanya satu instance**, dan *instance* tesebut *Global Accessible* +(artinya *instance* atau *object* tersebut dapat diakses oleh *unit* lain, +atau katakanlah file `.cpp` lain dalam *project* yang sama). Terlepas +dari baik dan buruknya *pattern* ini bisa dibilang banyak digunakan +karena kesederhaannya dan kemudahannya dalam pengaplikasiannya. + +## Kapan menggunakan Singleton ? + +Saat kamu ingin suatu *object* dapat diakses secara global, +dan tidak ada duplikasi dari *object* tersebut. + +## Cara kerja Singleton + +Singleton memiliki `contructor` yang bersifat `private` atau `protected`, +hal ini mencegah *class* tersebut dapat di-insialisasi (dibuat object) secara biasa +(`Class objek;`), lalu bagaimana cara mengakses *object* atau *instance* dari +*class* tersebut ?. Umumnya setiap Singleton memiliki 1 *function* public `static` +dengan return datatype class nya sendiri. [contoh code](singleton_basic.cpp) + +### mengenai `static` keyword + +dalam CPP terdapat 3 kondisi `static` keyword, setiap kondisi dia bertingkah +berbeda baik itu untuk variable ataupun untuk fingsi. +1. ketika digunakan di *file scope*, maka variable tersebut global namun +hanya sebatas dalam file dimana ia di-deklarasikan. [contoh kode](static_file_scope/) +2. ketika digunakan di *function scope*, maka variable tersebut global, +namun hanya bisa diakses oleh atau melalui fungsi tersebut saja. Contohnya +seperti yang [awal-awal](singleton_basic.cpp) +> [!NOTE] +> - variable tersebut belum akan di inisialisasi sampai fungsi tersebut +dipanggil pertama kali. (bagus untuk menghemat resources, namun eksekusi +task kamu akan butuh extra waktu saat eksekusi pertama kali) +> - tidak ada inisialisasi ulang, meskipun fungsi dipanggil beberapa kali +3. ketika digunakan di *struct* atau *class scope*, maka variable tersebut +bukan sembarang member, melainkan member yang global, dapat diakses meskipun +belum dibuat *object*-nya (tergantung level visibilitas), dan setiap *object* +mendapatkan *share* akan variable tersebut (jika *object* kesatu mengubah +nilai variable tersebut maka *object* kedua pun merasakan perubahannya). +Sederhananya variable tersebut milik *class* dan bukan milik *object* +[contoh code](static_class_scope.cpp) + +## Catatan + +Hindari penggunaan Singleton secara berlebihan, contoh saat kamu +ingin cek apakah *Plane* yang dibuat itu memiliki bentuk *square*: + +```cpp +class Plane { + public: + Plane (int x0, int y0, int x1, int y1) { + this->x0 = x0; + this->y0 = y0; + this->x1 = x1; + this->y1 = y1; + } + int x0, y0, x1, y1; +}; + +class PlaneManager { + private: + PlaneManager () = default; + public: + static PlaneManager& getInstance () { + static PlaneManager instance; + return instance; + } + + Plane* createPlane (int x0, int y0, int x1, int y1) { + Plane* plane = new Plane(x0, y0, x1, y1); + return plane; + } + bool isSquareShaped (Plane& plane) { + return (plane.x1 - plane.x0) == (plane.y1 - plane.y0); + } +}; +``` + +bagaiman kalau begini + +```cpp +class Plane { + public: + Plane (int x0, int y0, int x1, int y1) { + this->x0 = x0; + this->y0 = y0; + this->x1 = x1; + this->y1 = y1; + } + int x0, y0, x1, y1; + + bool isSquareShaped() { + return (x1 - x0) == (y1 - y0); + } +}; +``` + +lalu insialisasi objek dengan cara yang biasa, lebih simple +dan masalah terselesaikan. + +## Contoh kode + +- [basic singleton](singleton_basic.cpp) +- [basic singleton 2](singleton_another_example.cpp) + +--- + +- static + - [static di file scope](static_class_scope/) + - [static di function scope](singleton_basic.cpp) + - [static di class scope](static_class_scope.cpp) diff --git a/design_patterns/singleton/singleton_another_example.cpp b/design_patterns/singleton/singleton_another_example.cpp new file mode 100644 index 0000000..c6791bf --- /dev/null +++ b/design_patterns/singleton/singleton_another_example.cpp @@ -0,0 +1,48 @@ +#include + +// deklarasi class Logger, sebagai wrapper +class Logger { + protected: + Logger (); + + public: + static Logger& getInstance (); + virtual std::string log (std::string message) = 0; +}; + +// definisi class Logger untuk Windows +class WindowLogger : public Logger { + public: + std::string log(std::string message) override { + // lakukan Windows spesific task + + return "[WINDOWS] INFO: " + message; + } +}; + +// definisi class Logger untuk Unix +class UnixLogger : public Logger { + public: + std::string log(std::string message) override { + // lakukan Unix spesific task + + return "[UNIX] INFO: " + message; + } +}; + +// definisi class Logger +Logger::Logger () = default; +Logger& Logger::getInstance () { + // lazy intialize (di inisialisasi saat fungsi dipanggil pertama kali) +#ifdef _WIN32 + static WindowLogger* instance = new WindowLogger(); +#else + static UnixLogger* instance = new UnixLogger(); +#endif // _WIN32 + return *instance; +} + +int main () { + std::cout << "Logging menggunakan `Logger` class: \n"; + std::cout << "\t" << Logger::getInstance().log("Hello World") << std::endl; +} diff --git a/design_patterns/singleton/singleton_basic.cpp b/design_patterns/singleton/singleton_basic.cpp new file mode 100644 index 0000000..2f24fa9 --- /dev/null +++ b/design_patterns/singleton/singleton_basic.cpp @@ -0,0 +1,33 @@ +#include + +class Singleton { + private: + Singleton () = default; + + public: + static Singleton& getInstance () { + static Singleton instance; // lazy intialize, memory belum dialokasikan sampai fungsi ini dipanggil pertama kali + return instance; + } + + Singleton (const Singleton& obj) = delete; // mencegah instance assignable + + // definisikan non-static fungsi-fungsi dan variable-variable + // lalu akses seperti biasa. +}; + +// nilai address dari instace Singleton pasti selalu sama, +// yang berarti bahwa object yang dipanggil merupakan object yang sama. +int main () { + std::cout << "Pemanggilan instance pertama: \n"; + std::cout << "\tInstance address: "; + std::cout << &Singleton::getInstance() << '\n'; + + std::cout << "Pemanggilan instance kedua: \n"; + std::cout << "\tInstance address: "; + std::cout << &Singleton::getInstance() << '\n'; + + std::cout << "Pemanggilan instance ketiga: \n"; + std::cout << "\tInstance address: "; + std::cout << &Singleton::getInstance() << '\n'; +} diff --git a/design_patterns/singleton/static_class_scope.cpp b/design_patterns/singleton/static_class_scope.cpp new file mode 100644 index 0000000..5dcfaeb --- /dev/null +++ b/design_patterns/singleton/static_class_scope.cpp @@ -0,0 +1,27 @@ +#include + +class ClassScope { + public: + ClassScope () = default; + + static int sharedVar; // definisi, memori dialokasikan +}; + +int ClassScope::sharedVar = 5; // insialisasi + +int main () { + ClassScope obj1; + ClassScope obj2; + + std::cout << "Akses langsung nilai variable tanpa perantara object\n"; + std::cout << "\tNilai: " << ClassScope::sharedVar << '\n'; + std::cout << "Akses langsung nilai variable melalui object\n"; + std::cout << "\tNilai: " << obj1.sharedVar << '\n'; + + obj1.sharedVar = 10; + std::cout << "\nObject 1 mengubah nilai variable-nya, menjadi: "; + std::cout << obj1.sharedVar << '\n'; + + std::cout << "Maka object 2 pun merasakan perubahannya\n"; + std::cout << "\tNilai: " << obj2.sharedVar << '\n'; +} diff --git a/design_patterns/singleton/static_file_scope/another.cpp b/design_patterns/singleton/static_file_scope/another.cpp new file mode 100644 index 0000000..605b157 --- /dev/null +++ b/design_patterns/singleton/static_file_scope/another.cpp @@ -0,0 +1,9 @@ +static int var = 5; // untuk pembuktian lebih lanjut kamu bisa hilangkan `static` keyword-nya, + // kemudian lakukan kompilasi ulang, + // maka akan muncul error, karena tidah boleh ada 2 global variable (real) + // yang bernama sama. + +// fungsi yang me-return nilai `var` di file ini +int getVar () { + return var; +} diff --git a/design_patterns/singleton/static_file_scope/main.cpp b/design_patterns/singleton/static_file_scope/main.cpp new file mode 100644 index 0000000..a9c1b6d --- /dev/null +++ b/design_patterns/singleton/static_file_scope/main.cpp @@ -0,0 +1,13 @@ +#include + +extern int getVar(); // deklarasi, memberitahu compiler bahwa fungsi ini di definisikan di file lain + +int var = 10; // intsialisasi global variable (real) + +int main () { + std::cout << "Nilai variable dengan name 'var': "; + std::cout << var << std::endl; + std::cout << "Nilai variable dengan name 'var' di file lain: "; + std::cout << getVar() << std::endl; // tidak bisa akses secara langsung variable `var` + // di file lain karena ekslusif untuk file tersebut saja +} diff --git a/design_patterns/singleton/static_file_scope/readme.md b/design_patterns/singleton/static_file_scope/readme.md new file mode 100644 index 0000000..5d9a070 --- /dev/null +++ b/design_patterns/singleton/static_file_scope/readme.md @@ -0,0 +1,14 @@ +# Kompilasi + +Compile dengan menjalankan perintah + +```shell +g++ *.cpp -o nama_executable +./nama_executable +``` +atau + +```shell +g++ *.cpp -o nama_executable.exe +.\nama_executable.exe +```