December 16, 2016

Dependency Injection - Part 1

Dependency Injection(DI) ဟာ CDI ရဲ့ အဓိက Feature တစ်ခုဖြစ်ပါတယ်။ OO Design Pattern တစ်ခုဖြစ်တဲ့ Dependency Injection Pattern ကို အခြေခံထားပါတယ်။ DI Pattern ဟာ Dependency တွေ အတွက် Hard Coding နဲ့ ရေးနေရတာကို ဖြောက်ဖျက်ပေးနိုင်ပါတယ်။ အဲ့ဒါကြောင့် Module တွေကြားမှာရှိတဲ့ Coupling ကို လျော့ချပေးနိုင်ပါတယ်။ အကျိုးဆက်အနေနဲ့ Maintainability ကို တိုးမြင့်စေနိုင်ပါတယ်။


What is Dependency?


ဥပမာအားဖြင့် MessageService Class က MailSender ဆိုတဲ့ Class ကို အသုံးပြုပြီး Message တွေကို Send လုပ်နေကြတယ်ဆိုပါစို့။ MessageSerivce အတွက် MailSender ဟာ မရှိမဖြစ်လိုအပ်နေပါတယ်။ ဘာလို့လဲဆိုတော့ MailSender မရှိပဲ Message ကို Send လုပ်လို့ရမှာ မဟုတ်လို့ပါ။
public class MailSender {
    
    public void send(String address, String message) {
        // sending message
    }
}


public class MessageService {
    
    private MailSender sender = new MailSender();

    public void send(String address, String message) {
        sender.send(address, message);
    }
}

ဒီအတိုင်းရေးထားတယ်ဆိုရင် လတ်တလောတော့ အစဉ်ပြေနေပါမယ်။ ဒါပေမဲ့ အကြောင်းတစ်မျိုးမျိုးကြောင့် MessageService ကို SMS Message ကိုပါ Send လုပ်ဖို့လိုအပ်လာပြီဆိုကြပါစို့။

ဒီနေရာမှာ ပြဿနာ ၂ ခုရှိနေပါတယ်။ MessageService ဟာ High Level Class ဖြစ်ပြီး MailSender ဟာ Low Level Class ဖြစ်ပါတယ်။ ပထမတစ်ခုက High Level Class ဖြစ်တဲ့ MessageService ဟာ Low Level Class ဖြစ်တဲ့ MailSender ကို တိုက်ရိုက်အသုံးပြုနေတဲ့အတွက် MailSender ကလွဲရင် တစ်ခြား Object တွေနဲ့သုံးလို့ရမှာ မဟုတ်တော့ဘူး။

အဲ့ဒီအစား ပိုပြီး Abstraction ကျတဲ့ Abstract Class ဒါမှမဟုတ် Interface ကို အသုံးပြုမယ်ဆိုရင် တစ်ခြား အုပ်စုတူ Class တွေကို အစားထိုးသုံးမယ်ဆိုရင် အသုံးပြုနိုင်မှာ ဖြစ်တယ်။ ဒါ့ကြောင့် ကုဒ်တွေကို ဒီလို ပြောင်းကြည့်ရအောင်။ Sender ဆိုတဲ့ Interface တစ်ခုကို ဆောက်မယ်။ ပြီးတော့ send(address, message) method ကို ရေးထားလိုက်မယ်။ ပြီးမှ MailSender ကနေ Sender ကို Implement လုပ်လိုက်မယ်။
public interface Sender {
    void send(String address, String message);
}

public class MailSender implements Sender{
    
    @Override
    public void send(String address, String message) {
        // sending message
    }
}


public class MessageService {
    
    private Sender sender = new MailSender();

    public void send(String address, String message) {
        sender.send(address, message);
    }
}

