SOLID - Prinsip Desain Arsitektur Di Dalam Pemrograman Object Oriented (Liskov's Substitution Principal)
Liskov's Subtitution Principal
Pada prinsip ketiga ini, berkaitan erat dengan prinsip kedua yaitu Open / CLose Principal, namun lebih fokus terhadap behaviour antara Parent Class dan Child Class.
Let Φ(x) be a property provable about objects x of type T. Then Φ(y) should be true for objects y of type S where S is a subtype of T
bingung ? oke kira-kira simpelnya gini:
Object dari Parent Class bisa diganti dengan Object dari Child Class tanpa merusak aplikasi / membuat aplikasi berhenti (error)
Jadi suatu object dari Parent Class atau turunannya, harus bisa dengan mudah diganti dengan Object / Instance dari Class Turunan tanpa mengganggu jalannya program. Kita bisa menerapkan konsep ini dengan menggunakan abstraksi / interface. Kalau masih bingung, mari kita belajar langsung dengan contoh.
Contoh Kasus
Bayangkan kita punya 1 soket lampu di teras rumah yang mati dan akan kita ganti dengan yang baru. Lampu yang kita gunakan sebelumnya adalah bohlam lampu pijar yang fungsinya adalah untuk penerangan. Karena gunanya untuk penerangan, mungkin bisa saja kita ganti dengan lampu warna warni namun tidak sesuai dengan kegunaannya. Akan lebih tepat jika kita ganti dengan lampu LED yang memang kegunaannya untuk penerangan. Dari analogi ini, kita akan membuat Class Lampu yang akan mempunyai beberapa Class turunan yaitu Class LampuPijar, LampuLED, dan LampuDisko. Kemudian saya juga punya Class SocketLampuTeras yang digunakan untuk menyalakan lampu.
<?php namespace Kematjaya\PhpLsp\Lampu; /** * Description of AbstractLampu * * @author programmer */ abstract class AbstractLampu { abstract public function nyalakan(); }
<?php namespace Kematjaya\PhpLsp\Lampu; /** * Description of LampuPijar * * @author programmer */ class LampuPijar extends AbstractLampu { public function nyalakan() { return "Nyala Terang"; } }
<?php namespace Kematjaya\PhpLsp\Lampu; /** * Description of LampuLED * * @author programmer */ class LampuLED extends AbstractLampu { public function nyalakan() { return "Nyala Terang"; } }
<?php namespace Kematjaya\PhpLsp\Lampu; /** * Description of LampuDisko * * @author programmer */ class LampuDisko extends AbstractLampu { public function nyalakan() { return "Nyala Kelap Kelip"; } }
<?php namespace Kematjaya\PhpLsp; use Kematjaya\PhpLsp\Lampu\AbstractLampu; /** * Description of SocketLampuTeras * * @author programmer */ class SocketLampuTeras { private AbstractLampu $lampu; public function __construct(AbstractLampu $lampu) { $this->lampu = $lampu; } public function gantiLampu(AbstractLampu $lampu):self { $this->lampu = $lampu; return $this; } public function on():string { return $this->lampu->nyalakan(); } }
dari contoh diatas, bisa saja lampuPijar kita ganti dengan LampuDisko. Namun setelah dilakukan testing maka hasilnya seperti berikut:
<?php namespace Kematjaya\PhpLsp\Tests; use Kematjaya\PhpLsp\Lampu\LampuDisko; use Kematjaya\PhpLsp\Lampu\LampuLED; use Kematjaya\PhpLsp\Lampu\LampuPijar; use Kematjaya\PhpLsp\SocketLampuTeras; use PHPUnit\Framework\TestCase; /** * Description of LampuTamanTest * * @author programmer */ class LampuTamanTest extends TestCase { public function testNyalakanLampu() { $pijar = new LampuPijar(); $socket = new SocketLampuTeras($pijar); $this->assertEquals("Nyala Terang", $socket->on()); $socket->gantiLampu(new LampuLED()); $this->assertEquals("Nyala Terang", $socket->on()); $socket->gantiLampu(new LampuDisko()); $this->assertEquals("Nyala Terang", $socket->on()); } }
dari testing diatas, didapatkan hasil yang kurang sesuai karena peruntukannya untuk penerangan. Ini bisa anggap bug karena hasil yang kurang sesuai dengan yang diharapkan. Oleh karena itu kita perlu buat beberapa modifikasi pada kode Class SocketLampuTeras nya dengan menambahkan Interface / Abstract Class seperti kode berikut.
<?php namespace Kematjaya\PhpLsp\Lampu; /** * Description of AbstractLampuPenerangan * * @author programmer */ abstract class AbstractLampuPenerangan extends AbstractLampu { public function nyalakan() { return "Nyala Terang"; } }
<?php namespace Kematjaya\PhpLsp\Lampu; /** * Description of AbstractlampuHiburan * * @author programmer */ abstract class AbstractlampuHiburan extends AbstractLampu { public function nyalakan() { return "Nyala Kelap Kelip"; } }
Kemudian kita ubah Class LampuDisko menjadi turunan dari Class AbstractLampuHiburan
<?php namespace Kematjaya\PhpLsp\Lampu; /** * Description of LampuDisko * * @author programmer */ class LampuDisko extends AbstractlampuHiburan { }
dan Class LampuPijar dan LampuLED menjadi turunan Class AbstractLampuPenerangan
<?php namespace Kematjaya\PhpLsp\Lampu; /** * Description of LampuLED * * @author programmer */ class LampuLED extends AbstractLampuPenerangan { } class LampuPijar extends AbstractLampuPenerangan { }
Kemudian kita ubah Class SocketLampuTeras
<?php namespace Kematjaya\PhpLsp; use Kematjaya\PhpLsp\Lampu\AbstractLampuPenerangan; /** * Description of SocketLampuTeras * * @author programmer */ class SocketLampuTeras { private AbstractLampuPenerangan $lampu; public function __construct(AbstractLampuPenerangan $lampu) { $this->lampu = $lampu; } public function gantiLampu(AbstractLampuPenerangan $lampu):self { $this->lampu = $lampu; return $this; } public function on():string { return $this->lampu->nyalakan(); } }
<?php namespace Kematjaya\PhpLsp\Tests; use Kematjaya\PhpLsp\Lampu\LampuDisko; use Kematjaya\PhpLsp\Lampu\LampuLED; use Kematjaya\PhpLsp\Lampu\LampuPijar; use Kematjaya\PhpLsp\SocketLampuTeras; use PHPUnit\Framework\TestCase; /** * Description of LampuTamanTest * * @author programmer */ class LampuTamanTest extends TestCase { public function testNyalakanLampu() { $pijar = new LampuPijar(); $socket = new SocketLampuTeras($pijar); $this->assertEquals("Nyala Terang", $socket->on()); $socket->gantiLampu(new LampuLED()); $this->assertEquals("Nyala Terang", $socket->on()); } }
setelah dilakukan beberapa modifikasi, sekarang kita jalankan lagi unit test nya, maka hasilnya seperti berikut:
Kesimpulan
LSP bertujuan untuk menghindari error / bug ketika suatu object diganti dengan object yang lain. Karena itu LSP mengharuskan agar suatu object mempunyai perilaku yang sama dengan object yang digantikan. Untuk mencapai tujuan ini, kita bisa menggunakan abstraksi / interface untuk memastikan object mempunyai perilaku yang sama sehingga tidak mengganggu jalannya program.
Kira-kira cukup sekian ya tulisan ini. Jika ada yang perlu diperbaiki, bisa tulis di komentar
Komentar
Posting Komentar