최근에 나는 항상 유명한 Java 프레임 워크 스프링, 스트럿 등의 핵심 인 IOC (Control of Control) 및 DI (의존성 주입)와 같은 프로그래밍 원리 또는 패턴에 노출되어 왔습니다. 그것들을 읽은 후에, 나는 약간의 이해가 있습니다. 이제 나는 책의 설명과 내 자신의 처리를 결합하여 다음과 같이 분류 할 것입니다.
EG1
문제 설명 :
일일 보고서, 월간 보고서 등과 같은 다양한 요구 사항에 따라 Excel 또는 PDF 형식으로 보고서를 생성 할 수있는 시스템을 개발하십시오.
해결책:
"인터페이스 지향 프로그래밍"의 원리에 따르면 인터페이스 및 구현이 분리되어야합니다. 즉, 보고서 생성 기능은 일반 인터페이스 보고서 게이터로 추출되며 Excel 및 PDF 형식으로 Excel 및 PDF 보고서를 생성하는 두 가지 구현이 제공됩니다. 그런 다음 클라이언트는 서비스 제공 업체 ReportService를 통해 해당 보고서 인쇄 기능을 얻습니다.
구현 방법 :
위의 내용에 따르면 다음 클래스 다이어그램이 얻어집니다.
코드 구현 :
인터페이스 reportGenerator {public void Generate (테이블 표); } 클래스 ExcelGenerator는 ReportGenerator {public void Generate (표) {System.out.println ( "Excel report 생성 ..."); }} class pdfgenerator implements reportGenerator {public void Generate (표) {System.out.println ( "PDF 보고서 생성 ..."); }} Class ReportService {// 특정 요구 사항에 대한 보고서 생성기 생성 책임자 개인보고자 생성기 생성기 = 새로운 PDFGenerator (); // private static reportgenerator generator = new ExcelGenerator (); public void getDailyReport (날짜 날짜) {table.setDate (날짜); // ... generator.generate (표); } public void getMonthlyReport (월) {table.setmonth (월); // ... generator.generate (표); }} public class client {public static void main (string [] args) {reporservice reportservice = new ReportService (); reportservice.getDailyReport (새 날짜 ()); //reportService.getMonthlyReport(new date ()); }}
EG2
문제 설명 :
위 코드의 주석에 표시된 바와 같이, 특정 보고서 생성기는 ReportsErvice 클래스에서 내부적으로 하드 코딩하여 작성되므로 ReportsErvice가 PDFGenerator 또는 ExcelGenerator에 직접 의존 했으며이 명백한 단단한 커플 링을 제거해야합니다.
솔루션 : 컨테이너를 소개하여 중간 관리자, 즉 컨테이너 (컨테이너)를 소개하는 컨테이너를 소개합니다. 컨테이너 (컨테이너)는보고 시스템과 관련된 객체를 균일하게 관리합니다 (여기서는 구성 요소, Bean이라고합니다). 여기에서는이 콩을 저장하기 위해 키 값 쌍의 형태로 해시 맵 인스턴스를 사용합니다.
구현 방법 :
클래스 다이어그램은 다음과 같이 얻습니다.
코드 구현 :
클래스 컨테이너 {// 키 값 쌍에 다양한 필수 구성 요소를 저장 Bean 개인 정적지도 <String, Object> Bean; public container () {beans = new Hashmap <string, object> (); // 특정 보고서 생성기 생성 및 저장 ReportGenerator ReportGenerator = New PDFGenerator (); Beans.put ( "ReportGenerator", ReportGenerator); // reportservice reportservice reportservice = new ReportService ()의 참조를 얻고 관리합니다. beans.put ( "Reportservice", Reportservice); } public static 객체 getBean (String id) {return beans.get (id); }} Class ReportService {// 단단한 커플 링 관계를 제거하고 컨테이너로 바꾸십시오 // 개인 정적보고자 생성기 생성기 = 새로운 pdfgenerator (); Private ReportGenerator Generator Generator = (ReportGenerator) Container.getBean ( "ReportGenerator"); public void getDailyReport (날짜 날짜) {table.setDate (날짜); Generator.generate (표); } public void getMonthlyReport (월) {table.setmonth (월); Generator.generate (표); }} public class client {public static void main (String [] args) {container container = new Container (); Reportservice Reportservice = (Reportservice) container.getbean ( "Reportservice"); reportservice.getDailyReport (새 날짜 ()); //reportService.getMonthlyReport(new date ()); }}
타이밍 차트는 대략 다음과 같습니다.
효과:
위에서 볼 수 있듯이 보고서 서비스는 더 이상 특정 ReportGenerator와 직접 관련이 없습니다. 컨테이너를 사용하여 인터페이스 및 구현을 분리하여 시스템 구성 요소 Bean의 재사용 성을 향상 시켰습니다. 현재 구성 파일을 사용하여 컨테이너의 특정 구성 요소의 정의를 실시간으로 얻을 수도 있습니다.
EG3
문제 설명 :
그러나 위의 클래스 다이어그램을 보면 Reportservice와 컨테이너 사이에 양방향 관계가 있으며 상호 의존성이 있음을 쉽게 알 수 있습니다. 또한 ReportService를 재사용하려면 단일 컨테이너의 특정 검색 논리에 직접적으로 의존합니다. 다른 컨테이너마다 다른 구성 요소 검색 메커니즘 (예 : JNDI)이있는 경우 현재 보고서 서비스를 재사용하는 것은 컨테이너의 내부 검색 로직을 수정해야 함을 의미합니다.
해결책 : 서비스 로케이터 소개
간접 레이어 서비스 로케이터를 재 도입하는 것은 구성 요소 검색 로직에 대한 인터페이스를 제공하는 데 사용됩니다. Wikipedia의 설명 또는 Java EE 1 및 2의 설명을 참조하십시오.이를 통해 가능한 변경 사항을 분리 할 수 있습니다.
구현 방법 :
클래스 다이어그램은 다음과 같습니다.
코드 구현 :
// 실제 애플리케이션에서 인터페이스를 사용하여 통합 인터페이스 클래스 서비스를 제공 할 수 있습니다. servicelocator {private static 컨테이너 컨테이너 = new Container (); public static reportgenerator getReportGenerator () {return (reportGenerator) 컨테이너 ( "reportGeneraator"); }} Class ReportService {private ReportGenerator ReportGenerator = servicelocator.getReportGenerator (); // ...}EG4
문제 설명 :
그러나 컨테이너를 소개하든 서비스 로케이터를 사용하든 보고서 서비스는 특정 구성 요소를 검색하고 생성하는 데 '활성'이므로 고객으로서 필요한 것, 필요한 위치 및 얻을 수있는 방법에 대해 명확해야합니다. 갑자기 특정 논리적 세부 사항을 무엇, 어디서, 어떻게하기 때문에 추가되어야합니다.
예를 들어, '컨테이너 소개'의 이전 구현 방법에는 다음과 같은 코드가 있습니다.
Class ReportService {// 단단한 커플 링 관계를 제거하고 컨테이너로 바꾸십시오 // private static reportgenerator generator = new pdfgenerator (); // 개인 reportGenerator Generator = (ReportGenerator) 컨테이너 .getBean ( "ReportGenerator");
'서비스 로케이터 소개'구현 방법에는 다음과 같은 코드가 있습니다.
클래스 servicelocator {privatestatic 컨테이너 컨테이너 = 새로운 컨테이너 (); publicstatic reportgenerator getReportGenerator () {// 여전히 컨테이너입니다. }} class ReportService {// reportservice 결국, 그것은 여전히 servicelocator private reportgenerator reportgenerator = servicelocator.getReportGenerator ()에 대한 '적극적으로'검색 및 대표단; }
해결책:
이 경우 'Active'를 '수동적'으로 변경하면 의심 할 여지없이 Reportservice의 내부 지식 (즉, 구성 요소 찾기 논리)이 줄어 듭니다. 제어의 역전 원리 (IOC)에 따르면,이 풀 (풀, 활성)은 푸시 (푸시, 수동) 모드로 변환됩니다.
예를 들어, 우리가 일반적으로 사용하는 RSS 구독은 푸시 응용 프로그램으로, 기사 업데이트를 적극적으로 얻기 위해 하루에 여러 번 좋아하는 사이트에 로그인하는 번거 로움을 저장합니다.
Dependency Injection (DI)은 이러한 종류의 수동적 수신을 달성하고 복잡한 논리가 포함 된 고객 (여기, 보고서 서비스) 자체의 문제를 줄이고 너무 많이 아는 것입니다.
구현 방법 :
우리는 '수동적'수신이되기를 원하기 때문에 서비스 로케이터 모드를 사용하지 않고 컨테이너 예로 돌아 가야합니다. 수정 된 클래스 다이어그램은 다음과 같이 이것으로부터 얻습니다.
원래 클래스 다이어그램은 다음과 같습니다. 당신은 그것을 체크 아웃하고 의견 프롬프트에주의를 기울일 수 있습니다.
코드 구현 :
예제를 컴파일하고 실행하고 추적 코드의 실행 결과를 사용하여 전체 클래스 다이어그램을 명시 적으로 인스턴스화하고 순서대로 서로 협력하기 위해 다음과 같이 각 클래스의 생성자에 추가되었습니다.
import java.util.date; import java.util.hashmap; import java.util.map; // 컴파일하고 실행할 수 있으려면 두 가지 더 중요하지 않은 클래스가 있습니다. 클래스 월 {} 클래스 테이블 {publicVoid setDate (날짜 날짜) {} publicVoid setMonth (월) {}} // ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- System.out.println("2...start initialization ExcelGenerator ... "); } publicVoid generate (표) {System.out.println ( "Excel 보고서 생성 ..."); }} 클래스 pdfgenerator는 ReportGenerator {public pdfgenerator () {System.out.println ( "2 ... pdfgenerator의 초기화 시작 ..."); } publicVoid generate (표) {System.out.println ( "PDF 보고서 생성 ..."); }}//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Reportservice Reportservice Reportservice = New ReportService ()의 참조를 얻고 관리합니다. // 위에 생성 된 특정 reportGenerator 인스턴스를 주입했습니다. beans.put ( "Reportservice", Reportservice); System.out.println ( "5 ... 종료 초기화 컨테이너 ..."); } publicstatic 객체 getBean (String id) {System.out.println ( "마지막 GET 서비스 구성 요소 ... getBean () ->" + id + "..."); returnbeans.get (id); }} class ReportService {// 개인 정적보고자 게이터 생성기 = 새로운 pdfgenerator (); // 위의 단단한 커플 링 관계를 제거하고 컨테이너로 바꾸십시오 // private reportGenerator generator = (ReportGenerator) 컨테이너 // getBean ( "ReportGenerator"); // 위의 "활성"조회를 제거하고 외부 주입 된 객체 개인보고자 생성기를 저장하기 위해 개인 필드를 제공합니다. // setter 양식의 외부에서 publicVoid를 주입 SetReportGenerator (ReportGenerator Generator) {System.out.println ( "4 ... reportGenerator를 주입 시작 ..."); this.generator = 발전기; } 개인 테이블 테이블 = 새로운 테이블 (); public reportservice () {system.out.println ( "3 ... 초기화 시작 보고서 서비스 ..."); } publicVoid getDailyReport (날짜 날짜) {table.setDate (날짜); Generator.generate (표); } publicVoid getMonthlyReport (월) {table.setmonth (월); Generator.generate (표); }} publicclass client {publicstaticVoid main (string [] args) {// 컨테이너 새 컨테이너 (); reportservice reportservice = (Reportservice) 컨테이너 .getbean ( "Reportservice"); reportservice.getDailyReport (새 날짜 ()); // reportservice.getThlyRopport (new Date ()); }}
실행 결과 :
1 ... 초기화 시작 컨테이너 ... 2 ... 초기화 시작 PDFGenerator ... 3 ... 초기화 시작 보고서 서비스 ... 4 ...보고 reportGenerator를 주입 시작 시작 ... 5 ... 종료 초기화 컨테이너 ... 마지막으로 서비스 구성 요소를 가져옵니다 ... getBean () -> ReportService ... PDF 보고서 생성 ... PDF 보고서 생성 ...
알아채다:
1. 위의 실행 결과의 인쇄 순서에 따라 코드에 추가 된 특정 숫자가 합리적이므로 프로그램 실행 프로세스를 시뮬레이션하므로 더 이상 시퀀스 다이어그램을 그립니다.
2.이 예제에서 IOC 및 DI의 사용은 Client (즉, 구성 요소 Demander)로서 보고서 서비스를 기반으로하며, 코드의 클라이언트 클래스 Main ()의 테스트 코드는 서비스 구성 요소의 최종 사용자이지만 필요한 것은 구성 요소가 아니라 구성 요소가 가지고있는 서비스입니다.
3. 실제로, 스프링 박스 클립에서 컨테이너를 초기화하는 것은 분명히 최종 사용자 클라이언트가해야 할 일이 아니므로 서비스 제공 업체가 사전에 시작해야합니다.
4. 최종 사용자 클라이언트에서는 여전히 컨테이너 생성자에 인스턴스화 된 서비스 구성 요소를 얻기 위해 컨테이너를 사용하여 컨테이너를 사용합니다. 특정 애플리케이션에서 사용 가능한 서비스 구성 요소는 일반적으로 XML 및 기타 구성 파일을 사용하여 서버에 배포되며 구성 파일은 컨테이너가 읽고 반사 기술과 결합하여 특정 서비스 구성 요소를 작성하고 주입합니다.
분석 :
이전에 보고서 서비스는 컨테이너에서 서비스 구성 요소를 적극적으로 요청했지만 이제는 컨테이너 주입 (주입, 즉 푸시) 서비스 구성 요소를 수동적으로 기다렸습니다. 제어는 분명히 기본 모듈 (reportservice는 구성 요소 Demander)에서 높은 수준 모듈 (컨테이너는 구성 요소 제공자)으로 전송됩니다. 즉, 제어는 반전됩니다.