ဒါပေမဲ့ ဒီလိုရေးပြန်ရင်လဲ ပြဿနာ နောက်တစ်ခုကျန်နေပါသေးတယ်။ Sender Object ကို new MailSender() ဆိုပြီး Hard Coding နဲ့ Object ကို ဆောက်နေတဲ့နေရာဖြစ်ပါတယ်။ Dependency Object က Abstraction ဖြစ်တဲ့ Sender Interface ဆိုပေမဲ့ Object ဆောက်တာကို Class ထဲမှာ ရေးထားတဲ့အတွက် MailServcie မှာ SMS Send လုပ်ချင်ရင် MessageService ကို ပြုပြင်မှ ဖြစ်ပါမယ်။ ဒါ့ကြောင့် Class ထဲမှာ Dependency တွေကို Hard Coding နဲ့ Object ဆောက်နေတာဟာ Coupling ကို ပိုမိုမြင့်မားစေပါတယ်။

ကဲ ဒါဖြင့် Class ထဲမှာ Object မဆောက်ပဲ အဲ့ဒီ Dependency တွေကို ရရှိအောင် ဘယ်လိုလုပ်မလဲ။ နည်းလမ်း ၂ မျိုးရှိနိုင်ပါတယ်။ Constructor Argument ကနေတဆင့် Dependency Object ကို ရယူပြီး Dependency Object တွေမှာ အစားထိုးရင် ရနိုင်ပါတယ်။
public class MessageService {
    
    private Sender sender;

    public MessageService(Sender sender) {
     this.sender = sender;
    }

    public void send(String address, String message) {
        sender.send(address, message);
    }
}

နောက်တနည်းကတော့ setter method တွေနဲ့ Dependency တွေကို Set လုပ်တဲ့နည်းပါ။
public class MessageService {
    
    private Sender sender;

    public void setSender(Sender sender) {
     this.sender = sender;
    }

    public void send(String address, String message) {
        sender.send(address, message);
    }
}

အထက်ပါ အတိုင်း Constructor Argument ဒါမှ မဟုတ် setter တွေနဲ့ Dependency တွေကို Set လုပ်ပေးမယ်ဆိုရင် MessageService ဟာ သူ့ရဲ့ Dependency ဖြစ်တဲ့ Sender နဲ့ Coupling တွေကို ကင်းဝေးစေနိုင်ပါတယ်။ ဒါ့ကြောင့် MessageService နဲ့ Mail ကို ပို့ချင်ရင် MailSender Object ကို set လုပ်ပေးလိုက်ရုံပါပဲ။ တဖန် SMS ကို ပို့ချင်ရင် Sender Interface ကို Implement လုပ်ပြီး SMSSender Class ကို ဆောက်ပြီး အသုံးပြုရုံပါပဲ။

MessageService ကို သူ့ရဲ့ Dependency ကို မှီခိုမှု့မရှိအောင် လုပ်လိုက်နိုင်တဲ့အတွက် အဲ့ဒီ Object ကို နေရာအမျိုးမျိုးမှာ အသုံးချသွားနိုင်ပါတယ်။

ဒါပေမဲ့ MessageService မှာ Dependency တွေကို ဆောက်စရာမလိုအပ်တော့ပေမဲ့ MessageService ကို သုံးတဲ့ နေရာမှာ Object တွေကိုတော့ ဆောက်နေရဦးမှာပါပဲ။ တစ်နေနေရာမှာ Coupling ကတော့ ကျန်နေဦးမှာ ဖြစ်ပါတယ်


Dependency Injection With CDI

CDI ကို အသုံးပြုမယ်ဆိုရင် မိမိရဲ့ Application တွေထဲမှာရှိတဲ့ Class တွေရဲ့ Dependency တွေနေရာမှာ @Inject လို့ရေးသားပြီး Runtime မှာ Container ကနေ လိုအပ်တဲ့ Object တွေကို တည်ဆောက်ပြီး Inject လုပ်ပေးနိုင်အောင် ဆောင်ရွက်ထားပါတယ်။ ဒါ့ကြောင့် မိမိရဲ့ Application ထဲမှာ ဘယ်နေရာမှာမှ Object ဆောက်စရာ လိုအပ်တော့မှာ မဟုတ်ပါဘူး။ အရှေ့ကနမူနာနေရာမှာ ကို CDI နဲ့ ရေးကြည့်ပါမယ်။
public class MessageService {
 
