Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: menambahkan materi design patterns>singleton #135

Merged
merged 3 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
38 changes: 38 additions & 0 deletions design_patterns/readme.md
Original file line number Diff line number Diff line change
@@ -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/)
22 changes: 22 additions & 0 deletions design_patterns/singleton/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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" )
114 changes: 114 additions & 0 deletions design_patterns/singleton/readme.md
Original file line number Diff line number Diff line change
@@ -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)
48 changes: 48 additions & 0 deletions design_patterns/singleton/singleton_another_example.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#include <iostream>

// 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;
}
33 changes: 33 additions & 0 deletions design_patterns/singleton/singleton_basic.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include <iostream>

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';
}
27 changes: 27 additions & 0 deletions design_patterns/singleton/static_class_scope.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include <iostream>

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';
}
9 changes: 9 additions & 0 deletions design_patterns/singleton/static_file_scope/another.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
13 changes: 13 additions & 0 deletions design_patterns/singleton/static_file_scope/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include <iostream>

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
}
14 changes: 14 additions & 0 deletions design_patterns/singleton/static_file_scope/readme.md
Original file line number Diff line number Diff line change
@@ -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
```
Loading