首先,什么是依賴反轉,為什么叫反轉?
高層不應該依賴細節,細節應該依賴高層。
什么是高層?什么是細節?對一個系統來說,業務邏輯是高層,其他是細節。業務邏輯是僅僅包括用例、業務實體部分,不包括任何框架、存儲(數據庫)、其他系統等部分,是純粹的。其他細節,包括框架、數據庫、消息隊列,都是細節。業務邏輯應該不依賴任何細節。細節的實現可以任意替換而不影響業務邏輯。這樣的業務邏輯,可以測試、容易測試。
拿通常的Java Web應用來說,一般我們使用的包括:Controller->Service->DAO->數據庫。 其中,Service是高層,包含業務邏輯,其他都是細節。
為什么叫“反轉”,從調用順序來講,Service是依賴DAO來實現業務數據的存取的,那么這時候高層就依賴了細節。但如果我們在Service層抽象一個DAO接口,讓實際的DAO從這個接口派生一個類出來。那么Service這個模塊就不依賴於DAO的實現,而是依賴了一個內部的接口。這樣調用的方向和依賴的方向就相反了,稱之為“反轉”。例子:【Service->IDAO】<-DAOImpl->數據庫。
依賴注入和依賴反轉的關系
依靠框架的依賴注入功能,我們很容易實現依賴反轉的時候實現實例的注入。
業務對象:
public class QuizServiceImpl implements QuizService { private BankDAO bankDAO; private QuizDAO quizDAO;
在業務對象里面,我們定義了兩個DAO,都是接口。
在外部實現的時候,我們首先派生兩個對象,實現這兩個DAO接口。然后用Spring的Configuration功能,將這些接口的實現注入到Service中。Service完全不知道Spring框架、數據庫的存在,他只關注自己的業務邏輯,以及自己聲明的兩個DAO接口。
@Configuration public class ServiceConfig { @Bean BankDAO bankDAO(){ return new MemoryBankDAOImpl(); } @Bean QuizDAO quizDAO(){ return new MemoryQuizDAOImpl(); } @Bean QuizService quizService(BankDAO bankDAO,QuizDAO quizDAO){ QuizServiceImpl quizService=new QuizServiceImpl(); quizService.setBankDAO(bankDAO); quizService.setQuizDAO(quizDAO); return quizService; } @Bean QuestionAdminService questionAdminService(BankDAO bankDAO){ QuestionAdminServiceImpl questionAdminService=new QuestionAdminServiceImpl(); questionAdminService.setBankDAO(bankDAO); return questionAdminService; } }
這樣我們就可以在Controller等地方,使用@Autowired對QuiService進行自動注入。而Service完全不知道這些細節的存在!當然也就不存在依賴。
通過依賴反轉,高層的業務邏輯和各種細節完全解耦。你甚至可以這么做:一個工程寫業務邏輯,然后打包成一個jar,另外一個實現的工程引用這個jar,把數據庫、Controller等實現了,完成完整功能。