5 Model Desain Pada Pemrograman PHP Yang Banyak Digunakan - CRUDPRO

5 Model Desain Pada Pemrograman PHP Yang Banyak Digunakan

5 Model Desain Pada Pemrograman PHP Yang Banyak Digunakan

Mari kita lihat 5 pola design yang umum digunakan di dunia PHP sekarang ini.

Factory

Anda harus menggunakan Factory saat Anda ingin membangun object. Anda tidak ingin mempunyai Factory hanya untuk membikin object baru. Saat Anda membangun object, Anda pertama kali membuatnya dan menginisialisasi. Biasanya, diperlukan untuk melakukan beberapa langkah dan menerapkan logika tertentu. Dengan itu, logis untuk memiliki semuanya di satu tempat dan menggunakannya kembali kapan pun Anda perlu membuat object baru dengan cara yang sama. Pada dasarnya, itulah inti dari pola Factory.

Merupakan ide baik untuk memiliki interface Factory Anda dan membuat code Anda tergantung padanya dan bukan pada Factory concrete . Dengan itu, Anda bisa dengan mudah mengganti satu Factory dengan yang lain kapan saja Anda memerlukannya.

interface FriendFactoryInterface {
    public function create() : Friend
}

Selanjutnya, kami mengimplementasikan interface Factory kami dengan class berikut ini:

class FriendFactory implements FriendFactoryInterface {    public function create() : Friend {
        
        $friend = new Friend();        // initialize your friend        return $friend;
    }
}

Itu pola design yang cukup sederhana namun mampu!

Strategy

Ini digunakan untuk menyembunyikan detail implementasi dari algoritma yang dibutuhkan untuk melakukan operasi. Mempunyai Strategy, client bisa memilih algoritma yang dibutuhkan tanpa mengetahui implementasi sebenarnya dan menerapkannya untuk melakukan operasi.

Katakanlah kita perlu membuat library yang mentransfer data dari satu sumber data ke sumber yang lain. Misalkan, kita perlu mentransfer data dari database ke file csv, atau dari spreadsheet ke file json. Bagaimana Anda melakukannya?

Pertama, kita perlu membuat Strategy masing-masing untuk membaca data dari penyimpanan. Sebut saja mereka Pembaca. Selanjutnya, kita perlu membuat Strategy masing-masing untuk menulis data ke penyimpanan. Sebut saja mereka Penulis.

Maka dari itu, kami akan memiliki 2 Pembaca untuk membaca data baik dari database atau dari spreadsheet. Maka dari itu, kita akan memiliki 2 Penulis untuk menulis data ke file csv atau ke file json.

Penting: client yang akan bekerja dengan Strategy kita seharusnya tidak peduli dengan penerapannya. Maka dari itu, kita juga harus menentukan interface untuk Strategy kita. Dengan demikian, client hanya akan mengetahui tentang metode yang ditentukan oleh interface Strategy dan hanya bekerja dengannya, dan apa yang terjadi dibalik layar bukanlah permasalahannya.

Terakhir, kita perlu membuat client yang akan memilih strategi yang dibutuhkan berdasarkan dari mana dan ke mana dia perlu mentransfer data.

Silahkan kita lihat semua itu beraksi:

interface ReaderInterface { 
    public function start() : void;
    public function read() : array;
    public function stop() : void;
}interface WriterInterface {
   public function start() : void;
   public function write(array $data) : void;
   public function stop() : void;
}class DatabaseReader implements ReaderInterface {
    ...
}class SpreadsheetReader implements ReaderInterface {
    ...
}class CsvWriter implements WriterInterface {
    ...
}class JsonWriter implements WriterInterface {
    ...
}class Transformer {
    
    ...    public function transform(string $from, string $to) : void {
        $reader = $this->findReader($from);
        $writer = $this->findWriter($to);
        
        $reader->start();
        $writer->start();        try {
            foreach ($reader->read() as $row) {
                $writer->write($row);
            }
         } finally {
             $writer->stop();
             $reader->stop();
         }
     }     ...
}

