Dependency Inversion Principle: How you should write code

Yan Yan
4 min readJul 27, 2022

--

depend on interface A

စကေးချွတ်ပီး လစာကောင်းတဲ့ developer ဖြစ်ဖို့ TDD(Test Driven Development) ကိုသိထားသင့်တယ်၊ အများအားဖြင့် software company တွေမှာ TDD approve နဲ့ရေးကြတယ်။ TDD ကိုမလုပ်ခင် SOLID ရဲ့ Dependency Inversion Principle ကိုနားလည်ထားသင့်တယ်။ fresher တွေတော့ နားလည်ဖို့မလွယ်ပေမဲ့ အလွယ်ကူဆုံးဖြစ်အောင် ဒီ article မှာရှင်းပြသွားပါမယ်။

What is DIP?

D ဆိုတဲ့ SOLID ရဲ့ အတိုကောက်၊ သူ့ကိုရှင်းရင်

High-level modules should not depend on low-level modules. Both should depend on abstractions.

Structure ကောင်းတဲ့ code ကို ကြည့်လိုက်ရင် code base က hierarchy(အဆင့်ဆင့်) အလိုက် ရှိတယ်၊ high-level module နဲ့ low-level module ဆိုပီး ရှိတယ်၊ fresher တွေက ဒါကိုနားလည်မှုလွဲနိုင်တယ်၊ low-level ကနေ high-level ကို directly depend သွားလုပ်ကြတယ်။နားလည်အောင် အောက်က code ကိုကြည့်

import java.util.Arrays;
import java.util.List;
// High Level Module
class ProductCatalog {

public void listAllProducts() {

SQLProductRepository sqlProductRepository = new SQLProductRepository();

List<String> allProductNames = sqlProductRepository.getAllProductNames();

// Display product names

}

}
// Low Level Module
class SQLProductRepository {

public List<String> getAllProductNames() {
return Arrays.asList("soap", "toothpaste", "shampoo");
}

}

ဒီမှာProductCatalog က SQLProductRepository implementation class ကို create လုပ်လိုက်တယ်။ OOP အရ extend to interface, not to implementation ဖြစ်ရမှာ၊ အဲ့တော့ ProductCatalogclass( high-level module) က sub-module(low-level module) ဖြစ်တဲ့ SQLProductRepository ပေါ်မှာ depend ဖြစ်နေတယ်။ ဒါတွေက code ရေးခါစ မကြာခဏဖြစ်တတ်တယ်။ ပြန်ပြောပါမယ် Dependency Inversion Principle က

High-level modules should not depend on low-level modules. Both should depend on abstractions.

ဆိုတော့ code က DIP ကို violation( ချိုးဖောက် ) နေတာကိုတွေ့ရမယ်၊ ဒီလောက်ဆို နားလည်လောက်မယ် ထင်တယ်။

ဘယ်လိုပြင်မလဲ၊ မပြင်ခင် software design ရဲ့ abstraction ဆိုတာကို ကြည့်ရအောင်

What is Abstraction?

(1) Code without abstraction

class Benz  {

public void drive() {

}

}

class CarUtil {

public static void drive(Benz benz) {
benz.drive();
}

}

CarUtil class ရဲ့ drive method က Benz instance ကို parameter နဲ့ လက်ခံထားတယ်။ ဒါကြောင့် drive method ကို ခေါ်ရင် Benz instance ကိုထည့်ပေးရမယ်၊ dependency ရှိနေတယ်၊ Software Design အရ tight coupling လို့ခေါ်တယ်။ ဘာလို့ဆို Benz class ရဲ့ drive method ကို ချိန်းရင် CarUtil class ကို direct effect သွားဖြစ်တယ် Bugs ဖြစ်စေနိုင်မဲ့ facts တွေ

Tight Coupling is the most undesirable feature in Software

(2) Code with Abstraction

interface Car {
public void drive();
}

class Benz implements Car {

@Override
public void drive() {


}

}

class Tesla implements Car {

@Override
public void drive() {

}

}

class CarUtil {

public static void drive(Car car) {
car.drive();
}

}

code structure အရ CarUtil ရဲ့ drive method က Benz ကို depend မဖြစ်တော့ဘဲ Car interface ပေါ်မှာဘဲ depend သွားဖြစ်တော့တယ်။ ဆိုလိုတာက Car interface ကို implement လုပ်ထားတဲ့ ဘယ် car မဆို လက်ခံနိုင်တယ်။ ဒါကို abstration လို့ခေါ်သလို loose coupling လို့လည်းခေါ်တယ်။

Abstration ကို နားလည်သွားရင် အပေါ်က code နဲ့ ပြန်ကြည့်အောင်

