lidwup

          2009年5月25日 #

          用戶是一個絕頂聰明的傻子(轉(zhuǎn),向原創(chuàng)致敬)

           
          從用戶能夠感受得到的角度進行分析(做用戶體驗的原則:堅決做用戶感受得到,絕不做用戶感受不到的——控制成本的有效辦法),把用戶體驗劃分為四步(用戶體驗四步曲):

          第一步視覺體驗。

          視覺體驗是用戶對界面設(shè)計的第一感官體驗。它是完全是用戶幾乎沒有經(jīng)過思考前的一種視覺感受,如何給用戶提供一種近于完美的視覺體驗,需要考慮到用戶看到界面時可能出現(xiàn)的任何一種感受,這就提出UI設(shè)計師需要對目標用戶偏愛的準確了解和把握,精確傳達需要傳達的含義,才會獲得預(yù)期的效果;視覺體驗直接影響到用戶是否停留在此頁面進行一下步的表達理解。注意:任何一種讓用戶感官視覺不適的界面都可能導致用戶產(chǎn)生怨言和流失。

          第二步表達體驗。

          表達體驗是用戶在視覺體驗的基礎(chǔ)上對界面內(nèi)容的理解,界面內(nèi)容可能是文字,也可能是圖案,這些內(nèi)容是引導用戶進行操作的“提示或說明”,是需要用戶進行理解的內(nèi)容,如何給用戶提供一種簡單直白有趣易懂的表達體驗,需要產(chǎn)品設(shè)計人員考慮到用戶對需要理解的內(nèi)容可能出現(xiàn)的任何一種理解進行預(yù)估,消除一些傳達出錯誤信息的內(nèi)容,因為這些內(nèi)容都是單向的表達,沒有解釋和補充的機會來糾正用戶瞬間的理解;表達體驗直接影響到用戶下一步的操作行為是否合理。注意:任何一種讓用戶產(chǎn)生誤解的表達都可能導致用戶產(chǎn)生怨言和流失。

          第三步操作體驗。

          操作體驗是用戶通過對表達內(nèi)容的理解后做出的行為動作的體驗過程。功能邏輯是操作體驗的直接影響因素,如何優(yōu)化用戶的操作過程是產(chǎn)品設(shè)計人員需要思考的核心問題之一,既需要考慮正常的邏輯,又需要考慮非正常的邏輯形式,盡可能考慮到用戶可能出現(xiàn)的任何一種行為方式,才能保證用戶進行正確的愉悅的操作過程;操作體驗直接影響到用戶下一步的內(nèi)容體驗。注意:任何一種繁瑣和復(fù)雜的操作邏輯都可能導致用戶產(chǎn)生怨言和流失。

          第四步結(jié)果體驗。

          結(jié)果體驗是用戶通過操作行為獲得的行為結(jié)果的一種體驗過程。用戶的任何一種操作可能出現(xiàn)的任何一個行為結(jié)果都直接影響到用戶結(jié)果體驗。如果通過用戶的任何行為來預(yù)估用戶的準確需求是產(chǎn)品設(shè)計人員需要思考的核心問題之一,這就要求涉及好每一種用戶行為可能出現(xiàn)的任何一種結(jié)果,并預(yù)測每一種結(jié)果是否能夠滿足用戶的需求,如果不能,如何采用其他方式來滿足用戶的行為需求。注意:任何一種不能滿足用戶需求的操作結(jié)果都可能導致用戶產(chǎn)生怨言和流失。

          總結(jié):用戶是絕頂聰明的天才,因為他可能發(fā)現(xiàn)任何一個細微的不足;用戶是傻子,因為任何一個微不足道的細節(jié)失誤都可能導致用戶出現(xiàn)挫敗感;每一個細節(jié)看起來都微不足道,但是每一個細節(jié)的綜合就會促使整個產(chǎn)品趨于完美,也許用戶只是感動于某個細節(jié),但他接受你一定是認同你的全部!

          posted @ 2009-08-14 10:53 我愛咖啡 閱讀(80) | 評論 (0)編輯 收藏

          用Windows內(nèi)置功能輕松查看端口使用情況 (轉(zhuǎn))

          用Windows內(nèi)置功能輕松查看端口使用情況   [閱讀: 564]

          在局域網(wǎng)使用中,我們常常會發(fā)現(xiàn)系統(tǒng)中開放了一些莫名其妙的端口,這就給系統(tǒng)的安全帶來了一些隱患。為了讓端口的使用盡在掌握之中,一些朋友就會使用第三方工具來進行檢查出使用端口的特定程序究竟是誰,但實際上我們完全不必這樣興師動眾,因為Windows已經(jīng)內(nèi)置了這個功能,下面讓我們來學習一下吧!
            查看端口開放情況
           稍有經(jīng)驗的網(wǎng)管就會知道使用Netstat命令可以查看系統(tǒng)當前開放的端口有哪些,但你知道嗎?如果在使用Netstat命令的同時加上參數(shù)“-o”的話,就可以讓我們進一步知曉端口的關(guān)聯(lián)進程標識符 (PID)了,也就是說這個PID可用來確定哪個進程(程序)在使用特定的端口。例如,現(xiàn)在使用“netstat-ano”命令后可以發(fā)現(xiàn)端口3026、 3030、3728在開放(如圖1所示)。
            從上圖中并不能直接看出這三個端口的關(guān)聯(lián)進程是誰,但我們卻可以通過PID信息知道是628這個進程在使用這三個端口。

          1 查看端口
            激活進程PID
           既然知道系統(tǒng)中有個PID為628的進程,那么就來看看它究竟是誰。大家都知道,查看系統(tǒng)進程可以使用同時按“Ctrl+Alt+Delete”組合鍵的方法,在打開的“Windows任務(wù)管理器”的“進程”選項卡中進行。但默認狀態(tài)下,“進程”選項卡中是沒有PID這一項存在的,這樣我們就無法知道進程相對應(yīng)的PID是什么了。因此,要首先激活進程對應(yīng)的PID項顯示狀態(tài)才行。方法如下:
            單擊“Windows任務(wù)管理器”窗口中“查看”下的“選擇列”菜單項,然后單擊選中“PID”(進程標識符)復(fù)選框(如圖2所示)。
          2 選擇進程PID
            稍后在“Windows任務(wù)管理器”窗口“進程”選項卡的列表中就會發(fā)現(xiàn)多出了PID這一項,接下來只需按從大到小的順序查找到PID為628的進程后,就可以發(fā)現(xiàn)“幕后程序”究竟是誰了(如圖3所示)。

          3 Windows任務(wù)管理器
            通過上圖中顯示信息可以發(fā)現(xiàn),原來使用端口3026、3030、3728的關(guān)聯(lián)PID是628,而使用PID為628的應(yīng)用程序是“MSN Messenger”!!瞧,我們已經(jīng)輕易地為端口找到的“另一半”了!

          posted @ 2009-07-30 11:06 我愛咖啡 閱讀(160) | 評論 (0)編輯 收藏

          【轉(zhuǎn)】淺談Hibernate的flush機制

          隨著Hibernate在Java開發(fā)中的廣泛應(yīng)用,我們在使用Hibernate進行對象持久化操作中也遇到了各種各樣的問題。這些問題往往都是我們對Hibernate缺乏了解所致,這里我講個我從前遇到的問題及一些想法,希望能給大家一點借鑒。

                  這是在一次事務(wù)提交時遇到的異常。
                  an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
          net.sf.hibernate.AssertionFailure: possible nonthreadsafe access to session
          注:非possible non-threadsafe access to the session (那是另外的錯誤,類似但不一樣)

                  這個異常應(yīng)該很多的朋友都遇到過,原因可能各不相同。但所有的異常都應(yīng)該是在flush或者事務(wù)提交的過程中發(fā)生的。這一般由我們在事務(wù)開始至事務(wù)提交的過程中進行了不正確的操作導致,也會在多線程同時操作一個Session時發(fā)生,這里我們僅僅討論單線程的情況,多線程除了線程同步外基本與此相同。

                  至于具體是什么樣的錯誤操作那?我給大家看一個例子(假設(shè)Hibernate配置正確,為保持代碼簡潔,不引入包及處理任何異常)
            

          SessionFactory sf = new Configuration().configure().buildSessionFactory() ;
          Session s = sf.openSession();
          Cat cat = new Cat();

          Transaction tran = s.beginTransaction(); (1)
          s.save(cat); (2)(此處同樣可以為update delete)
          s.evict(cat); (3)
          tran.commit(); (4)
          s.close();(5)


                  這就是引起此異常的典型錯誤。我當時就遇到了這個異常,檢查代碼時根本沒感覺到這段代碼出了問題,想當然的認為在Session上開始一個事務(wù),通過Session將對象存入數(shù)據(jù)庫,再將這個對象從Session上拆離,提交事務(wù),這是一個很正常的流程。如果這里正常的話,那問題一定在別處。

                   問題恰恰就在這里,我的想法也許沒有錯,但是一個錯誤的論據(jù)所引出的觀點永遠都不可能是正確的。因為我一直以為直接在對數(shù)據(jù)庫進行操作,忘記了在我與數(shù)據(jù)庫之間隔了一個Hibernate,Hibernate在為我們提供持久化服務(wù)的同時,也改變了我們對數(shù)據(jù)庫的操作方式,這種方式與我們直接的數(shù)據(jù)庫操作有著很多的不同,正因為我們對這種方式?jīng)]有一個大致的了解造成了我們的應(yīng)用并未得到預(yù)先設(shè)想的結(jié)果。

          那Hibernate的持久化機制到底有什么不同那?簡單的說,Hibernate在數(shù)據(jù)庫層之上實現(xiàn)了一個緩存區(qū),當應(yīng)用save或者update一個對象時,Hibernate并未將這個對象實際的寫入數(shù)據(jù)庫中,而僅僅是在緩存中根據(jù)應(yīng)用的行為做了登記,在真正需要將緩存中的數(shù)據(jù)flush入數(shù)據(jù)庫時才執(zhí)行先前登記的所有行為。

          在實際執(zhí)行的過程中,每個Session是通過幾個映射和集合來維護所有與該Session建立了關(guān)聯(lián)的對象以及應(yīng)用對這些對象所進行的操作的,與我們這次討論有關(guān)的有entityEntries(與Session相關(guān)聯(lián)的對象的映射)、insertions(所有的插入操作集合)、deletions(刪除操作集合)、updates(更新操作集合)。下面我就開始解釋在最開始的例子中,Hibernate到底是怎樣運作的。
          (1)生成一個事務(wù)的對象,并標記當前的Session處于事務(wù)狀態(tài)(注:此時并未啟動數(shù)據(jù)庫級事務(wù))。
          (2)應(yīng)用使用s.save保存cat對象,這個時候Session將cat這個對象放入entityEntries,用來標記cat已經(jīng)和當前的會話建立了關(guān)聯(lián),由于應(yīng)用對cat做了保存的操作,Session還要在insertions中登記應(yīng)用的這個插入行為(行為包括:對象引用、對象id、Session、持久化處理類)。
          (3)s.evict(cat)將cat對象從s會話中拆離,這時s會從entityEntries中將cat這個對象移出。
          (4)事務(wù)提交,需要將所有緩存flush入數(shù)據(jù)庫,Session啟動一個事務(wù),并按照insert,update,……,delete的順序提交所有之前登記的操作(注意:所有insert執(zhí)行完畢后才會執(zhí)行update,這里的特殊處理也可能會將你的程序搞得一團糟,如需要控制操作的執(zhí)行順序,要善于使用flush),現(xiàn)在cat不在entityEntries中,但在執(zhí)行insert的行為時只需要訪問insertions就足夠了,所以此時不會有任何的異常。異常出現(xiàn)在插入后通知Session該對象已經(jīng)插入完畢這個步驟上,這個步驟中需要將entityEntries中cat的existsInDatabase標志置為true,由于cat并不存在于entityEntries中,此時Hibernate就認為insertions和entityEntries可能因為線程安全的問題產(chǎn)生了不同步(也不知道Hibernate的開發(fā)者是否考慮到例子中的處理方式,如果沒有的話,這也許算是一個bug吧),于是一個net.sf.hibernate.AssertionFailure就被拋出,程序終止。

          我想現(xiàn)在大家應(yīng)該明白例子中的程序到底哪里有問題了吧,我們的錯誤的認為s.save會立即的執(zhí)行,而將cat對象過早的與Session拆離,造成了Session的insertions和entityEntries中內(nèi)容的不同步。所以我們在做此類操作時一定要清楚Hibernate什么時候會將數(shù)據(jù)flush入數(shù)據(jù)庫,在未flush之前不要將已進行操作的對象從Session上拆離。

          對于這個錯誤的解決方法是,我們可以在(2)和(3)之間插入一個s.flush()強制Session將緩存中的數(shù)據(jù)flush入數(shù)據(jù)庫(此時Hibernate會提前啟動事務(wù),將(2)中的save登記的insert語句登記在數(shù)據(jù)庫事務(wù)中,并將所有操作集合清空),這樣在(4)事務(wù)提交時insertions集合就已經(jīng)是空的了,即使我們拆離了cat也不會有任何的異常了。
          前面簡單的介紹了一下Hibernate的flush機制和對我們程序可能帶來的影響以及相應(yīng)的解決方法,Hibernate的緩存機制還會在其他的方面給我們的程序帶來一些意想不到的影響。看下面的例子:
            

          (name為cat表的主鍵)
          Cat cat = new Cat();
          cat.setName(“tom”);
          s.save(cat);

          cat.setName(“mary”);
          s.update(cat);(6)

          Cat littleCat = new Cat();
          littleCat.setName(“tom”);
          s.save(littleCat);

          s.flush();


          這個例子看起來有什么問題?估計不了解Hibernate緩存機制的人多半會說沒有問題,但它也一樣不能按照我們的思路正常運行,在flush過程中會產(chǎn)生主鍵沖突,可能你想問:“在save(littleCat)之前不是已經(jīng)更改cat.name并已經(jīng)更新了么?為什么還會發(fā)生主鍵沖突那?”這里的原因就是我在解釋第一個例子時所提到的緩存flush順序的問題,Hibernate按照insert,update,……,delete的順序提交所有登記的操作,所以你的s.update(cat)雖然在程序中出現(xiàn)在s.save(littleCat)之前,但是在flush的過程中,所有的save都將在update之前執(zhí)行,這就造成了主鍵沖突的發(fā)生。

          這個例子中的更改方法一樣是在(6)之后加入s.flush()強制Session在保存littleCat之前更新cat的name。這樣在第二次flush時就只會執(zhí)行s.save(littleCat)這次登記的動作,這樣就不會出現(xiàn)主鍵沖突的狀況。

          再看一個例子(很奇怪的例子,但是能夠說明問題)

          Cat cat = new Cat();
          cat.setName(“tom”);
          s.save(cat); (7)
          s.delete(cat);(8)

          cat.id=null;(9)
          s.save(cat);(10)
          s.flush();


          這個例子在運行時會產(chǎn)生異常net.sf.hibernate.HibernateException: identifier of an instance of Cat altered from 8b818e920a86f038010a86f03a9d0001 to null

          這里例子也是有關(guān)于緩存的問題,但是原因稍有不同:
          (7)和(2)的處理相同。
          (8)Session會在deletions中登記這個刪除動作,同時更新entityEntries中該對象的登記狀態(tài)為DELETED。
          (9)Cat類的標識符字段為id,將其置為null便于重新分配id并保存進數(shù)據(jù)庫。
          (10)此時Session會首先在entityEntries查找cat對象是否曾經(jīng)與Session做過關(guān)聯(lián),因為cat只改變了屬性值,引用并未改變,所以會取得狀態(tài)為DELETED的那個登記對象。由于第二次保存的對象已經(jīng)在當前Session中刪除,save會強制Session將緩存flush才會繼續(xù),flush的過程中首先要執(zhí)行最開始的save動作,在這個save中檢查了cat這個對象的id是否與原來執(zhí)行動作時的id相同。不幸的是,此時cat的id被賦為null,異常被拋出,程序終止(此處要注意,我們在以后的開發(fā)過程盡量不要在flush之前改變已經(jīng)進行了操作的對象的id)。

          這個例子中的錯誤也是由于緩存的延時更新造成的(當然,與不正規(guī)的使用Hibernate也有關(guān)系),處理方法有兩種:
          1、在(8)之后flush,這樣就可以保證(10)處save將cat作為一個全新的對象進行保存。
          2、刪除(9),這樣第二次save所引起的強制flush可以正常的執(zhí)行,在數(shù)據(jù)庫中插入cat對象后將其刪除,然后繼續(xù)第二次save重新插入cat對象,此時cat的id仍與從前一致。

          這兩種方法可以根據(jù)不同的需要來使用,呵呵,總覺得好像是很不正規(guī)的方式來解決問題,但是問題本身也不夠正規(guī),只希望能夠在應(yīng)用開發(fā)中給大家一些幫助,不對的地方也希望各位給與指正。

            總的來說,由于Hibernate的flush處理機制,我們在一些復(fù)雜的對象更新和保存的過程中就要考慮數(shù)據(jù)庫操作順序的改變以及延時flush是否對程序的結(jié)果有影響。如果確實存在著影響,那就可以在需要保持這種操作順序的位置加入flush強制Hibernate將緩存中記錄的操作flush入數(shù)據(jù)庫,這樣看起來也許不太美觀,但很有效。

          posted @ 2009-07-23 17:41 我愛咖啡 閱讀(112) | 評論 (0)編輯 收藏

          java中的排序1:comparable和comparator(轉(zhuǎn))

          分三種情況:

          簡單類型排序。

          內(nèi)部對象實現(xiàn)comparable

          外部對象實現(xiàn)comparator

          1、簡單類型的排序

          簡單類型不外是byte, char, short, int, long, float, double等數(shù)據(jù)類型,這些類型不能放在聚集中,只能使用數(shù)組。java.util.Arrays方法提供了對這些類型的sort方法(實際上還有很多其他有用的方法),下面是對一個簡單的int數(shù)組排序:

                      int[] arr = {2, 3, 1,10,7,4};

                        System.out.print("before sort: ");

                        for (int i = 0; i< arr.length; i++)

                               System.out.print(arr[i] + " ");

                        System.out.println();            

                        Arrays.sort(arr);

                        System.out.print("after sort: ");

                        for (int i = 0; i< arr.length; i++)

                               System.out.print(arr[i] + " ");

                        System.out.println();      

          輸出結(jié)果:

          before sort: 2 3 1 10 7 4

          after sort: 1 2 3 4 7 10

          我們看到排序結(jié)果是按照升序排列的,下面的排序都是如此。

          Comparable & Comparator 都是用來實現(xiàn)集合中的排序的,只是Comparable是在集合內(nèi)部定義的方法實現(xiàn)的排序,Comparator是在集合外部實現(xiàn)的排序,所以,如想實現(xiàn)排序,就需要在集合外定義Comparator接口的方法或在集合內(nèi)實現(xiàn)Comparable接口的方法。

          2、內(nèi)部對象實現(xiàn)comparable

          案例:

          class Programmer implements Comparable{

                 private String name;

                 private String language;

                 private double pay;

                

                 public Programmer(String name, String language, double pay) {

                        this.name = name;

                        this.language = language;

                        this.pay = pay;

                 }

                 public int compareTo(Object o) {

                        Programmer other = (Programmer)o;

                        return (int)pay - (int)other.pay;

                 }

                 public String toString(){

                        return "{name: " + name + ", language: " + language + ", money: " + pay + "}";

                 }

          }

          對其進行排序:

                        ArrayList list = new ArrayList();

                        list.add(new Programmer("張三", "C", 12000));

                        list.add(new Programmer("李四", "Java", 200));

                        list.add(new Programmer("王五", "C++", 5000));

                        list.add(new Programmer("錢六", "VB", 3000));

                        System.out.println("before sort: " + list);

                        Collections.sort(list);

                        System.out.println("after sort: " + list);

          3、外部對象實現(xiàn)comparator

          案例:

          import java.util.Arrays;
          import java.util.Comparator;

          public class SampleComparator implements Comparator {

             public int compare(Object o1, Object o2) {
               return toInt(o1) - toInt(o2);
             }

             private int toInt(Object o) {
               String str = (String) o;
               str = str.replaceAll("一
          ", "1");
               str = str.replaceAll("二
          ", "2");
               str = str.replaceAll("三
          ", "3");
               //
               return Integer.parseInt(str);
             }

             /**
              *
          測試方法
              */
             public static void main(String[] args) {
               String[] array = new String[] { "
          一二", "三", "" };
               Arrays.sort(array, new SampleComparator());
               for (int i = 0; i < array.length; i++) {
                 System.out.println(array[i]);
               }
             }

          posted @ 2009-07-01 15:30 我愛咖啡 閱讀(290) | 評論 (0)編輯 收藏

          oracle 支持的遞歸查詢語句

          oracle支持的遞歸查詢語句:Connect By Prior

          如:select * from module where parentid>-1  Start With ID =621 Connect By Prior ID = ParentId

          posted @ 2009-05-25 02:12 我愛咖啡| 編輯 收藏

          主站蜘蛛池模板: 竹溪县| 涟源市| 新龙县| 彰化市| 凤翔县| 密山市| 特克斯县| 揭西县| 台江县| 阿合奇县| 青田县| 济阳县| 永福县| 娄底市| 全椒县| 崇仁县| 齐河县| 镇沅| 吴堡县| 盐源县| 漠河县| 定兴县| 新疆| 澄迈县| 清新县| 衢州市| 巴中市| 衡水市| 郯城县| 丰台区| 鄄城县| 白河县| 宁都县| 蓬安县| 广河县| 正镶白旗| 青冈县| 桐乡市| 蒙城县| 镇平县| 平乐县|