    @Inject    
    private Sender sender;

    public void send(String address, String message) {
        sender.send(address, message);
    }
}

အများကြီး ရှင်းရှင်းလင်းလင်းရေးနိုင်တာကို တွေ့ရပါမယ်။ MessageService ထဲမှာ Sender Object နေရာမှာ @Inject Annotation ကို တပ်ပြီးရေးထားရုံနဲ့ သက်ဆိုင်ရာ Object ကို Container ကနေ အလိုအလျောက် လာပြီး Inject လုပ်ပေးနိုင်ပါလိမ့်မယ်။


Where can Inject?

ဘယ်နေရာတွေမှာ @Inject Annotation တွေကို ရေးသားလို့ရလဲ။ အထက်ပါ အတိုင်း Instance Variable တွေရဲ့ရှေ့မှာရေးသားနိုင်သလို Conctructor တွေရဲ့ ရှေ့မှာကော၊ setter method တွေရဲ့ရှေ့မှာလဲ ရေးသားနိုင်ပါတယ်။
public class MessageService {
 
    private Sender sender;

    @Inject    
    public void setSender(Sender sender) {
     this.sender = sender;
    }

    public void send(String address, String message) {
        sender.send(address, message);
    }
}
အထက်ပါ နမူနာကတော့ Setter Method မှာ @Inject Annotation ကို ရေးသားထားတာဖြစ်ပါတယ်။
public class MessageService {
 
    private Sender sender;

    @Inject    
    public MessageService(Sender sender) {
     this.sender = sender;
    }

    public void send(String address, String message) {
        sender.send(address, message);
    }
}
အထက်ပါကုဒ်ထဲမှာတော့ Argument ကို ယူတဲ့ Constructor ရှေ့မှာ @Inject ကို အသုံးပြုထားတာဖြစ်ပါတယ်။ ပြန်ကြည့်မယ်ဆိုရင် Depencency Object တွေကို set လုပ်လို့ရတဲ့နေရာတွေကြီးပါပဲ။


ဘယ်လို Class တွေကို Inject လုပ်ပေးနိုင်တာလဲ

ဒီနေရာမှာ စကားလုံးသုံးပုံကြောင့် အထင်မှားနိုင်ပါတယ်။ Inject လုပ်တဲ့နေရာမှာ ပါဝင် ပတ်သက်နေတာက နှစ်ခုရှိနိုင်ပါတယ်။ Injection Point မှာရေးထားတဲ့ Type နဲ့ အဲ့ဒီ​နေရာမှာလာပြီး Inject လုပ်မည့် Class ပါ။ ဒီနေရာမှာက ကျွန်တော်ပြောချင်တာက Inject လုပ်မည့် Class အကြောင်းဖြစ်ပါတယ်။

Injection Point မှာ Inject လုပ်မည့် Class တွေဟာ အောက်ပါ အရည် အချင်းနဲ့ ပြည့်စုံရပါမယ်။

  • Non Static Inner Class တစ်ခု မဟုတ်ရပါဘူး။
  • Concrete Class ဒါမှမဟုတ် @Decorator Annotation ကို တပ်ဆင် ရေးသားထားရပါမယ်။
  • No Argument Default Constructor တစ်ခုကို မဖြစ်မနေပိုင်ဆိုင်ရပါမယ်။
  • တကယ်လို့ Constructor မှာ Argument ကို ယူနေခဲ့ရင် အဲ့ဒီ Constructor ရှေ့မှာ @Inject ကို ရေးသားထားရပါမယ်။

အထက်ပါ အရည်အချင်းနဲ့ ပြည့်စုံမှသာ Container ကနေ Inject လုပ်ပေးနိုင်မှာ ဖြစ်ပါတယ်။

ဆက်ပါဦးမယ်
မင်းလွင်

No comments:

Post a Comment