Refactoring Previous Code with Abstraction

“Abstractions should not depend on details. Details should depend on abstractions.”

import java.util.Arrays;
import java.util.List;

// High Level Module
class ProductCatalog {

public void listAllProducts() {

// High Level Module depends on Abstraction
ProductRepository productRepository = new SQLProductRepository();

List<String> allProductNames = productRepository.getAllProductNames();

// Display product names

}

}
interface ProductRepository {

List<String> getAllProductNames();

}

// Low Level Module
class SQLProductRepository implements ProductRepository {

public List<String> getAllProductNames() {
return Arrays.asList("soap", "toothpaste", "shampoo");
}

}

ProductCatalog class က SQLProductRepository ပေါ် depend မဖြစ်တော့ဘူး။ ProductRepository interface ပေါ်မှာဘဲ depend သွားဖြစ်တော့တယ်။ဘာကောင်းသွားလဲဆို

  • Concrete implementation ဖြစ်တဲ့ SQLProductReopsitoryကို ProductCatalogက direct access မလုပ်ဘဲ interface ကနေတစ်ဆင့် access လုပ်တယ်။ တစ်ခြား database ချိတ်ဖို့လိုအပ်လာရင် နောက် low level class create လုပ်လိုက်ရင် ရတယ်။ Dabase connection ကို ပြောင်းလို့ရသွားမယ်။ MySQL လား Mango ချိတ်မလားဆို interface ကို implement လုပ်ပီး ခေါ်ရုံဘဲ၊
  • ProductCatalog ရဲ့ ListAllProducts() မှာ Abstration အပေါ် depend ဖြစ်နေတဲ့ SQL class တွေခေါ်လိုက်ရုံဘဲ၊ loose coupling ဖြစ်တယ်။

ဒီအထိ ကျွန်တော်တို့ ဘာတွေရလိုက်လဲဆိုရင်

before refactoring

Refactoring မလုပ်ခင် ProductCatalog က SQlProductRepository ပေါ်မှာ depend ဖြစ်တယ်။ high-level module က low-module ပေါ်မှာ depend ဖြစ်တယ်လို့ ပြောရမယ်၊ tight coupling ဖြစ်တယ်။

after refactoring

ProductCatalog က ProductRepository ပေါ်မှာ depend ဖြစ်တယ်။ SQLProductRepository က ProductRepository ပေါ်မှာဘဲ depend ဖြစ်တယ်။

Dependency Inversion Principle အရ

High-level modules should not depend on low-level modules. Both should depend on abstractions.

စာကြောင်းကို နားလည်သွားမယ်ထင်တယ်။

နောက်တစ်ဆင့် အနေနဲ့ Dependency Injection ကိုကြည့်ရအောင်

import java.util.Arrays;
import java.util.List;

class ProductCatalog {

private ProductRepository productRepository;

public ProductCatalog(ProductRepository productRepository) {
this.productRepository = productRepository;
}

public void listAllProducts() {

List<String> allProductNames = productRepository.getAllProductNames();

// Display product names

}

}

interface ProductRepository {

List<String> getAllProductNames();

}

class SQLProductRepository implements ProductRepository {

public List<String> getAllProductNames() {
return Arrays.asList("soap", "toothpaste", "shampoo");
}

}

class EcommerceApplication {

public static void main(String[] args) {

ProductRepository productRepository = new SQLProductRepository();

ProductCatalog productCatalog = new ProductCatalog(productRepository);

productCatalog.listAllProducts();

}

}

ProductCatalog က ProductRepository ကို inject လုပ်ထားတာတွေ့ရမယ်။ ဒီလို Object ဆောက်ပီး inject လုပ်တာက ကောင်းတယ် ဘာလို့ဆို Mock data တွေနဲ့ TDD လုပ်တဲ့ချိန်မှာ အသုံ၀င်လို့ ၊ ဒီ TDD concept တွေကိုနောက်မှ ရှင်းပါတော့မယ်။

Conclusion

  • “High-level modules should not depend on low-level modules. Both should depend on abstractions.”
  • “Abstractions should not depend on details. Details should depend on abstractions.”

ဒီစာကြောင်းနှစ်ကြောင်းကို နားလည်ရင် congratulation ပါ DIP principle ကို သင်နားလည်သွားပါပီး။ experience ရှိရင်တော့ လွယ်လွယ်နဲ့ နားလည်နိုင်ပေမဲ့ ၊ မရှိရင် ခတ်နိုင်တယ်။ ဒါကြောင့် experience ရှိဖို့ intentional practice များများလုပ်ဖို့ လိုပါတယ်။

reference…

--

--