• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            Shuffy

            不斷的學習,不斷的思考,才能不斷的進步.Let's do better together!
            posts - 102, comments - 43, trackbacks - 0, articles - 19

            面向切面(AOP)與面向對象(OOP)

            Posted on 2011-10-18 01:15 Shuffy 閱讀(665) 評論(0)  編輯 收藏 引用 所屬分類: Java
            首先你要明確的一點,AOP和OOP是兩種不同的認識事物的角度,并不是說有了AOP就不要用OOP.AOP所關注的是傳統OOP不能優雅解決的問題.(程序員通常都是完美主義者,當解決某個問題不優雅的時候,那就意味著不完美.)下面將就一個簡單的例子來說明他們到底如何的不同.

            作為一個使用OOP多年的人來說,當我聽說AOP可以解決一些OOP一直都不能優雅地解決的問題時,我覺得應該去探個究竟了.對兩種技術的比較最能給我們實際應用提供見解.這里我設計了一個例子:一個OOP應用,其中某些方面適合使用AOP.

            本 文展示了一個簡單的例子.一開始介紹了問題域,然后分別給出OOP與AOP的解決方案.后者使用了 JDK5.0,JUnit,和 AspectWerkz.最后說明如何編寫代碼.讀完本文后,我希望你能知道AOP到底是什么,解決什么樣的問題.(由于作者在后面AOP的例子中使用了 Java5.0的批注(Annotation),建議讀者先有所了解. -- 譯者注).

            問題域描述
            一個軟件公司雇傭一個程序員,指定給他一個業務部門并要求他隨時向經理報告.當團隊成員完成他們的目標時,經理會給他們相應的獎金.公司所需要的方案必須能夠增加一個新的雇員并給當前的員工增加獎金.為了方便,我們用CSV文件存儲數據.


            圖1 解決方案模型

            類Manager(經理)繼承自類Employee,包含一個額外的屬性,Managing Project.一個部門可能包含很多員工.多個部門構成了公司.暫不考慮公司這樣的一個類,因為它在問題域之外.

            解決方案設計
            以下流程圖描述了解決方案設計.


            圖2 對象之間的交互(增加一個新的員工,指派給他一個部門和經理)

            出于簡單的考慮,本文只關注必需的細節.當然你也可以深入代碼得到你想要的其他信息.
            [link]http://www.devx.com/assets/sourcecode/13172.zip[/link]
            EmployeeServiceTestCase, 一個JUnit測試用例,模擬一個最終用戶,創建新員工記錄,指派部門和經理.它獲取所有可用的部門和經理數據并顯示在圖形界面上.為了實例化域對象 BusinessUnit和Manager,獲得的記錄將傳遞給工廠類.之后,通過給EmployeeService傳遞一個引用來創建一個 Employee對象.這個服務類使用EmployeeFactory創建對象,并把這個對象傳給EmployeeRepository 來進行持久化操 作.

            應用程序中需要面向哪些"切面"
            到目前為止,對模型和設計的討論還限于一個較抽象的層面.現在,我轉向這個應用的其他方面 - 這對理解AOP的價值至關重要.

            操作所需的資源
            1. public static Set findAllBusinessUnits() throws RepositoryException {
            2. Set businessUnits = new HashSet();
            3. try {
            4. FileReader businessUnitFile = null;
            5. BufferedReader bufferedBusinessUnitFile = null;
            6. try {
            7. businessUnitFile = new FileReader(FILE_NAME);
            8. bufferedBusinessUnitFile = new BufferedReader(businessUnitFile);
            9. String businessUnitRecord;
            10. while((businessUnitRecord = bufferedBusinessUnitFile.readLine()) != null) {
            11. BusinessUnit businessUnit = BusinessUnitFactory.createBusinessUnit(businessUnitRecord);
            12. businessUnits.add(businessUnit);
            13. }
            14. } finally {
            15. if(bufferedBusinessUnitFile != null) {
            16. bufferedBusinessUnitFile.close();
            17. }
            18. if(businessUnitFile != null) {
            19. businessUnitFile.close();
            20. }
            21. }
            22. } catch(IOException ioe) {
            23. String message = "IOError. Unable to find Business Unit records";
            24. logger.log(SEVERE, message, ioe);
            25. throw new RepositoryException(message, ioe);
            26. }
            27. logger.log(INFO, "Manager Records returned:" + businessUnits.size());
            28. return businessUnits;
            29. }

            上面的代碼通過FileReader和BUfferedReader來讀取CSV文件中的業務數據.
            應用程序重復地從資源文件中取得數據然后在操作完成后釋放. 我們會發現:去掉程序的這兩個"切面"將提高代碼的可讀性并達到一個更好的設計,因為去掉這些"多余"的東西,剩下的代碼才是這個方法真正的精髓.這個方 法的作用是讀取業務單位數據.所以不應該也不需要去知道"如何獲取和釋放資源以及這個過程中出現的異常"這個"切面".同樣地,使用AOP處理異常也變得 不同.(后面將詳細介紹)

            持久層
            傳統的OOP使用倉庫類(repository classes)來打理應用程序的持久層.即:
            1. public class EmployeeRepository {
            2. public static void createEmployee(Employee employee) throws RepositoryException {
            3. //使用print writer把數據放入csv文件
            4. }
            5. public static String findEmployeeRecordById(String id) throws RepositoryException {
            6. //使用file reader來獲得指定id的員工數據
            7. }
            8. public static Employee findEmployeeById(String id) throws RepositoryException {
            9. //使用該方法獲取員工數據,Employee對象由工廠類創建
            10. }
            11. public static void updateEmployee(Employee employee) {
            12. //更新員工數據
            13. }
            14. }

            類EmployeeService 使用一個倉庫類給應用中相關雇員提供服務,在一個企業應用中,從域模型 (domain model)中去掉持久層代碼是一種設計上的改進.模型設計者和程序員就可以關注各自的業務邏輯和持久層處理.后面你將會看到如何通過 AOP來達到這樣的效果.

            日志
            刪除用于調試的日志代碼將會極大地改進代碼的可讀性.考慮下面的代碼片斷:
            1. public Employee createEmployee(String name,
            2. String contactNumber,
            3. BusinessUnit businessUnit,
            4. Manager manager)
            5. throws EmployeeServiceException {
            6. String id = createNewEmployeeId();
            7. Employee employee =
            8. EmployeeFactory.createEmployee(id, name, contactNumber, businessUnit, manager);
            9. try {
            10. EmployeeRepository.createEmployee(employee);
            11. } catch(RepositoryException re) {
            12. String message = "Created employee successfully:" + employee;
            13. logger.log(SEVERE, message);
            14. throw new EmployeeServiceException(message, re);
            15. }
            16. logger.log(INFO, "Created employee successfully:" + employee);
            17. return employee;
            18. }

            上面的代碼里包含了一個致命錯誤和一個成功信息.輸出日志這一"切面"同樣可以移到業務模型外獨立實現.

            異常處理
            異 常處理的例子我這里不再贅述,但這節已經通過上面的代碼討論了潛在的問題.當你調用EmployeeRepository 對象的 createEmployee 方法時,你可能會得到一個RepositoryException異常.傳統的解決方法是,在這個類中處理.另一種方法 是,當RepositoryException 異常被拋出時createEmployee 方法返回null,catch塊中的其他邏輯可以在類外處理 這一錯誤.
            錯誤處理在不同的情況中也會不同.但是,通過AOP可以區分開每種情況.


            圖3

            圖3中描述了AOP方法的設計以及在一個更抽象的層次上類間的交互.你可以通過對比圖1和圖3來更好地理解AOP.
            程 序的目的是通過BusinessUnit對象讀取CSV文件中的記錄然后 填入類BusinessUnitService 中的map.使用AOP來填充 這個map有點類似后門(backdoor)方法 -- 控制被委派給BusinessUnit 來讀取存儲介質中的記錄.

            AOP就是定 義一些切入點(pointcut)和處理方法(advice).一個"切入點"是源代碼中一個執行點.前面的例子定義了一個"切入點" -- 類 BusinessUnitService中的findBusinessUnits方法.一個"處理方法"顧名思義就是當執行到某個"切入點"時的一塊代 碼.類BusinessUnitPersistentAspect 包括advice方法findAllBusinessUnits,該方法從存儲介質中 載入數據,然后使用工廠類創建BusinessUnit 對象.然后這個對象被加入map,map對象的引用通過 BusinessUnitService 對象獲得."切入點"和"處理方法"組成了所謂的"切面(Aspect)"

            為了讀取存儲介質中的數據,OOP方法通過一個DAO類來做.而AOP中,你只要定義一個"切入點"和相應的"處理方法"來讀取數據.AOP框架會以advice的形式注入代碼,既可以在執行期也可以在編譯期.

            總 而言之,當類BusinessUnitService 中的findAllBusinessUnits 方法被調用時,AOP框架會在"切入點"處注入處 理方法,通過BusinessUnit 對象預先讀取數據來填充map對象.這樣,持久層方面的代碼就可以移到業務代碼之外了.

            新方法里的"切面"

            本節討論如何用AOP為應用程序的各個"切面"建模

            操作資源

            類BusinessUnitPersistenceAspect 的持久方法使用了一個buffered reader.你甚至可以定義"切面"的"切面",但為了簡單,這里只關注類的查找方法.
            1. @Aspect("perJVM")
            2. public class BufferedFileReaderAspect {
            3. @Expression("execution(* org.javatechnocrats.aop.withaop.aspects.BusinessUnitPersistenceAspect.find*(..))")
            4. Pointcut businessUnitPersistenceAspect;
            5. // 其他"切入點"定義
            6. @Expression("businessUnitPersistenceAspect ||
            7. employeePersistenceAspect ||
            8. managerPersistenceAspect")
            9. Pointcut allPersistencePointcuts;
            10. private Map<Class, String> fileNames;
            11. public BufferedFileReaderAspect() {
            12. System.out.println("BufferedFileReaderAspect created");
            13. fileNames = new HashMap<Class, String>();
            14. fillFileNames();
            15. }
            16. @Before("allPersistencePointcuts")
            17. public void assignReader(JoinPoint joinPoint) throws Throwable {
            18. System.out.println("assignReader advice called");
            19. Object callee = joinPoint.getCallee();
            20. IBufferedFileReaderConsumable bufReaderConsumable = (IBufferedFileReaderConsumable)callee;
            21. Class persistenceClass = callee.getClass();
            22. String fileName = fileNames.get(persistenceClass);
            23. FileReader fileReader = new FileReader(fileName);
            24. BufferedReader bufferedReader = new BufferedReader(fileReader);
            25. bufReaderConsumable.setBufferedReader(bufferedReader);
            26. }
            27. @AfterFinally("allPersistencePointcuts")
            28. public void releaseReader(JoinPoint joinPoint) throws Throwable {
            29. //釋放buffered reader等資源
            30. }
            31. //其他方法
            32. }


            上面的代碼試圖為每一個方法創建"切入點"-- 所有以find開頭的方法.無論何時這些方法被調用,assignReader方法都會被提前執行.這里它獲取被調用的類實例然后設置新建的buffered reader.

            同樣地,在releaseReader 方法里,代碼會預先關閉buffered reader集合.本節只解釋@before和@
            AfterFinally 這兩個"切入點".(以J2SE 5.0的標記定義).另外,你也可以在方面定義的xml文件中聲明他們.你可以查看例程源代碼中的aop.xml文件.

            下載

            持久化

            前 面提到,OOP方法使用BusinessUnit 來為應用的持久層填充Map.在下面的高亮代碼中(@before一行,以及while循環代碼 - 譯者注),當BusinessUnitService 中的方法findAllBusinessUnits 被調用時"處理方法 "findAllBusinessUnits 也將被調用.
            1. @Aspect("perJVM")
            2. public class BusinessUnitPersistenceAspect implements IBufferedFileReaderConsumable {
            3. private BufferedReader buffFileReader;
            4. @Before("execution(Collection org.javatechnocrats.aop.withaop.BusinessUnitService.findAllBusinessUnits())")
            5. public void findAllBusinessUnits(JoinPoint joinPoint) throws Throwable {
            6. System.out.println("findAllBusinessUnits advice called");
            7. Map<String, BusinessUnit> businessUnits =
            8. ((BusinessUnitService)joinPoint.getThis()).getBusinessUnits();
            9. String businessUnitRecord;
            10. while((businessUnitRecord = buffFileReader.readLine()) != null) {
            11. BusinessUnit businessUnit = BusinessUnitFactory.createBusinessUnit(businessUnitRecord);
            12. businessUnits.put(businessUnit.getId(), businessUnit);
            13. }
            14. }
            15. public void setBufferedReader(BufferedReader buffFileReader) {
            16. System.out.println("BusinessUnitPersistenceAspect.setBufferedReader called");
            17. this.buffFileReader = buffFileReader;
            18. }
            19. public BufferedReader getBufferedReader() {
            20. System.out.println("BusinessUnitPersistenceAspect.getBufferedReader called");
            21. return this.buffFileReader;
            22. }
            23. }

            "處理方法"從數據存儲中讀取記錄,使用工廠類創建一個BusinessUnit實例.然后這個實例被加入到Map.該Map掌管程序的所有持久化"切面".

            日志
            本文中的例子沒有包含一個完整的日志AOP解決方案.但是,它為java.lang.Object類的toString方法定義了一個"切入點"來獲取類的調試信息.因此,域中的類不需要實現toString方法.通常可能你可能需要為每一個類都要實現這個方法.

            1. @Aspect("perJVM")
            2. public class LoggingAspect {
            3. @Around("execution(String org.javatechnocrats.aop.withaop..*.toString())")
            4. public Object toStringAdvice(JoinPoint joinPoint) throws Throwable {
            5. System.out.println("toStringAdvice called");
            6. String toString = (String)joinPoint.proceed();
            7. Object target = joinPoint.getThis();
            8. Field fields[] = target.getClass().getDeclaredFields();
            9. List members = new ArrayList(fields.length + 1);
            10. members.add(toString);
            11. for(Field field : fields) {
            12. field.setAccessible(true);
            13. Object member = field.get(target);
            14. members.add(field.getName() + "=" + member);
            15. }
            16. return members.toString();
            17. }

            你也可以用這個樣例代碼完成錯誤處理"切面".

            深入源代碼

            為了理解樣例需求的OOP設計,請參看源代碼并思考以下幾個問題: 下載

            * 首先分析oldway包中EmployeeServiceTestCase 類中的代碼
            *查看testEmployeeCredit 方法
            *搞懂業務類Employee和BusinessUnit
            *學習 service,repository和factory概念.這些是業務驅動設計的主要概念.
            *更深入地理解oldway包中的service,repository和factory類

            而AOP地理解則應該是:
            *分析newway包中EmployeeServiceTestCase 類
            *查看service,repository和factory類,基本和前一種差不多.只是你要讓"處理方法"截獲程序的執行流程.
            *研究aspect類學習"切入點"的定義

            要執行程序,你需要做的工作:
            * 下載AspectWerkz 2.0 http://aspectwerkz.codehaus.org/
            *設置以下的環境變量:
            set JAVA_HOME=c:\Program Files\Java\jdk1.5.0
            set ASPECTWERKZ_HOME=C:\aw_2_0_2
            set PATH=%PATH%;%ASPECTWERKZ_HOME%\bin
            set CLASSPATH=
            C:\aw_2_0_2\lib\aspectwerkz-2.0.RC2.jar;C:\aw_2_0_2\lib\aspectwerkz-jdk5-2.0.RC2.jar; classes;C:\ junit\3.8.1\resources\lib\junit.jar
            *解壓縮源代碼和其他文件
            *編譯Java文件,但不要編譯測試用例否則你調試時會遇到一個錯誤.
            *進行離線調試.假設你把文件解壓縮到c:\aop ,類文件解壓到c:\aop\classes,在c:\aop目錄下執行以下命令:
            %ASPECTWERKZ_HOME%\bin\aspectwerkz -offline etc/aop.xml -cp classes classes
            *AOP框架會修改類來注入必要的字節碼
            *編譯測試用例,使用JUnit運行它.

            后記
            當你完成了上面的這些工作,你應該有以下的領悟:
            *程序中的交叉關聯
            *關于AOP中深入源代碼

            為了理解樣例需求的OOP設計,請參看源代碼并思考以下幾個問題: 下載

            * 首先分析oldway包中EmployeeServiceTestCase 類中的代碼
            *查看testEmployeeCredit 方法
            *搞懂業務類Employee和BusinessUnit
            *學習 service,repository和factory概念.這些是業務驅動設計的主要概念.
            *更深入地理解oldway包中的service,repository和factory類

            而AOP地理解則應該是:
            *分析newway包中EmployeeServiceTestCase 類
            *查看service,repository和factory類,基本和前一種差不多.只是你要讓advice截取程序的流程.
            *研究aspect類學習point cut的定義

            要執行程序,你需要做的工作:
            * 下載AspectWerkz 2.0 http://aspectwerkz.codehaus.org/
            *設置以下的環境變量:
            set JAVA_HOME=c:\Program Files\Java\jdk1.5.0
            set ASPECTWERKZ_HOME=C:\aw_2_0_2
            set PATH=%PATH%;%ASPECTWERKZ_HOME%\bin
            set CLASSPATH=
            C:\aw_2_0_2\lib\aspectwerkz-2.0.RC2.jar;C:\aw_2_0_2\lib\aspectwerkz-jdk5-2.0.RC2.jar; classes;C:\ junit\3.8.1\resources\lib\junit.jar
            *解壓縮源代碼和其他文件
            *編譯Java文件,但不要編譯測試用例否則你調試時會遇到一個錯誤.
            *進行離線調試.假設你把文件解壓縮到c:\aop ,類文件解壓到c:\aop\classes,在c:\aop目錄下執行以下命令:
            %ASPECTWERKZ_HOME%\bin\aspectwerkz -offline etc/aop.xml -cp classes classes
            *AOP框架會修改類來注入必要的字節碼
            *編譯測試用例,使用JUnit運行它.

            后記
            當你完成了上面的這些工作,你應該有以下的領悟:
            *程序中的交叉關聯
            *關于AOP中"切面"的含義
            *如何用AOP來把程序業務層中的交叉關聯分離出來,使用"切入點"和"處理方法"
            *OOP和AOP時在程序控制流上的不同

            從本文你應該也得到一種看待實際開發的新視角.你應該有信心使用AOP來改進項目中的設計,建模,提高代碼的重用性.至少,你可以開始使用AOP來處理日志,錯誤和持久化.

            個人覺得,AOP的學習曲線相對較陡,尤其在理解定義"切入點"的句法時.理想的情況是,使用OOP來設計業務模型,使用AOP把業務模型中的交叉關聯移出,從而使代碼簡潔并提高可讀性.

            AOP的一個缺點是會使調試變得困難,因為不同于OOP,程序流變的復雜了,交互是由編譯期或執行期決定.我準備將來做一些自動化工具來解決這個問題.
             
            原文鏈接:http://www.360doc.com/content/11/1018/01/7940162_157035710.shtml
            精品无码久久久久久国产| 天天爽天天狠久久久综合麻豆| 久久99国产精品一区二区| 国产美女久久久| 久久这里有精品视频| 亚洲国产精品一区二区久久hs| 国产成人精品白浆久久69| 久久99亚洲综合精品首页| 无码国内精品久久综合88 | 91精品国产91久久综合| 久久精品国产精品亚洲精品| 免费一级做a爰片久久毛片潮| 久久精品亚洲精品国产色婷| 久久久久久国产精品免费免费| 中文国产成人精品久久不卡| 精品久久人人妻人人做精品| 综合久久国产九一剧情麻豆| 国内精品久久久久久久影视麻豆| 色婷婷综合久久久久中文一区二区| 99久久综合狠狠综合久久| 国内精品久久久人妻中文字幕| 欧美性猛交xxxx免费看久久久| 国产精品久久久久久福利漫画| 久久精品国产乱子伦| 欧美久久天天综合香蕉伊| 精品99久久aaa一级毛片| 99久久人妻无码精品系列| 久久久女人与动物群交毛片 | 久久亚洲中文字幕精品有坂深雪| 欧美国产精品久久高清| 久久99精品久久久久久不卡 | 久久精品国产亚洲av麻豆小说| 久久亚洲精品无码aⅴ大香| 久久久久99精品成人片牛牛影视| 久久精品国产福利国产秒| 久久99精品国产麻豆宅宅| 久久狠狠色狠狠色综合| 国产精品成人无码久久久久久 | 久久天天婷婷五月俺也去| 亚洲午夜福利精品久久| 久久AV高潮AV无码AV|