posts - 176, comments - 240, trackbacks - 0, articles - 7

              據說"Less is more"是1961年寶姿品牌提出的設計理念,它開創了簡約優雅的時尚風格。Unix系統的設計可以說也是這一設計理念的最佳體現。使用Unix工具, 讀Unix系統源碼,我們時時都能體會到一種簡約之美。而Microsoft發放出來的源代碼一般都相當冗長,變量名也長,這一度讓我很反感。為了簡化C ++中的COM編程,我不得不專門寫了一個封裝框架。
              簡約并不簡單。例如,注釋一般都是期望能夠幫助理解的,應該是有益的。但注釋太多就會干擾對程序的理解。這里關鍵就是能否提供有效的信息量。利用這一點,我們換一個角度看,多未必意味著復雜。例如,這樣的一個函數調用
                    StupidFunc(arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8)
              顯然不如
                    BetterFunc(userName:arg1, userMail:arg2, ...)
              非常緊湊的調用可能需要極端強度的注意力和思考力的投入,即要求我們付出的更多才能夠理解。Perl這樣的腳本是可以緊致到變態的一種語言。 Microsoft的自動化組件支持命名參數,則大大簡化了OLE編程。與此類似,xml動態腳本中的函數調用也是比較容易理解的,甚至比java函數調 用更容易理解。
                 <sendMail from="a@b.com" to="c@d.com" text="hello" />
              使用witrix平臺中的SQL類寫起SQL語句來雖然語句很長,但參數意義明顯,并不容易出錯。
              很多時候都存在著某個最適尺度,偏離了這個尺度,我們就會遇上各種各樣的"More"。不要畫蛇添足,也不要削足適履。

          posted @ 2005-12-12 22:46 canonical 閱讀(683) | 評論 (1)編輯 收藏

              在witrix平臺中,validate.js提供了完整的客戶端輸入校驗框架。其基本思想是為每個輸入控件指定驗證函數(validator屬性),在提交Form的時候自動調用該驗證函數即可。
          <form action="test.jsp">
          <input type="text" name="userName" validator="js.validate.checkNotEmpty(value,'用戶名')" />
          <input type="button" value="submit" onclick="js.validate.submitForm(this.form)" />
          </form>
              witrix平臺的一個基本設計原則是模塊的獨立性,不僅各個模塊之間的耦合很少,我們還盡量避免使用配置文件。與struts等web框架不同, witrix的輸入校驗不依賴于外部配置文件,可以完全獨立的使用。雖然jsplet框架也提供了服務器端校驗的支持,但在實際使用過程中卻很少使用。客 戶端校驗提供了更好的用戶體驗。而如果我們需要進一步確保業務邏輯的穩定性,例如避免用戶偽造客戶端url請求,數據校驗需要在業務邏輯對象層進行而不應 是在解析用戶請求的時候。針對每個form所寫的配置文件有很多不方便的地方,例如witrix平臺支持從數據庫描述文件直接生成操作界面的快速開發,校 驗規則在數據描述文件中指定,而同一個字段可能在多個界面中出現,如果針對Form寫校驗配置文件,就會出現冗余,而難以保證結構的同步。實際上,一個結 構在界面上表現一次,又在校驗配置文件中表現一次,就必然會出現同步問題,解決的方法就是面向對象設計中的對象化,局部化,而不是一個個分離的處理層。

              很多客戶端的校驗框架使用的是一個萬能的校驗函數,通過參數不同來實現不同校驗。例如
          <input type="text" name="userName" validateType="required" />
          這種方式的擴展性不好。正如面向對象設計中的通常做法,我們通過使用回調函數(虛擬函數)來實現可擴展的設計。

          posted @ 2005-12-12 22:26 canonical 閱讀(935) | 評論 (0)編輯 收藏

              守破離(Shu Ha Ri)是日本劍道(Kendo)的哲學。http://c2.com/cgi/wiki?ShuHaRi  (日本人很善于推銷自己傳統的思想,而中國的傳統卻似乎在盲目自卑和盲目自大兩個極端之間徘徊)
              守是模仿(Imitate),遵循,是無我的過程。在日本的傳統心性中,守的階段需要完全開放心志,全盤接受導師的教導。此時應該學習唯一的真理,知道唯 一正確的方法,分清對與錯。通過長期不輟的練習,將對規則的記憶固化在自己的身體中。初學者看似是自由的,但也是不明智的(unwisely),我們總是 傾向于采用錯誤的方式。
              beginners are very hard to fight... they don't do anything you expect them to do. They move freely, and randomly. Only by returning to very fundamental principles, can one uncover the faults (unwisdom) in their actions and defeat them. 簡言之,就是沒有結構。

              破是變(diverge),是自我意識逐漸增強,心智逐漸收縮的過程。Just 'winning' is not enough, you must win well. 這個階段我們需要區分出好與壞而不僅僅是兩分的對與錯。
              離是返樸歸真, 是忘我的過程。最終我們得到行為和思想上的自由(freedom)。離看似是隨機的,但實際上是混沌的(chaos)。
              It's being good irrespective of whether you are right or wrong.看似打破常規,舉手投足卻都遵循著道(Tao),這是持續而自由的創造。
              傳統上守破離這三個遞進的階段是在導師監護下完成的,導師決定你是否進入下一階段。而在沒有導師的情況下,我們需要增加一個步驟:檢驗(Test),即我 們需要縮短每個階段的時間,對我們的修行成果進行檢驗,通過迭代循環來自我實現階段跨越。(http://www.aikidofaq.com/essays/tin/shuhari.html 提到Test, 大概是這篇文章在agile社區流傳的原因之一吧,呵呵)。
               我們無法跨越守的階段。敏捷編程絕不意味著沒有design pattern。沒有良好的基本功,一切都是空談。

             破,首先是破除權威。小的時候我們喜歡引用大師,喜歡談論他們的軼事,現在也是引用,但卻經常略帶狡黠的歪曲他們的原意。大學以后應該少去閱讀大部頭的 書,讀薄的書,并把書讀薄。真正的思想并不是很多,當大量的細節都成為背景知識以后,我們需要進行思考的內容并不是很多。避免重復書中的原話,因為那是別 人的思想。廣為涉獵,多做比較。換個角度看一看,或通過類比,盡力建立事物之間的關聯,同中求異,異中求同。我以物理學的觀點來看待軟件,這是我采取的破 的方式。

          posted @ 2005-12-12 22:24 canonical 閱讀(1386) | 評論 (0)編輯 收藏

          witrix平臺中的tpl模板技術是一種通用的xml動態標簽技術,不僅可以用于文本生成,而且可以用于任何需要動態標簽的地方,例如工作流引擎 的配置和執行腳本。tpl模板引擎采用的不是jsp tag的標準機制,而是重新設計并實現的。在開發的后期,因為jstl標準出現,我們對標簽的命名作了一定的修改,以盡量符合標準的調用接口。tpl模板 語言完全符合xml規范,其標簽定義都是完全獨立開發的。在開發tpl的時候,我們甚至沒有看到任何類似于c:forEach和c:if的標簽設計。但是 我們發現,tpl的動態處理功能與jstl雖然命名不同,但是基本是等價的,所以修改是非常直接的過程。

          FreeMarker是一種流 行的文本模板語言,其語法類似于xml tag,但是命名規則不同。這實在是一種令人費解的設計。有意思的是,我們發現tpl的功能集也包含了FreeMarker的功能集。這實際上表明了一件 事情,xml動態標簽存在一些必然的功能需求,無論是jsp tag, FreeMarker還是tpl, 所不同的只是表現形式而已。但這種表現形式的差異卻又造成了實際功能上的巨大差異。

          tpl與FreeMarker具體對比如下。

          宏定義
          <#macro greet person>
          <font size="+2">Hello ${person}</font>
          </#macro>]]>

          <c:lib namespace="test">
          <greet demandArgs="person">
          <font size="+2">Hello ${person}</font>
          </greet>
          </c:lib>

          tpl具有更加強大的變量域控制手段,可以通過importVars參數來指定是否使用調用環境中的變量作為缺省參數。另一方面,tpl具有更加靈活的參數校驗規則,可以通過demandArgs, otherArgs等參數來指定對自定義標簽參數的校驗規則。
          調用宏
          <@greet person="Fred" />
          <test:greet person="Fred" />

          嵌套內容
          <#macro border>
          <table border="4" cellspacing="0" cellpadding="4"><tr><td>
          <#nested>
          <#nested>
          </tr></td></table>
          </#macro>
          <c:lib namespace="test">
          <border type="bodyTag">
          <table border="4" cellspacing="0" cellpadding="4"><tr><td>
          <cp:compile src="${tagBody}" />
          </tr></td></table>
          </border>
          </c:lib>

          tpl的<cp:compile>指令在執行時可以指定xslt參數,從而在編譯tagBody之前應用xslt變換。
          復雜嵌套
          與FreeMark一樣,嵌套內容可以是復雜內容

          <@border>
          <ul>
          <@do_thrice>
          <li><@greet person="Joe"/>
          /@do_thrice
          </ul>
          /@border
          <test:border>
          <ul>
          <test:do_thrice>
          <li><test:greet person="Joe" /></li>
          </test:do_thrice>
          </ul>
          </test:border>

          導入庫
          <#import "/lib/my_test.ftl" as my>
          <c:lib src="/lib/my_test.ftl" namespace="my" />

          創建或替代變量
          <#assign mail="jsmith@other.com" />
          <c:set var="mail" value="jsmith@other.com" default="xx"/>

          判斷
          <#if animals.python.price < animals.elephant.price>
          Pythons are cheaper than elephants today.
          </#if>
          <c:if test="${lt(animals.python.price,animals.elephant.price)}">
          Pythons are cheaper than elephants today.
          </c:if>

          tpl因為是xml語法,算術操作符<和>必須轉義后才能使用,使用起來很不方便,因而最終決定tpl不支持操作符,通過lt(), gt()等函數來實現功能。
          循環
          <#list animals as being>
          <tr><td>${being.name}<td>${being.price} Euros
          </#list>
          <c:forEach var="being" items="${animals}" >
          <tr><td>${being.name}<td>${being.price} Euros
          </c:forEach>

          tpl提供<c:tile>等多種循環方式
          include指令
          <#include "/copyright_footer.html">
          <c:include src="/copyright_footer.html" />

          tpl強大的模板功能加上jsplet框架面向對象的能力,使得我們可以輕易的封裝復雜的界面組件。而且這種封裝能力還不需要Tapestry那種復雜的配置文件。tpl對portal應用的支持也是一個自然的發展過程。

          posted @ 2005-12-12 22:18 canonical 閱讀(1782) | 評論 (2)編輯 收藏

              http://www.ps.uni-sb.de/~duchier/python/continuations.html
              A continuation is a procedure that takes the value of the current expression and computes the rest of the computation.

              Continuation是一種非常古老的程序結構,關于它的理論分析可謂淵源流長,參見http://library.readscheme.org/page6.html
              continuation簡單的說起來就是entire default future of a computation, 即對程序"接下來要做的事情"所進行的一種建模. 這一概念在理論上當然存在著巨大的價值, 至少它使得我們有一種手段把程序未來的運行過程明確的表達出來(給它取了個名字), 從而有可能對之作進一步的分析. continuation是對未來的完整描述, 這對于理論分析而言是有很多方便之處的, 正如統計學中最常見的分析工具是分布函數而不是密度函數一樣. 實際上任何程序都可以通過所謂的CPS(Continuation Passing Style)變換而轉換為使用continuation結構, 例如
              int foo(int x){
                  return x+1;
              }
               ==>
              void foo(int x,Continuation c){
                  c.continueWith(x+1);
              }   
              
              使用continuation的函數不"返回"值,而是把值作為一個參數傳遞給continuation從而"繼續"處理值. 在傳統的軟件理論中, 程序本身在運行期是固定不變的, 我們只需要記錄下執行點(excution point)的信息(例如指針位置和堆棧內容)即足以完整的描述程序未來的運行情況, 因此continuation有時也被看作是"帶參數的goto", 是goto語句的一種函數形式.
              在函數式語言中, continuation的引入是非常自然的過程, 考察如下函數調用
                   h(g(k(arg)))
              根據函數的結合律, 我們可以定義復合函數 m = h(g(.)), 它自然的成為 k(arg)的continuation. 在理論上我們有可能利用泛函分析的一些技術實現對于continuation(復合函數)的化簡, 但實踐已經證明這是極為艱難的, 主要是我們的程序不可避免的要涉及到程序與數據的糾纏.
              在引入continuation概念之后, 程序運行的表述是非常簡單的:
                  continuation.proceed();

              針對串行程序,我們可以建立更加精細的運行模型。
                  while(continuation.hasNextStep())
                      continuation.proceedOneStep();
             
              只要以某種方式構造出一種continuation closure(這意味著我們能夠通過單一變量來表示程序未來的運行結構), 我們就有可能在某個層面上以如上方式實現對程序的一種簡潔的描述.
              如果我們的眼界開闊一些, 不拘泥于構造語言級別通用的continuation結構(這需要以抽象的方式定義并保存任意程序的完整運行狀態), 而是考察"對程序未來運行的整體結構進行建模"這一更寬廣的命題, 我們很快就能發現大量對于continuation概念的應用. 例如實現AOP(Aspect Oriented Programming)的interceptor時所經常使用的MethodInvocation對象.
                  class MyInterceptor implements MethodInterceptor{
                      public Object invoke(MethodInvocation invocation){
                          doSomeThingBeforeRawMethodCall();
                          return invocation.proceed();
                      }
                  }
              
               在網絡編程中, 一種常用的設計模式是Observer模式, 即注冊監聽器(listener)來處理接收到的網絡指令. 在一些比較復雜的網絡協議中, 網絡指令之間往往存在一定的關聯, 我們可以通過建立一個龐大的有限自動機來描述所有指令之間的關聯規則, 也可以采用如下方式動態的實現對于監聽器的調整.
                  class ACommandListener{
                      public void onEvent(Event event, FutureListeners futureListeners){
                          handleEvent(event);
                          futureListeners.clear();
                          futureListeners.add("BCommand", new BCommandListener());
                          futureListeners.add("CCommand", new CCommandListener());
                      }
                  }
              這種方式可以看作是對程序未來運行結構的一種動態調整. 實際上沿著這種方式深入下去, 我們甚至可以建立一種完整的動態工作流(workflow)機制.

               最近struts和webwork步cocoon和rife的后塵, 相繼引入了對web continuation的支持, 在后臺程序中實現了對于page flow的完整描述, 這無疑是一些非常有趣的工作. 例如現在我們可以編寫
                  void onRequest(){
                      funcA();
                      Input input = sendPageAndWait("collectionInfoFromUser.jsp");
                      handleInput(input);
                  }
               在調用sendPageAndWait的時候, web框架會保存當前函數調用的continuation, 向用戶返回頁面collectionInfoFromUser.jsp, 等待用戶提交表單之后, web框架重新激活我們所保存的continuation, 繼續執行我們的函數. 這種做法與系統調用和線程調度等機制是非常類似的.  
               有些人認為這種基于continuation的方式可以自然的解決在session中保存并清理變量的問題, 這顯然是一種大材小用的做法, 而且事實上使用一種通用的continuation 實現很有可能在無意中保存了過多的臨時變量, 從而對系統性能造成極大的損害. 有趣的是, 在Mach3.0中對系統線程所作的一項改進即稱為continuation, 其動因恰在于避免保留線程堆棧,希望使用完全無狀態的continuation函數.(參見Uresh Vahalia的經典著作"UNIX Internals" http://www.china-pub.com/computers/common/info.asp?id=12731).
              在傳統的系統調用實現中
              syscall_l(argl)
              {
                  ...
                  thread_block();
                  f2(arg);
                  return;
              }
             
              f2(arg){
                  ...
                  return;
              }

              thread_block()函數會阻塞住當前系統調用過程, 并自動保存所有堆棧變量, 等待內核重新把控制權返回給調用函數. 在使用continuation函數的方式中, 我們需要顯式的存取狀態變量,
              syscall_1(arg1)
              {
                  ...
                  save arg1 and any other state information;
                  thread_block(f2);  // thread_block(void * (contiuationFunc));
                  /* not reached */
              }
             
              f2()
              {
                  restore argl and any other state information;
                  ...
                  thread_syscall_return(status);
              }
              在這種方式中thread_block()并不返回到調用者。線程恢復執行時,內核把控制權傳遞給f2(). 函數thread_syscall_return()用來從系統調用返回到用戶級。"整個過程對用戶是透明的,用戶所看到的只是從系統調用一個同步返回 ". 在Linux系統內核中所使用的bottom_half機制也是基于類似的原理.

          posted @ 2005-12-12 00:58 canonical 閱讀(2323) | 評論 (1)編輯 收藏

              現在很多設計中推崇接口和依賴注入(dependency injection),而不傾向于采用繼承機制來構造程序結構。但很多時候作為一種簡便而廉價的封裝方法,繼承仍然是不可或缺的. 例如與一些Engine打交道的時候,需要實現某些特定的接口. 在osworkflow中, 我們需要實現FunctionProvider接口,
               interface FunctionProvider{
                  void execute(Map transientVars, Map args, PropertySet ps) throws WorkflowException;
               }
              在Quartz中需要實現Job接口
                interface Job{
                    public void execute(JobExecutionContext context) throws JobExecutionException;
                }
              這些接口是一種技術性的要求, 它們表示了代碼生存所依賴的技術環境. 為了屏蔽這種對于外部引擎的依賴, 我們可以簡單的選擇實現一個基類,
              abstract class AbstractFunction implements FunctionProvider,Runnable{
                  Map transientVars;
                  Map args;
                  PropertySet ps;

                  public final void execute(Map transientVars, Map args, PropertySet ps){
                      this.transientVars = transientVars;
                      this.args = args;
                      this.ps = ps;
                      run();
                  }

                  public Object getPersistVar(String name){
                     return ps.getAsActualType(name);
                  }

                  public void setPersistVar(String name, Object value){
                     ps.setAsActualType(name,value);
                  }

                  public void removePersistVar(String name){
                     ps.remove(name);
                  }
              }
              在派生類中我們只要使用getPersistVar等函數就可以回避對于osworkflow特有的PropertySet類的依賴,而只在概念上需要一 個對象的持久化機制.當我們把業務代碼從osworkflow移植到其他工作流引擎的時候, 只需要改變一下基類即可.我們可以在基類中引入更加特殊的假 設,
              abstract AbstractBusinessFunction extends AbstractFunction{
                  public BusinessObject getBusinessObject(){
                      return transientVars.get("businessObject");
                  }

                  public void commonBusinessOp(){ ... }
              }

              AbstractBusinessFunction提供的可以是一個完整的業務對象環境, 我們在派生類中的所有代碼都可以是與業務直接相關的,而與具體 的技術實現無關(例如業務變量是存放在transientVars中還是存放在args中)

              class BusinessFunction extends AbstractBusinessFunction{
                  public void run(){
                      BusinessObject bo = getBusinessObject();
                      bo.methodA();
                      commonBusinessOp();
                  }
              }
              對于我們來說實際有意義的是在派生類中所書寫的代碼,基類僅僅提供一個環境而已.無論我們使用Ioc注入業務變量還是從transientVars中主動 獲取業務變量,都是與我們的業務操作無關的. 實際上在理論上我們希望整個基類都可以是注入的(包括業務變量和業務操作),在泛型編程中這對應于所謂的 policy class.

          posted @ 2005-12-06 22:33 canonical 閱讀(511) | 評論 (0)編輯 收藏

              Six Learning Barriers in End-User Programming Systems http://www.cs.cmu.edu/~ajko/LearningBarriers.pdf

              學習在傳統上被認為是人類特有的活動, 怎樣降低學習的難度一直是理論上非常令人迷惑的問題, 同時它也是人類所面臨的最大的實際困難之一. 在軟件的世界中, 關于學習的研究也是一個非常重要的領域, 例如所謂的可用性(usability)和用戶友好設計,其核心問題就是如何降低用戶學習的難度. 我們目前的了解多半是一些經驗的總結,例如Pane所總結的,
              . Use signaling to highlight important information.
              . Support incremental testing and feedback.
              . Choose an appropriate computational metaphor.
              . Help detect, diagnose, and recover from errors.
              . Provide guiding knowledge through online help.
              . Support recognition rather than recall.

              Andrew J. Ko等人作了更加嚴謹一些的研究, 試圖對Learning Barriers作出一個基本的分類(classification),

          1. Design barriers: I don't know what I want the computer to do, 需要mapping a desired program behavior to an
          abstract description of a solution.
          2. Selection barriers: I think I know what I want the computer to do, but I don't know what to use, 需要mapping a behavior to appropriate search terms for use in help or web search engines, and interpreting the relevance of the results.
          3. Coordination barriers : I think I know what things to use, but I don't know how to make them work together, 需要mapping a desired behavior to a computational pattern that obeys “invisible rules."
          4. Use barriers: I think I know what to use, but I don't know how to use it, 需要mapping a desired behavior to a
          programming interface’s available parameters.
          5. Understatnding barriers: I thougtht I knew how to use this, but it didn't do what I expected, 需要interpreting the external behavior of a program to determine what it accomplished at runtime
          6. Information barriers: I think I know why it didn't do what I expected, but I don't know how to check,需要mapping a hypothesis about a
          program to the environment’s available tools, and interpreting the tool’s feedback.

              其中design, coordination和use的障礙體現了所謂的gulf of execution(the difference between users' intensions and the available actions), understanding的障礙則體現了所謂的gulf of evaluation(the effort of deciding if expectation have been met), 而selection和information的障礙則同時體現了gulf of execution和gulf of evaluation.
              
              關于以上分類的一件有趣的事情是它們之間的相互關系, 經常出現的情況是我們通過一些不正確的假定(invalid assumption)暫時克服了當前的困難,但是很快又遇上了其他不可克服的困難. 例如design barrier經常導向seletion barrier, 而coordination barrier和use barrier經常導向understanding barrier.

              對于如何克服這些學習上的障礙, Andrew J.Ko等人通過Factory的隱喻,提出了一些具體的建議, 但是實用意義不是很大.

          posted @ 2005-12-03 21:49 canonical 閱讀(467) | 評論 (0)編輯 收藏

              在witrix平臺中,異常處理沒有采用java語法支持的checked exception, 也不提倡使用自定義的異常類, 而是定義了少數幾個RuntimeException基類,一般是CommonException(RuntimeException的派生類)。
              在我自己的經驗中,checked exception從未發揮過實質性的作用。checked exception在某種程度上破壞了封裝性原則。我們一般不會在最細的粒度上處理異常,而是在某個統一的模塊節點處進行。如果使用checked exception, 則從最底層的調用到具體異常處理層的整個調用堆棧上的函數都必須明確標記自己不處理該異常,這是完全不必要的負擔。這種細粒度上的負擔往往將程序員引導到 錯誤的方向上去,例如編寫catch塊直接捕獲異常
            try{
               ...
            }catch(MyException e){
               e.printStackTrace();
            }
          在witrix平臺中通過包裝類來將checked exception包裝為RuntimeException, 而且除了在最終代碼處理模塊決不屏蔽異常。
           try{
              ...
           }catch(IOException e){
           throw Exceptions.source(e); // 此時會自動trace異常堆棧及異常消息
           }

          (后來看到Bruce Eckel的文章Does Java need Checked Exception,發現大家在對待checked exception的態度上倒是心有戚戚焉。)

               一般使用自定義的異常類似乎是要將類名作為錯誤返回碼使用,利用java編譯器可以做所謂的強類型檢查,這實在是一種概念上的浪費。畢竟創建并維護一個 java類還是有一定的代價的,特別是錯誤碼經常變動而且數量不菲。實際上,java類庫的設計中也是盡量重用已有的異常類,例如整個jdbc包只拋出 SQLException異常,xml包只拋出SAXException異常。

               使用異常,常見的方法是拋出一個字符串消息,例如 throw new MyException("the object manager does not contains the object :" + objectName);
          這種做法的主要問題是,字符串異常消息無法進行進一步的處理,因而只能直接顯示給最終用戶,這一方面限制了錯誤顯示的格式和方式,另一方面也不利于程序的多語言支持。
               witrix平臺中拋出異常的標準方法為
           throw Exceptions.code(errorCode).param(paramValue).param(paramName,paramValue);
          例如
              throw Exceptions.code("web.CAN_err_missing_object_in_manager").param(objectName).param(objectManager);

          class Exceptions{
              public static CommonException code(String errorCode){
            return new CommonException(code);
           }
          }

          class CommonException extends RuntimeException{
           public CommonException param(Object paramValue){
            ...
            return this;
           }
          }
                Exceptions規定只使用規范格式的錯誤碼而不是任意格式的異常消息。這樣在捕獲異常之后,就可以根據錯誤碼和當時的語言Locale設置來決定最終顯示的消息格式。
                同時CommonException采用流式設計來支持任意數量的自定義參數。這一方面減少了自定義異常類的需求,另一方面也避免了將參數與錯誤碼混合的傾向,即我們就不會傾向于
          使用 throw Exceptions.code("the object manager does not contains the object :" + objectName);

          posted @ 2005-12-02 23:00 canonical 閱讀(1064) | 評論 (0)編輯 收藏

              tag在國內java社區并不算流行,這在很大程度上是因為jsp tag的設計失誤造成的。但在整個開發業界內,tag已經成為一種廣泛應用的技術。微軟的dotNet服務器端極端依賴tag技術,而在瀏覽器端IE的 behaviour, htc也獨立的發展起來。Longhorn的XAML, Firefox的XUL無一例外的依賴于可自定義的tag。java社區的JSF, SiteMesh, Tiles 等等,不可盡數。有些人在前臺通過給html原有元素增加自定義屬性,然后通過javascript去解釋的做法,也是一種element enhance概念的變種。至于FreeMarker這種模板語言,明明類似于tag技術,偏偏不采用xml語法,簡直是自找麻煩。
              這里最關鍵的地方就是自定義tag可以實現抽象層次的提升,是一種類似于函數封裝的機制,從而實現概念的分離和明確化。基于tag可以實現頁面元素的組件 化,加上xml語法的可理解性,表達能力以及無與倫比的集成能力,使得tag技術可以超越VB等組件開發環境(想想集成別人的組件代碼難還是集成別人的 xml描述文件難)。自定義tag提供的抽象能力不僅僅是面向對象的,而且是類似AOP的,這些都極大的輔助了我們的思考和設計。

              cocoon使用管道技術也構造了某種tag機制,但是它的效率很成問題。從數學上說多個處理函數 g, h, k可以通過函數組合(composition)構成新的函數f

              f(data) = g * h * k(data) 

          這是所謂函數式語言強大能力的源泉。cocoon處理的時候從k(data)開始,處理完畢之后調用h, 即函數是從右向左結合的。如果我們保證處理函數滿足左結合律,則g*h*k就可以預編譯為f, 從而解決性能問題,這正是witrix平臺中tpl技術所采用的方案。

          posted @ 2005-12-02 22:59 canonical 閱讀(853) | 評論 (2)編輯 收藏

              AOP作為一種新的分解與合成技術,除了性能問題之外,仍有一些概念層面上的細節問題需要解決。最近Stoerzer的一篇論文AOP Considered harmful因為與Dijkstra的經典論文Go To Statement Considered Harmful  進行對比而引起了廣泛的討論。

              Dijkstra認為程序運行時的指令序列是我們最終想要的東西,而這一序列是運行時根據源代碼的描述在時間軸上展開的(串行結構)。因為人們更容易理解 靜態關系而不是隨時間演化的過程,所以我們應該盡量縮小靜態程序(spread out in text space)和動態過程(spread out in time)的邏輯差距,因而我們需要使它們能夠在一個固定的坐標系統(coordinate system)下形成對應。對于包括條件和分支語句的串行程序,我們只需要源代碼的行號(line number)即可確定一個單一位置。在循環的情況下,我們只需要增加一個額外的循環計數器(loop counter)即可保證可理解性。而對于子例程(procedure)調用,我們可以認為整個調用堆棧(call stack)也構成坐標系統的一部分。goto導致一種非結構化的控制流,因而破壞了這種理解上所必需的獨立坐標系統。例如,如果一個循環中充滿了自由的 goto調轉(可能跳出循環又跳回),我們就很難確定循環變量的值到底是怎么增加的,除非我們在腦海中把源代碼運行一遍!
            仿照Dijkstra的分析,Stoerzer指出AOP Advice雖然類似于procedure,但存在如下重要區別: 1. 與方法調用不同,advice執行位置在基礎源代碼中沒有標識(obliviousness of application), advice有可能在任何位置插入并改變現場變量的值 2. pointcut可能依賴運行時變量值而無法靜態計算得出(non-certainty of application)。
              第一點是由AOP技術的開放性造成的,但正如面向對象中的原則: open to extension but close to modification,我們需要遵循一些原則來避免破壞原有的結構。當然,AOP應用的場景可能確實只存在著某種弱可分性,advice需要深度依賴base code中的一些特性,可能應用類似模板(template)的技術會在一定程度上緩解encapsulation breaking. AOP的開放性造成的更嚴重的問題是pointcut在演化過程中的不確定性。只有在擁有全局知識的情況下才能確認pointcut的結果正是我們所期望的。特別是重構造成方法名改變之后,pointcut無法監測這種變化。當base code修改之后,我們可能沒有意識到缺省影響到很多的aspect, 即完全理解base code變得非常困難。這種困境有一部分的原因是方法名同時作為調用標記和pointcut標記,責任過重造成的。參考一下css的選擇符
             selector { property: value }

                        \_declaration_/
             \___________ rule _________/
          css可以通過選擇符應用,也可以通過指定標簽的class屬性來應用,選擇符所依靠的選擇特征也可以不僅僅是標簽名而包含屬性名等。Java最近增加了與dotNet類似的meta attribute的支持,pointcut所依賴的元數據與方法名分離之后應該可以提高pointcut的穩定性。
            關于第二點,實際上OOP中的Dynamic Dispatch在某種程度上也是需要動態計算決定的,但因為接口具有明確的概念含義(an overriding method should only expect less and provide more, by maintaining all invariants),我們可以在更高的層次上理解代碼,而不需要具體到特定的實現。AOP目前可能是缺乏一些指導性的設計原則。
            相對OOP而言,AOP影響到大范圍內的對象及系統的一些整體特性,因而更加需要工具的支持。

          posted @ 2005-12-02 22:50 canonical 閱讀(673) | 評論 (1)編輯 收藏

          僅列出標題
          共18頁: First 上一頁 9 10 11 12 13 14 15 16 17 下一頁 Last 
          主站蜘蛛池模板: 纳雍县| 农安县| 洛浦县| 浠水县| 南岸区| 荆门市| 台中市| 莱阳市| 梓潼县| 六枝特区| 长岛县| 林周县| 岗巴县| 怀宁县| 奉化市| 宿州市| 凌源市| 搜索| 宾川县| 习水县| 龙口市| 盘锦市| 民乐县| 岐山县| 淮滨县| 隆尧县| 株洲县| 建平县| 凤凰县| 巴彦县| 青铜峡市| 定南县| 灵武市| 九台市| 合山市| 盈江县| 叶城县| 鸡泽县| 呼图壁县| 太湖县| 东海县|