# Liskov Subtitution Principle (LSP)

Gagasan atau idea yang dikenalkan oleh Barbara Liskov pada tahun 1987 pada conference dengan judul *Data abstraction and Hierarchy*. Dalam gagasannya memiliki kesimpulan kurang lebih seperti ini, Jika S adalah subtitusi dari T, maka T bisa digantikan oleh S tanpa merubah properti-properti pada program. Maka syarat S agar dapat menggantikan T adalah S harus memiliki perilaku seperti T dengan syarat-syarat tertentu.

> Tulisan ini adalah bagian dari [SOLID Principles](https://verri.andriawan.web.id/series/solid-principles) Series, sehingga untuk bisa membaca secara menyeluruh dan berkesinambungan, alangkah lebih baik bisa membaca secara urut dari link series [berikut](https://verri.andriawan.web.id/series/solid-principles).

Jika dalam buku *“Design Principles and Design Pattern”* menyebutkan seperti ini

> *“Subclass should be substitutable for their base classes”*

Bahwa class turunan harus bisa disubtitusikan untuk Class Basenya. Sebagai contoh, jika Sebuah function semisal `function office(ketua: PemimpinClass)`, jika `WakilKetuaClass` merupakan subclass dari `PemimpinClass` maka argument pada fungsi `office` bisa digantikan oleh `WakilKetuaClass`.

Principle ini terlihat sederhana dan banyak yang tidak terlalu mendetailkan prinsip ini. Menurut saya pribadi prinsip ini merupakan prinsip paling rumit dibanding 4 prinsip lainnya dalam SOLID, karena pada prinsip ini ada beberapa aturan yang harus dipatuhi. Terutama konsep-konsep dasar pada OOP seperti *Covariance*, *Contravariance* dan *Invariant*. Jika belum memahami tentang hal tersebut silahkan membaca posting sebelumnya di link berikut.

> [Covariance dan Contravariane](https://verri.andriawan.web.id/covariance-dan-contravariance)

Selain itu silahkan membaca posting lainnya tentang class invariant pada link [berikut](https://hashnode.com/post/cmasacigd000d09jv9w6tdbxf).

> [Class Invariant](https://hashnode.com/post/cmasacigd000d09jv9w6tdbxf)

Perlunya memahami konsep-konsep dasar tersebut dapat mempermudah reader dalam memahami aturan-aturan yang perlu dipatuhi pada LSP. Sederhananya pada prinsip ini adalah tentang mensubtitusikan/menggantikan Obejct sejenis. Sebagai contoh.

```tsx
// super type
class Machine {}

// sub type mini machine
class MiniMachine extends Machine {}

// sub type large machine
class LargeMachine extends Machine {}
```

Jika melihat cuplikan code tersebut akan mengerti apa yang dimaksud mensubtitusi. Dimana pada cuplikan code tersebut terdapat tiga buah class, dimana dua diantaranya adalah subtype. Jika kedua subtype tersebut diimplementasikan akan seperti ini

```tsx
const mini = new MiniMachine
const large = new LargeMachine

function dispatch(machine: Machine) {}
```

Pada function `dispatch` terdapat param `machine` yang bertype `Machine`. Karena param machine bertipe `Machine` maka dua instance yang dibuat bisa saling menggantikan sebagai argument pada param machine di fungsi `dispatch` karena kedua instance tersebut merupakan turunan dari class Machine.

Namun LSP tidak sesederhana itu. Ada aturan-aturan yang perlu dipatuhi. Hal tersebut diperlukan guna mejaga ketahanan dari program yang dibuat.

## Aturan-aturan LSP

Berikut ini adalah pejelasan tentang aturan-aturan yang perlu dipatuhi.

### Signature Rules

* **Convariance** dimana keluar dari method atau function harus memiliki degree yang lebih kecil dari suppertype-nya.
    
* **Contravariance** dimana nilai argument harus memiliki degree lebih besar dari pada supertype-nya.
    
* Jumlah `throw exception` pada logika method bisa dikurangi, tapi tidak boleh ditambah.
    

Untuk pembahasan tentang [Covariance dan Contravariance](https://verri.andriawan.web.id/covariance-dan-contravariance) bisa membaca artikle tentang ini terlebih dahulu pada link [berikut](https://verri.andriawan.web.id/covariance-dan-contravariance). Aturan lainnya bahwa jumlah **Throw Exception** pada method **boleh dikurangi** tapi **tidak boleh ditambah**. Karena akan mengakibatkan unpredictable throw exception yang mengakibatkan system break.

### Property Rules

* **Class invariant**
    

Dimana class/object selalu terjaga dan tergaransi selama object class tersebut hidup. Untuk penjelasan detail tentang class invariant bisa membaca pada post sebelumnya di link berikut

> [Class Invariant](https://hashnode.com/post/cmasacigd000d09jv9w6tdbxf)

* **History constraint**
    

Dimana property yang seharusnya immutable tetap terjaga atau tidak boleh berubah. Sebagai contoh.

```typescript
class VendingMachine {
	protected maxItem: number = 0;
	
	constructor(maxItem: number) {
	   this.maxItem = maxItem;
	}
}
```

Pada class `VendingMachine` terdapat property `maxItem` pada code di atas. Property ini hanya boleh di-set nilai sekali ketika pembuatan object. Karena pada dasarnya `maxItem` adalah asumsi bahwa ketika pembuatan machine, maksimal barang yang bisa ditampung sesuai dengan desain dari mechine itu sendiri dan bernilai constant selama mesin berjalan. Sehingga kecil kemungkinan jika ada perubahan nilai `maxItem` tersebut.

* **Preconditions boleh dipertahankan tapi tidak boleh dikuatkan dari method di class parent-nya.**
    

Dimana terdapat conditional flow yang mungkin ada pada head dari sebuah method public pada supertype, atau bisa kita syarat yang harus dipenuhi sebelum masuk ke section logic dari method tersebut. Misal seperti code di bawah ini.

```typescript
class VendingMachine {	
	protected maxItem: number = 10;
	
	doSomething(min: number) {
	
	  // pre condition
	  if (min < 2 || min >= this.maxItem) {
	     throw new Error("Invalid min item")
	  }
	  
	  // main logic method
	  
	  // post condition
	}
}

class MiniMachine extends VendingMachine {

	doSomething(min: number) {
		// dikuatkan, dilarang menguatkan
		// ---
	    // if (min < 3 || min >= this.maxItem) {
	    //   throw new Error("Invalid min item")
	    // }
	  
	    // dilemahkan, diperbolehkan
		if (min < 1 || min >= this.maxItem) {
	     throw new Error("Invalid min item")
	  }
	}
	
}
```

Pada cuplikan code tersebut, pada method `doSomething` terdapat control flow dimana nilai `min` tidak boleh kurang dari `2`. Maka pada method di subtype-nya, nilai `2` tersebut tidak boleh dikuatkan misalkan menjadi `3`. Hal tersebut bisa mengakibatkan program break/berhenti jika terdapat program lain menggunakan/mengeksekusi method tersebut. Kecuali jika batas tersebut dilemahkan, misal jadi batasnya 1 item. Hal tersebut tidak akan bermasalah, karena secara asumsinya ketika pertama kali class tersebut didesain, batasan minimal 2 item. Jadi semua client yang menggunkan class tersebut masih mempertahankan bahwa syarat vending machine minimal 2 item. Ketika nilai diganti jadi 1, maka program tidak akan break karena masih memenuhi syarat.

Jadi yang dimaksud pada pre-condition adalah, jangan membuat syarat baru pada head method yang akan membuat program break.

* **Postconditions boleh dipertahankan tapi tidak boleh dilemahkan dari method di class parent-nya**
    

Post condition kebalikan dari pre condition. Post condition ini berada pada foot method atau setelah logic utama dari method dijalankan. Dimana syarat dari LSP adalah tidak boleh dilemahkan.

```typescript
class VendingMachine {	
	protected maxItem: number = 10;
	
	doSomething(min: number) {
	
	  // pre condition
	  
	  // main logic method
	  const sum = min - 3;
	  
	  // post condition
	  if (sum < 5) {
	     throw new Error("Value is too small")
	  }
	}
}

class MiniMachine extends VendingMachine {
   doSomething(min: number) {
	   
	   // Dilemahkan, tidak diperbolehkan
	   // ---
	   // if (sum < 6) {
	   //  throw new Error("Invalid result")
	   // }
	   
	   // Dikuatkan, diperbolehkan
	   // --
	   
	   if (sum < 3) {
		   throw new Error("Value is too small")
	   }
   }
}
```

Pada cuplikan code tersebut terlihat bahwa jika hasil sum kurang dari `5` maka `tampilkan Error`. Ketika sudah banyak client yang menjadi turunan dari class `VendingMachine` tersebut, berarti sudah mendefinisikan bahwa paling besar adalah nilai `5` sebagai batas. Jika postcondition pada subtype dilemahkan, misal menjadi `sum < 6`, maka ketika hasil perhitungan pada main logic bernilai `5`, program yang seharusnya bisa berlanjut tapi berakhir break karena tidak memenuhi syarat.
