Java Dependency Injection - Shembull Tutorial i Modelit të Dizajnit DI
Modeli i dizajnit të Java Dependency Injection na lejon të heqim varësitë e koduara fort dhe ta bëjmë aplikacionin tonë të lidhur lirshëm, të zgjerueshëm dhe të mirëmbajtur. Ne mund të implementojmë injeksion të varësisë në java për të lëvizur rezolucionin e varësisë nga koha e përpilimit në kohën e ekzekutimit.
Java Dependency Injection
Injeksioni i varësisë në Java duket i vështirë për t'u kuptuar me teorinë, kështu që unë do të merrja një shembull të thjeshtë dhe më pas do të shohim se si të përdorim modelin e injektimit të varësisë për të arritur bashkim të lirë dhe shtrirje në aplikacion. Le të themi se kemi një aplikacion ku konsumojmë EmailService
për të dërguar email. Normalisht do ta zbatonim këtë si më poshtë.
package com.journaldev.java.legacy;
public class EmailService {
public void sendEmail(String message, String receiver){
//logic to send email
System.out.println("Email sent to "+receiver+ " with Message="+message);
}
}
Klasa EmailService
mban logjikën për të dërguar një mesazh email tek adresa e emailit të marrësit. Kodi ynë i aplikimit do të jetë si më poshtë.
package com.journaldev.java.legacy;
public class MyApplication {
private EmailService email = new EmailService();
public void processMessages(String msg, String rec){
//do some msg validation, manipulation logic etc
this.email.sendEmail(msg, rec);
}
}
Kodi ynë i klientit që do të përdorë klasën MyApplication
për të dërguar mesazhe me email do të jetë si më poshtë.
package com.journaldev.java.legacy;
public class MyLegacyTest {
public static void main(String[] args) {
MyApplication app = new MyApplication();
app.processMessages("Hi Pankaj", "pankaj@abc.com");
}
}
Në pamje të parë, nuk duket asgjë e keqe me zbatimin e mësipërm. Por logjika e kodit të mësipërm ka kufizime të caktuara.
- Klasa
MyApplication
është përgjegjëse për të inicializuar shërbimin e postës elektronike dhe më pas për ta përdorur atë. Kjo çon në varësi të kodifikuar. Nëse duam të kalojmë në ndonjë shërbim tjetër të avancuar të postës elektronike në të ardhmen, ai do të kërkojë ndryshime të kodit në klasën MyApplication. Kjo e bën të vështirë zgjerimin e aplikacionit tonë dhe nëse shërbimi i postës elektronike përdoret në klasa të shumta, atëherë kjo do të ishte edhe më e vështirë.- Nëse duam ta zgjerojmë aplikacionin tonë për të ofruar një veçori shtesë të mesazheve, si mesazhi SMS ose Facebook, atëherë do të na duhet të shkruajmë një aplikacion tjetër për këtë. Kjo do të përfshijë ndryshime të kodit në klasat e aplikacionit dhe në klasat e klientëve gjithashtu.
- Testimi i aplikacionit do të jetë shumë i vështirë pasi aplikacioni ynë po krijon drejtpërdrejt shembullin e shërbimit të postës elektronike. Nuk ka asnjë mënyrë që t'i tallim këto objekte në klasat tona të testimit.
Dikush mund të argumentojë se ne mund të heqim krijimin e shembullit të shërbimit të postës elektronike nga klasa MyApplication
duke pasur një konstruktor që kërkon shërbimin e postës elektronike si argument.
package com.journaldev.java.legacy;
public class MyApplication {
private EmailService email = null;
public MyApplication(EmailService svc){
this.email=svc;
}
public void processMessages(String msg, String rec){
//do some msg validation, manipulation logic etc
this.email.sendEmail(msg, rec);
}
}
Por në këtë rast, ne po kërkojmë nga aplikacionet e klientëve ose klasat e testimit të inicializojnë shërbimin e postës elektronike që nuk është një vendim i mirë dizajni. Tani le të shohim se si mund të aplikojmë modelin e injektimit të varësisë java për të zgjidhur të gjitha problemet me zbatimin e mësipërm. Injeksioni i varësisë në java kërkon të paktën sa më poshtë:
- Përbërësit e shërbimit duhet të dizajnohen me klasën bazë ose ndërfaqen. Është më mirë të preferoni ndërfaqet ose klasat abstrakte që do të përcaktonin kontratën për shërbimet.
- Klasat e konsumatorëve duhet të shkruhen në termat e ndërfaqes së shërbimit.
- Klasat e injektuesve që do të inicializojnë shërbimet dhe më pas klasat e konsumatorit.
Java Dependency Injection - Komponentët e Shërbimit
Për rastin tonë, ne mund të kemi MessageService
që do të deklarojë kontratën për zbatimin e shërbimit.
package com.journaldev.java.dependencyinjection.service;
public interface MessageService {
void sendMessage(String msg, String rec);
}
Tani le të themi se kemi shërbime Email dhe SMS që zbatojnë ndërfaqet e mësipërme.
package com.journaldev.java.dependencyinjection.service;
public class EmailServiceImpl implements MessageService {
@Override
public void sendMessage(String msg, String rec) {
//logic to send email
System.out.println("Email sent to "+rec+ " with Message="+msg);
}
}
package com.journaldev.java.dependencyinjection.service;
public class SMSServiceImpl implements MessageService {
@Override
public void sendMessage(String msg, String rec) {
//logic to send SMS
System.out.println("SMS sent to "+rec+ " with Message="+msg);
}
}
Shërbimet tona java të injektimit të varësisë janë gati dhe tani ne mund të shkruajmë klasën tonë të konsumatorit.
Java Dependency Injection - Konsumator i Shërbimit
Nuk na kërkohet të kemi ndërfaqe bazë për klasat e konsumatorëve, por unë do të kem një ndërfaqe Consumer
që deklaron kontratën për klasat e konsumatorëve.
package com.journaldev.java.dependencyinjection.consumer;
public interface Consumer {
void processMessages(String msg, String rec);
}
Zbatimi i klasës sime të konsumatorit është si më poshtë.
package com.journaldev.java.dependencyinjection.consumer;
import com.journaldev.java.dependencyinjection.service.MessageService;
public class MyDIApplication implements Consumer{
private MessageService service;
public MyDIApplication(MessageService svc){
this.service=svc;
}
@Override
public void processMessages(String msg, String rec){
//do some msg validation, manipulation logic etc
this.service.sendMessage(msg, rec);
}
}
Vini re se klasa jonë e aplikacionit po përdor vetëm shërbimin. Ai nuk inicializon shërbimin që çon në \ndarje më të mirë të shqetësimeve. Gjithashtu përdorimi i ndërfaqes së shërbimit na lejon të testojmë lehtësisht aplikacionin duke tallur Shërbimin e Mesazhit dhe të lidhim shërbimet në kohën e ekzekutimit në vend që të përpilojmë Tani jemi gati të shkruajmë klasat e injektuesve të varësisë nga Java që do të inicializojnë shërbimin dhe gjithashtu klasat e konsumatorit.
Java Dependency Injection - Klasat e Injectors
Le të kemi një ndërfaqe MessageServiceInjector
me deklaratën e metodës që kthen klasën Consumer
.
package com.journaldev.java.dependencyinjection.injector;
import com.journaldev.java.dependencyinjection.consumer.Consumer;
public interface MessageServiceInjector {
public Consumer getConsumer();
}
Tani për çdo shërbim, do të duhet të krijojmë klasa injektori si më poshtë.
package com.journaldev.java.dependencyinjection.injector;
import com.journaldev.java.dependencyinjection.consumer.Consumer;
import com.journaldev.java.dependencyinjection.consumer.MyDIApplication;
import com.journaldev.java.dependencyinjection.service.EmailServiceImpl;
public class EmailServiceInjector implements MessageServiceInjector {
@Override
public Consumer getConsumer() {
return new MyDIApplication(new EmailServiceImpl());
}
}
package com.journaldev.java.dependencyinjection.injector;
import com.journaldev.java.dependencyinjection.consumer.Consumer;
import com.journaldev.java.dependencyinjection.consumer.MyDIApplication;
import com.journaldev.java.dependencyinjection.service.SMSServiceImpl;
public class SMSServiceInjector implements MessageServiceInjector {
@Override
public Consumer getConsumer() {
return new MyDIApplication(new SMSServiceImpl());
}
}
Tani le të shohim se si aplikacionet e klientëve tanë do ta përdorin aplikacionin me një program të thjeshtë.
package com.journaldev.java.dependencyinjection.test;
import com.journaldev.java.dependencyinjection.consumer.Consumer;
import com.journaldev.java.dependencyinjection.injector.EmailServiceInjector;
import com.journaldev.java.dependencyinjection.injector.MessageServiceInjector;
import com.journaldev.java.dependencyinjection.injector.SMSServiceInjector;
public class MyMessageDITest {
public static void main(String[] args) {
String msg = "Hi Pankaj";
String email = "pankaj@abc.com";
String phone = "4088888888";
MessageServiceInjector injector = null;
Consumer app = null;
//Send email
injector = new EmailServiceInjector();
app = injector.getConsumer();
app.processMessages(msg, email);
//Send SMS
injector = new SMSServiceInjector();
app = injector.getConsumer();
app.processMessages(msg, phone);
}
}
Siç mund ta shihni që klasat tona të aplikacionit janë përgjegjëse vetëm për përdorimin e shërbimit. Klasat e shërbimit krijohen në injektorë. Gjithashtu, nëse duhet të zgjerojmë më tej aplikacionin tonë për të lejuar mesazhe në facebook, do të na duhet të shkruajmë vetëm klasa shërbimi dhe klasa injektori. Pra, zbatimi i injektimit të varësisë e zgjidhi problemin me varësinë e koduar dhe na ndihmoi ta bëjmë aplikacionin tonë fleksibël dhe të lehtë për t'u zgjeruar. Tani le të shohim se sa lehtë mund të testojmë klasën tonë të aplikacionit duke u tallur me klasat e injektorit dhe të shërbimit.
Java Dependency Injection - Case Test JUnit me Injektor Mock dhe Shërbimi
package com.journaldev.java.dependencyinjection.test;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.journaldev.java.dependencyinjection.consumer.Consumer;
import com.journaldev.java.dependencyinjection.consumer.MyDIApplication;
import com.journaldev.java.dependencyinjection.injector.MessageServiceInjector;
import com.journaldev.java.dependencyinjection.service.MessageService;
public class MyDIApplicationJUnitTest {
private MessageServiceInjector injector;
@Before
public void setUp(){
//mock the injector with anonymous class
injector = new MessageServiceInjector() {
@Override
public Consumer getConsumer() {
//mock the message service
return new MyDIApplication(new MessageService() {
@Override
public void sendMessage(String msg, String rec) {
System.out.println("Mock Message Service implementation");
}
});
}
};
}
@Test
public void test() {
Consumer consumer = injector.getConsumer();
consumer.processMessages("Hi Pankaj", "pankaj@abc.com");
}
@After
public void tear(){
injector = null;
}
}
Siç mund ta shihni që unë jam duke përdorur klasa anonime për talljen e klasave të injektuesve dhe shërbimeve dhe mund të testoj lehtësisht metodat e mia të aplikimit. Unë jam duke përdorur JUnit 4 për klasën e mësipërme të testit, prandaj sigurohuni që të jetë në rrugën e ndërtimit të projektit tuaj nëse jeni duke ekzekutuar mbi klasën e testit. Ne kemi përdorur konstruktorë për të injektuar varësitë në klasat e aplikacionit, një mënyrë tjetër është përdorimi i një metode setter për të injektuar varësi në klasat e aplikacionit. Për injektimin e varësisë së metodës setter, klasa jonë e aplikacionit do të zbatohet si më poshtë.
package com.journaldev.java.dependencyinjection.consumer;
import com.journaldev.java.dependencyinjection.service.MessageService;
public class MyDIApplication implements Consumer{
private MessageService service;
public MyDIApplication(){}
//setter dependency injection
public void setService(MessageService service) {
this.service = service;
}
@Override
public void processMessages(String msg, String rec){
//do some msg validation, manipulation logic etc
this.service.sendMessage(msg, rec);
}
}
package com.journaldev.java.dependencyinjection.injector;
import com.journaldev.java.dependencyinjection.consumer.Consumer;
import com.journaldev.java.dependencyinjection.consumer.MyDIApplication;
import com.journaldev.java.dependencyinjection.service.EmailServiceImpl;
public class EmailServiceInjector implements MessageServiceInjector {
@Override
public Consumer getConsumer() {
MyDIApplication app = new MyDIApplication();
app.setService(new EmailServiceImpl());
return app;
}
}
Një nga shembujt më të mirë të injektimit të varësisë së setterit janë shënimet java. Gjithçka që na nevojitet është të shënojmë metodën e fushës, konstruktorit ose vendosësit dhe t'i konfigurojmë ato në skedarët ose klasat e konfigurimit xml.
Përfitimet e Java Dependency Injection
Disa nga përfitimet e përdorimit të Dependency Injection në Java janë:
- Ndarja e shqetësimeve
- Reduktimi i kodit të bojlerplate në klasat e aplikacionit sepse e gjithë puna për inicializimin e varësive trajtohet nga komponenti injektor
- Përbërësit e konfigurueshëm e bëjnë aplikacionin lehtësisht të zgjerueshëm
- Testimi i njësisë është i lehtë me objekte tallje
Disavantazhet e Java Dependency Injection
Injeksioni i varësisë në Java ka gjithashtu disa disavantazhe:
- Nëse përdoret tepër, mund të çojë në probleme të mirëmbajtjes sepse efekti i ndryshimeve dihet në kohën e ekzekutimit.
- Injektimi i varësisë në java fsheh varësitë e klasës së shërbimit që mund të çojnë në gabime të kohës së ekzekutimit që do të ishin kapur në kohën e përpilimit.
Shkarkoni Projektin e Injektimit të Varësisë
Kjo është e gjitha për modelin e injektimit të varësisë në java. Është mirë ta njohim dhe ta përdorim atë kur kemi kontrollin e shërbimeve.