Seperti yang Anda lihat, transformator yang disebut client dari Strategy kami tidak begitu peduli dengan implementasi yang digunakannya. Yang dipedulikannya hanya metode yang ditentukan oleh interface strategi kami.

Adapter

Ini digunakan untuk mengubah interface asing jadi interface umum. Mari kita simpulkan jika dalam project Anda mendapatkan data dari beberapa penyimpanan menggunakan class ini.

class Storage {    private $source;
    
    public function __constructor(AdapterInterface $source) {
        $this->source = $source;
    }    public function getOne(int $id) : ?object {
        return $this->source->find($id);
    }
    
    public function getAll(array $criteria = []) : Collection {
        return $this->source->findAll($criteria);
    }
}

Perhatikan jika penyimpanan tidak bekerja langsung dengan sumber, tetapi bekerja dengan Adapter sumber.

Selain itu, penyimpanan tidak tahu apapun mengenai Adapter concrete . Ini mengacu pada interface adaptor saja. Dengan begitu, implementasi konkret dari Adapter yang disediakan ialah kotak hitam yang lengkap untuknya.

Berikut contoh interface Adapter

interface AdapterInterface {
    public function find(int $id) : ?object;
    public function findAll(array $criteria = []) : Collection;
}

Saat ini, mari kita simpulkan jika kita menggunakan beberapa library untuk mengakses database MySQL. Library menentukan interface sendiri dan terlihat sebagai berikut:

$row = $mysql->fetchRow(...);
$data = $mysql->fetchAll(...);

Sama seperti yang Anda lihat, kami tidak bisa mengintegrasikan package ini begitu saja ke penyimpanan kami. Kita perlu membuat Adapter untuk itu seperti di bawah ini:

class MySqlAdapter implements AdapterInterface {
    
     ...     public function find(int $id) : ?object {
         
         $data = $this->mysql->fetchRow(['id' => $id]);         // some data transformation
     }     public function findAll(array $criteria = []) : Collection {
              
         $data = $this->mysql->fetchAll($criteria);         // some data transformation
     }
   
     ...}

Kemudian, kita bisa inject ke Storage seperti ini:

$storage = new Storage(new MySqlAdapter($mysql));

Jika nanti kami memutuskan untuk menggunakan package lain bukannya yang itu, kami perlu membuat Adapter lain untuk package itu seperti yang kami kerjakan di atas, lalu, masukan Adapter baru ke Storage. Sama seperti yang Anda lihat, untuk menggunakan package yang berbeda untuk mendapatkan data dari basis data, kita tidak perlu menyentuh apa pun itu dalam class storage. Itulah kemampuan pola design Adaptor!

Observer

Ini digunakan untuk memberitahu semua mekanisme mengenai peristiwa tertentu di tempat tertentu. Untuk lebih memahami manfaat dari pola ini, silahkan evaluasi dua solusi dari permasalahan yang sama.

Katakanlah kita perlu membuat Theater untuk menayangkan film ke para kritikus. Kami mendefinisikan class Theater dengan metode ini. Saat sebelum mempresentasikan filmnya, kami ingin mengirim pesan ke handphone para kritikus. Selanjutnya, di tengah film kami ingin menghentikan film selama 5 menit agar para kritikus bisa istirahat. Terakhir, sesudah film berakhir kami ingin minta para kritikus untuk meninggalkan respon mereka.

Mari kita lihat bagaimana tampilannya dalam code:

class Theater {
   
    public function present(Movie $movie) : void {
       
        $critics = $movie->getCritics();        $this->messenger->send($critics, '...');

        $movie->play();

        $movie->pause(5);        $this->progress->break($critics)        $movie->finish();

        $this->feedback->request($critics);    }
}

Kelihatannya bersih dan menjanjikan.

Nah, setelah beberapa waktu, bos memberitahu kami jika sebelum memulai film, kami ingin mematikan lampu. Selain itu, di tengah film saat jeda kami ingin menampilkan iklan. Terakhir, saat film berakhir, kami ingin memulai pembersihan otomatis ruangan.

Nah, salah satunya permasalahan di sini yaitu untuk mencapai itu kita perlu melakukan modifikasi class Theater kita, dan itu melanggar konsep SOLID. Khususnya, ini melanggar konsep terbuka/tertutup. Selain itu, pendekatan ini akan membuat class Theater bergantung pada beberapa service tambahan yang tidak baik.

Bagaimana jika kita membalikkan kondisi. Bukannya menambahkan lebih banyak kompleksitas dan ketergantungan ke class Theater, kami akan menyebarkan kompleksitas ke semua sistem, dan dengan itu, mengurangi ketergantungan class Theater sebagai bonus.

Seperti ini tampilannya saat beraksi:

class Theater {
    
    public function present(Movie $movie) : void {
        
        $this->getEventManager()
            ->notify(new Event(Event::START, $movie));        $movie->play();

        $movie->pause(5);        $this->getEventManager()
            ->notify(new Event(Event::PAUSE, $movie));        $movie->finish();

        $this->getEventManager()
            ->notify(new Event(Event::END, $movie));
    }
}$theater = new Theater();$theater
    ->getEventManager()
    ->listen(Event::START, new MessagesListener())
    ->listen(Event::START, new LightsListener())
    ->listen(Event::PAUSE, new BreakListener())    
    ->listen(Event::PAUSE, new AdvertisementListener())
    ->listen(Event::END, new FeedbackListener())
    ->listen(Event::END, new CleaningListener());$theater->present($movie);

Sama seperti yang Anda lihat, metode sekarang ini menjadi sangat mudah. Dia tidak peduli dengan apa yang terjadi di luar class. Itu hanya melakukan apa yang seharusnya dilakukan dan memberitahu semua sistem tentang fakta. Siapa saja yang tertarik dengan beberapa fakta itu dapat mendengarkan peristiwa terkait dan diberi tahu tentangnya dan melakukan apa yang harus dilakukan.

Dengan pendekatan ini, menambahkan kerumitan tambahan jadi sangat mudah. Yang harus Anda lakukan ialah membuat pendengar baru dan meletakkan logika yang diperlukan di situ.

Semoga Anda menemukan pola Observer yang berguna

Decorator

Ini digunakan saat Anda ingin menyesuaikan perilaku object saat dijalankan, dan dengan itu, mengurangi inheritance yang berlebihan dan jumlah class. Anda mungkin bertanya kenapa saya membutuhkannya? Yah, itu bisa lebih baik dijelaskan contoh.

Katakanlah kita memiliki class window dan door, dan keduanya mengimplementasikan OpenerInterface.

interface OpenerInterface {
    public function open() : void;
}class Door implements OpenerInterface {    public function open() : void {
        // opens the door
    }}class Window implements OpenerInterface {    public function open() : void {
        // opens the window
    }
}

Baik window atau door memiliki perilaku yang sama untuk membuka. Saat ini, kami membutuhkan door dan window lain dengan fungsi tambahan yang akan memberitahu pengguna suhu di luar saat mereka membuka window dan door. Kita dapat menyelesaikan permasalahan ini dengan inheritance seperti ini:

class SmartDoor extends Door {    public function open() : void {
        parent::open();
        $this->temperature();
    }
}class SmartWindow extends Window {    public function open() : void {
        parent::open();
        $this->temperature();
    }
}

Secara keseluruhan, kami mempunyai total 4 class sekarang ini. Tetapi, dengan pola Decorator kita dapat menyelesaikan permasalahan ini cukup dengan 3 class. Ini caranya :

class SmartOpener implements OpenerInterface  {
    
    private $opener;    public function __construct(OpenerInterface $opener) {
        $this->opener = $opener;
    }
    
    public function open() : void {
        $this->opener->open();
        $this->temperature();
    }
}$door = new Door();
$window = new Window();$smartDoor = new SmartOpener($door);
$smartWindow = new SmartOpener($window);

Kami sudah memperkenalkan jenis pembuka baru yang berfungsi seperti proxy tapi dengan fungsi tambahan di atasnya. Itulah triknya.