2007/12/31

JS OO programming筆記

原本我們都是使用JS framework的包裝,但在這兩篇說明javascript對OO的支援方式的文章中,我們可以看到最原始的javascript

Part I: Inheritance http://www.webreference.com/js/column79/

Part II: Methods http://www.webreference.com/js/column80/

OO的基本特性 inheritance, encapsulation, polymorphism

提供 objects, prototypes, implicit inheritance,沒有classes, instances

prototyping 是瞭解inheritance的核心概念

以js定義function,在function中定義methods

保護 private field data element

context, scope, context switching

***********************

OO的特色 encapsulation 封裝(data and methods), inheritance 繼承, and polymorphism 多型, class, superclass, subclass, override

js 不直接支援inheritance,有兩種方法

1. 用function來實作

function superClass() {

this.bye = superBye;


this.hello = superHello;


}

function subClass() {

// 這兩行用來定義superclass


this.inheritFrom = superClass;


this.inheritFrom();


// override bye這個 method


this.bye = subBye;


}

2. 用prototype

function superClass() {

this.bye = superBye;


this.hello = superHello;


}

function subClass() {

this.bye = subBye;


}

// 這裡用prototype指定 superclass

subClass.prototype = new superClass;

************

object1.prototype.isPrototypeOf(0bject2); 判斷object2裡面是否有object1為prototype

netscape: 以 __proto__ 紀錄 prototype物件

************

用__proto__來模擬 instanceOf

//for NS only

function instanceOf(object, constructorFunction) {

while (object != null) {

if (object == constructorFunction.prototype)

{return true}

object = object.__proto__;

}

return false;

}

************

Object object supports the constructor property

************

js 支援三種object: native, host, and user-defined

native: js 語言支援的物件,包含 Object, Math, and Number

host: browser在載入頁面時產生的物件,包含 document, window, and frames

user-defined: 自訂物件,case-sensitive

The Object object is the base object for all native and user-defined objects. All objects inherit from this superclass.

Object的內容

1. property: constructor

2. methods: isPrototypeOf, hasOwnProperty, toLocaleString, toSource, toString, valueOf

propertyIsEnumerable

*****************

列印 物件property

for (property in ken) {

alert(property);


}

*****************

產生物件

function Employee() {

var registerA = "Initial Value";

function setRegisterA(param) {

registerA = param;

}

this.setRegisterA = setRegisterA;

this.dept = "HR";

this.manager = "John Johnson";

}

***************

定義function的三種方法

1.

function first(param) {

alert(param + " method to define a function");


}

2.

second = function(param)

{alert(param + " method to define a function")};


3.

third = new Function("param",

"alert(param + ' method to define a function')");


****************

Functions in javascript can modify global variables from the outer context.

定義method的時候,就會希望是使用private data member,第三種方法(用Function)會產生錯誤,

所以只能用第一與第二種建立function的方法

function myGlobal() {

var global = 5;


function first(param) {


global += 10;


alert(param + " method shows global to be " + global);


}


this.first = first;


this.second = function(param) {global += 10;


alert(param + " method shows global to be " + global)};


this.third = new Function("param", " global += 10;


alert(param + ' method shows global to be ' + global)");


}

var x = new myGlobal();

***********************

定義Context

有三種context: Global code, Eval code, and Function code

當browser進入一個新的context的時候,就會建立新物件(variable object),保存新context的變數與function,當browser跳出此context的時候,variable object就會被刪除

global code: 在functions外面執行的codes

eval code: 是透過eval function執行的code

function code: 是在function定義中的code

當browser切換context的時候,就會決定新的scope,進而決定可以存取哪些variables, objects與functions

************

判斷scope

global code的scope包含了兩種物件: global object and variable object

global object就是window這個variable物件,也可以用this來代表window,ex: window.location也可以寫成this.location

eval code的scope等同於calling code的scope,包含global and variable object

function code的scope包含

(1) calling code's execution context的variable objects,包含該function被呼叫時,所定義的所有的variables

(2) outer function的variable object

(3) global object,也就是window

(4) arguments object,包含了所有參數

通常this就代表window,但在object method中,this就是這個method所屬的object

ex:

function ObjectConstructor() {

// 這裡的this就不是window


alert(this.location);


}

// 物件

function createObj() {

// method


var newObject = new ObjectConstructor();


}

***********

強制建立object 的方法

function Employee(a) {

if (!(this instanceof Employee)) return new Employee(a); // 如果這行沒寫的話就會error


this.name = a;


}

function init(){

John = Employee("Johnson"); // 沒有寫 new


alert(John.name);


}

*********

在subclasses之間共用private data

function Shape() {

var area = 50;


this.setArea = function(a) {area = a;};


this.getArea = function() {return area;};


}

function Square() {

}

Square.prototype = new Shape();

var shape1 = new Square();

var shape2 = new Square();

shape1.setArea(100);

****************

保護private data的兩種方法

1. 用call()

function Shape() {

var area = 50;


this.setArea = function(a) {area = a;};


this.getArea = function() {return area;};


}

function SquareB() {

Shape.call(this);


}

var shape1B = new SquareB();

var shape2B = new SquareB();

shape1B.setArea(100);

//shape2B's 的area變數就不會被影響還是50

2. call a constructor from within a constructor

function Shape() {

var area = 50;


this.setArea = function(a) {area = a;};


this.getArea = function() {return area;};


}

function SquareA() {

this.Shape = Shape;


this.Shape();


}

var shape1A = new SquareA();

var shape2A = new SquareA();

shape1A.setArea(100);

//shape2B's 的area變數就不會被影響還是50

2007/12/26

轉轉球迷宮 130關 100% 紀念

這是個好玩的遊戲,需要穩定的雙手控制Wii Remote(單手拿比較不穩),但偶爾也能耍些小技巧,例如第一部份最難的第E關,要在那麼小的路上走滑水道,簡直是天方夜譚,就直接想辦法讓球從某一個中繼點跳到另一個中繼點,事實上也要感謝遊戲設計者沒有那麼刁難我們,因為可以一直接關,比較難的關卡設計了中繼點。

大多數特殊的球都沒有用,只要用一開始的彈珠就行了,只有為了要拿25個金盃的時候,有時候就會用排球(重量輕、速度快、但會彈跳)、或是土星(重量重、穩定、速度快)。

前50關

前50關的鏡像關

後15關

後15關的鏡像關

所有的球

2007/12/18

Ghost Squad Level 99 紀念

前一陣子唯一的娛樂,就是每天晚上拿槍掃射,這是Level 99的紀念。其實這個遊戲也沒什麼,就是一直開槍跟接關而已,反正也是消磨時間。

Level 99

元帥的Gold服裝,和一把貫穿能力高,100發連發的機槍


三個關卡都是Lv16

熊貓裝的正面

熊貓裝頭部


熊貓裝背後的那隻貓

2007/11/25

得獎的童書比較不會踩到地雷

有一陣子沒有大量地買書了,最近用網路購物買了不少,有些中文書就到「販讀書店」去訂。

在網路上購書,因為不能看到內容(事實上有些書店會把書本裝在塑膠袋裡,也是不能試讀的),除非買的書有口碑、有名氣,就不用擔心作品的品質如何,可以勇敢地刷下去,這個時候,參考是不是得獎作品這個指標,就蠻好用的,網路上也有人列出百大繪本或是圖書的列表,這也是個參考的指標。

在這個網站http://store.pchome.com.tw/mackids/S037251.htm一次買了六本得獎的書(因為要滿一千元才不用運費><):

‧ Click Clack Moo
‧ 〈凱迪克銀牌獎〉Snow
‧ There was an Old Lady Who Swallowed a Fly
‧ ﹝凱迪克大獎﹞YO! YES!
‧ Don't Let the Pigeon Drive the Bus!

‧ 〈James Marshall〉Goldilocks and the Three Bears

六本書才沒幾天就送到家裏,真的很方便,小朋友也迫不及待地,一下子就聽了五個故事,第六個故事字太多了,小朋友的注意力也不集中了,馬上停住,休息囉。

2007/11/5

去跟媽媽講,爸爸喝醉了

2007年11月3日,爸爸第一次喝醉了,奇怪的是,爸爸是很「清醒地」喝醉了。

晚上飯後,很久沒有喝酒的爸爸,開了一瓶750ml 14.5%的白酒,配上多力多滋,一邊吃一邊喝,我也很興奮地跟著爸爸一起吃零食。

爸爸才喝到1/3,就跟媽媽說,他的頭有點暈,爸爸還是一直喝,我們還一邊看著電視裡的人,在比賽魔術表演。接下來媽媽可能因為白天工作,很累地睡在沙發上,我也不大想看電視,爸爸已經喝了2/3,還沒停下來。

我跟爸爸說要玩心臟病,我們坐在和室地板上玩撲克牌,爸爸還是很清醒地,跟我一起玩牌,他還能控制自己什麼時候要放水,不過我發現,爸爸去上廁所的時候,扶著牆壁搖搖晃晃地。

時間到了,我該洗澡去,爸爸已經把酒喝光了,爸爸要我準備洗澡,他搖搖晃晃地到了房間拿衣服,跟平常一樣,幫我洗澡,還問我要不要講故事,我當然說好,接著,我選了兩本書在床上聽爸爸講故事。

爸爸很清醒地,幫我講了兩本故事書,「貓咪數不完」跟「等我一分鐘」,爸爸一樣搖搖晃晃地走到他們房間,還沒到浴室的時候,爸爸就吐了,地上多了兩攤東西,好臭,爸爸跟我說「去跟媽媽講,爸爸喝醉了」。

媽媽醒過來了,幫我刷牙,我要回房間的時候,發現爸爸自己已經用抹布,把地板清理乾淨,只聽到他跟媽媽說,我喝醉了,我自己整理好了,要睡覺了。

隔天中午,爸爸說他還有一點點頭暈,這應該可說是宿醉吧,下次別再喝酒了,你已經不會喝酒了,爸爸。

2007/11/4

我的資優班 by 游森棚

這本書由建中數理資優班的導師記錄一些帶班所發生的人事物,頂尖的學生也得要有優秀的老師,才能夠壓得著他們,沒兩把刷子的老師根本沒辦法成為一個資優班級的龍頭。

家長常常期待小朋友是個資優兒童,因為大家普遍認為,資優班所取得的資源與師資,都是頂尖的,這樣的期望下,也就會發生真假資優的問題,如果是真資優,小孩在家長的培養下,能取得該有的成績,假資優就麻煩了。

我小時候也待過所謂的資優班,但到中學時期,我就是一般人了,也許我自己就是屬於假資優那一群人的其中之一,像高等微積分、微分方程等等天書,我到了大學也是一竅不通,書本裡描述的資優學生,他們能在高中的時候,就自修學習到研究所的程度,真的是與眾不同。

好的老師,能找到最適當的時機,給予最適合的機會教育,書本中提到的例子,像是合唱比賽、寫週記、留一手、打電動、英文話劇等等一堆實際的例子,都能看到老師在適當的時候,給了學生最好的註解。

書本提到的都是一些正面有趣的故事,正如作者自己所說得,他隱惡揚善,把一些不好的故事都略去不談,這種作法或許就是支撐著他一直快樂地走下去的方法,但人生最重要的經驗是失敗而不是成功,每一個人都得累積許多的失敗,才會獲得一些勝利的果實,如果有一個人一直覺得自己沒有失敗或作過錯事,那麼或許在將來的某一天,他可能會因為一點點的挫折,而萬劫不復。

這就像從小到大都是資優的學生一樣,到了建中資優班,他才知道自己是井底之蛙,還有一堆怪物在他班上,第一名註定只會退步,而最後一名才有充足的進步空間。

我常在想,媒體大都刊登著成功的故事,卻不會講失敗的,或許就是在鼓勵大家努力向上,但失敗的經驗才是重要的,這種經驗卻是最沒有辦法傳播給別人的一種資訊。就像寫程式一樣,寫錯的程式碼會一直被修正,直到沒有錯誤,但這其中的變化過程,只有寫程式的人自己知道,這也是沒有辦法傳播出去的一種個人經驗。

2007/10/16

Aachi And Ssipak 阿阿奇與西西帕克

這是一部韓國的動畫電影,畫面的處理加上科幻故事的背景,我覺得這是一部值得看看的作品。在滿地都是日本動畫的時代,看看韓國人的動畫也不錯。很明顯地,韓國在電玩工業的帶動下,內容與影視娛樂產業也成功地打出國際市場。故事的想法,跟「能源危機」有關,也帶有「美麗新世界」的影子。畫面精緻這不用多說,大家自己去看看就知道了。

故事的背景,在故事一開始就陳述出來:世界上所有形態的能源都用完了,人們通過糞便提煉新的能源,建造了一個新的城市,不久後城市的領導人,宣佈了兩項法規,以生產和控制新能源。首先在每個市民的肛門,安裝ID芯片,以監控他們的排便程度,其次,給排便的市民一條吃了會上癮的"果汁棒"作為回報,不久後糞便的數量就急速上升。

但是由於果汁棒,城裡到處都是癮君子,非法販賣果汁棒變得很普遍,而果汁棒的副作用,使得啞巴小個子變種人出現,這些變種人不能拉屎,也因為小弟弟縮得跟花生那樣不能生孩子,所以他們就被趕出那個城市,這些變種人組織了搶劫果汁棒的幫會,最後成為我們所知的"尿布幫"。

故事設定很合理地,讓城市的政府軍,依靠怪博士的幫忙,製造出一個變種人殺手「狗鼻子」,他存在的唯一目的,就是要把尿布幫趕盡殺絕。尿布幫的老大,因為跟政府軍搶奪果汁棒遇到狗鼻子而失敗,只好想辦法,到第四區準備從黑幫的手裡搶奪他們地下交易的貨源。

尿布幫的老大在黑幫的地盤遇到了吉米,他提供了一個驚人的主意,要是把一堆人的ID晶片塞到一個很會拉屎的人屁股裡,那麼只要拉了一次,就會一次拿到上百個果汁棒。他們找上了「美麗」,阿阿奇與西西帕克這兩個阿飛,救出「美麗」後,發現這個秘密,因此賺了不少錢。

尿布幫的老大跟政府軍,都想趕快找到「美麗」,而發生了追逐戰,「美麗」被尿布幫抓走了,他們又找上了怪博士幫忙製造軍隊,於是又是一場大混戰,最後是阿阿奇與西西帕克跟著「美麗」一起富有地浪跡天涯。

整個故事情節雖然荒誕,但是從全球「能源危機」所開始的想法,跟「駭客任務」裡所設定的,以人體來當作能源的想法有一點像,糞便供應能源的城市自給自足,就像是一個「美麗新世界」,城市的主人為了控制能源的來源,保障能源不會斷絕,就創造了類似「美麗新世界」中使用的「蘇麻」,來麻醉居民的神經。

為了抗拒城市的監控,產生了對抗的勢力"尿布幫",三方的勢力:政府軍、黑幫、尿布幫,中間夾著主角阿阿奇、西西帕克與美麗,他們之間的關係,就構成了這部電影,表面上看來,政府軍、尿布幫是壞人,但其實是沒有絕對的對錯。

政府軍為了居民的安危,奮力對抗反抗勢力尿布幫,為了保障能源供應,以ID晶片與果汁棒控制居民的行動,看起來有立意良善的初衷,但卻使用了卑劣的手段。尿布幫為了爭取自己的權利,致力搶奪果汁棒,成為這個城市的毒瘤,但他們也不願意這樣,完全是因為果汁棒產生的副作用,他們有不得不做的理由,但是選擇了使用最激烈的反抗手段。

尿布幫的成員似乎是殺也殺不完,而且全部的人,似乎只有老大一個人有腦袋,其他都是「我為人人、人人為我」的好榜樣,爭先恐後地,要為組織效力,尿布幫是個最具有向心力的組織。

「美麗」被塞了多個ID晶片,是故事中段的一個小高潮,這的確是對付政府軍的一個好方法,至於究竟合不合理,為甚麼「美麗」跑了,尿布幫老大不再另外重新抓個新的屎人,反而執意要找美麗,這麼厲害的「狗鼻子」為甚麼會只有一隻,不像怪博士最後幫忙尿布幫做的怪物,可以有那麼多隻,這點缺陷的小細節就不重要了。

韓國動畫 - Aachi And Ssipak
Aachi & Ssipak
Aachi And Ssipak『阿阿奇與西西帕克』

2007/10/15

惡童 Tekkonkinkreet

改編自「松本大洋」的漫畫,二宮和也、蒼井優聲演,Michael Arias導演。

惡童 (Tekkonkinkreet)這篇文章用了幾個字來描述「惡童」這部令人難以理解的作品:黑與白、朝與夜、善與惡,被稱為「貓」的「黑」和「白」是兩個住在「地獄之街」寶町的小孩,他們兩個在故事一開始,就好好地教訓了「朝」與「夜」,這個象徵比喻,或許是要說明,「黑」和「白」在橫行在白天與夜晚的寶町,無人不知無人不曉,所有人都明白,在寶町就得好好地注意「黑」這個不定時炸彈。

做了十多年黑幫的木村,因為成家立室而要過新的人生,為了保護家人,他只能遵照老大「蛇」的指示,解決掉他的老爹「耗子」鈴木桑,黑幫與「蛇」的「兒童城」計畫,一步一步地改變了寶町。大家都因為這種改變,而有不同的適應方式。「蛇」帶著數個講「鳥語」的怪人幫他做事,主要是要解決掉「貓」,也就是「黑」,「黑」為了生存下去,選擇跟「白」分開,讓他接受警察的保護,自己一個人嘗試要解決這個難題。

「黑」和「白」用完全不同的人生態度,面對改變中的寶町,成天做夢的「白」,沒有被外界影響自己的信念,嗜血的「黑」則是為了生存及目標而改變自己的理念,在光與暗之間彷徨,引出了另一個「黑」,也就是完全黑暗的「鼬」。但是小白與小黑是一個互相補足的平衡,互相補完兩者心理上的不足,亦即是小白所說的「我擁有小黑所欠缺的螺絲,他擁有我欠缺的螺絲」。很幸運地,「黑」擺脫了「鼬」的糾纏,又回到一起生活互相幫助,那麼純淨,衝突與平衡的「黑」與「白」。

2007/10/8

秒速5公分 - 新海誠

對新海誠認識的上一步作品是「雲之彼端,約定的地方」,「秒速5公分」同樣也是一部音樂與唯美畫面完美結合的動畫電影。

多數的評價,是認為故事平庸無奇,但畫面與音樂的處理,讓這部動畫依然能站穩一席之地,的確如此,我看到了一個莫名其妙的憂鬱少年,慢慢地長大後,還是一樣憂鬱,姑且不論這樣子從頭到尾的憂鬱合不合理,但這樣的少年,確實也讓我想起自己有些時候,那種莫名其妙的陰沉感覺。

有些時候,確實就會發覺自己一直沒來由地心情低落,很像是一般人所說得低潮期,做什麼事都提不起勁,只有憂鬱再憂鬱。

三個段落的故事,分別是櫻花抄、太空人與秒速5公分,第一個段落,描述憂鬱少年遠野貴樹,跟青梅竹馬篠原明裡的分離,後來約定見面的過程,暴風雪似乎讓他們的距離隨著時間越拉越遠。第二段落,遠野貴樹沒有收件者的短訊習慣,明顯說明了,空間的距離讓他與明裡漸行漸遠,沒有聯絡了,而同學澄田花苗暗戀的過程,這個憂鬱少年一點也沒察覺,到了火箭發射的那一瞬間,澄田花苗終於明白,貴樹的眼光,一直落在遙遠的那一方。

第三個段落,明裡已經結婚了,貴樹與明裡已經是完全不再相交的兩條線,永遠存放在心理的某一個角落,交往了三年的水野,即使還想跟貴樹貼近,這個憂鬱少年還停留在對明裡的思念中,直到最後山崎正義的這首情歌「One more time One more chance」。


秒速5釐米
秒速5公分 - Wiki
山崎正義-One more time,One more chance
新海誠

2007/10/5

Using ldap api to connect MS Active Directory

當系統需要透過外部的帳號倉庫做帳號認證時,我們以Spring外接一段程式,處理系統登入與修改密碼的程序,連接外部的LDAP或AD。

當我遇到MS AD時,我必須要先把他安裝起來,這是我遇到的第一個困難,我參考了JNDI AD 備忘記的程序,先把MS Windows 2003標準版安裝在VM上,然後設定了AD,接下來設定了IIS,並安裝CA,把SSL搞定跟key store搞定,其中我以ldap browser測試過,可以連接AD成功,瀏覽整個AD Tree的資料。

後來就參考了java如何結合(呼叫)Win2000 ActiveDirectory,做出了AD認證的程式,這個認證有點奇怪,因為只要是合法的帳號,就可以瀏覽所有的使用者節點資料。

接下來就是修改密碼,參考如何使用 JNDI change win 2000 AD user password,可以處理修改密碼的程序,但前提是MS IIS export的key必須要先import到JVM的cacerts這個key store裡面,得用SSL才能運作。

搜尋所有使用者帳號的查詢,就是透過ldap的searchFilter功能,針對Users,把一些非一般使用者帳號的條件放進去,將不合用的帳號過濾掉,拉出資料的查詢結果,就完成了(查詢結果回傳的ADUser物件,只是一個包含了id與name欄位的data bean物件)。

安裝是最麻煩的地方......。

public class ADClient {
private static Logger logger = Logger.getLogger(ADClient.class.getName());

public static boolean checkAuth(String ldapserver, String userdn, String password)
throws NamingException {
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
// env.put(Context.PROVIDER_URL, "ldap://192.168.2.200");
env.put(Context.PROVIDER_URL, ldapserver);
env.put(Context.SECURITY_AUTHENTICATION, "Simple");
// env.put(Context.SECURITY_PRINCIPAL, "CN=" + uid + ",CN=Users,DC=msdiv,DC=com,DC=tw");
env.put(Context.SECURITY_PRINCIPAL, userdn);
env.put(Context.SECURITY_CREDENTIALS, password);

DirContext ctx = null;
boolean bLogin = false;
try {
ctx = new InitialDirContext(env);
bLogin = true;
} catch (javax.naming.AuthenticationException authe) {
logger.error("Error: ", authe);
bLogin = false;
} finally {
if (ctx != null) {
try {
ctx.close();
} catch (Exception Ignore) {
}
}
}
return bLogin;
}

public static void changePwd(String ldapserver, String admindn, String adminpassword,
String userdn, String newPassword, String keystorePath, String keystorePassword)
throws NamingException, UnsupportedEncodingException {
Hashtable env = new Hashtable();
// String admindn = "cn=administrator,CN=Users,DC=msdiv,DC=com,DC=tw";
// String adminpassword = "adtest1111";
// String userdn = "CN=test,CN=Users,DC=msdiv,DC=com,DC=tw";
// String newPassword = "test2222";

// System.out.println("java.home=" + System.getProperty("java.home"));
// String keystore = System.getProperty("java.home") + "\\lib\\security\\cacerts";
// String keystore = "C:\\Program Files\\Java\\jdk1.6.0\\jre\\lib\\security\\cacerts";

System.setProperty("javax.net.ssl.trustStore", keystorePath);
System.setProperty("javax.net.ssl.trustStorePassword", keystorePassword);
System.setProperty("UseSunHttpHandler", "true");

env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, admindn);
env.put(Context.SECURITY_CREDENTIALS, adminpassword);
env.put(Context.SECURITY_PROTOCOL, "ssl");
// String ldapURL = "ldaps://192.168.2.200";
env.put(Context.PROVIDER_URL, ldapserver);

LdapContext ctx = null;
try {
logger.info("AD is trying to reset Password for: " + userdn);
ctx = new InitialLdapContext(env, null);
String newQuotedPassword = "\"" + newPassword + "\"";
byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");
ModificationItem[] mods = new ModificationItem[1];
mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute(
"unicodePwd", newUnicodePassword));
ctx.modifyAttributes(userdn, mods);
logger.info("AD Reset Password for: " + userdn + " ok.");
} finally {
if (ctx != null) {
if (ctx != null) {
try {
ctx.close();
} catch (Exception Ignore) {
}
}
}
}

}

public static ArrayList queryAD(String ldapserver, String admindn,
String adminpassword, int searchScope, String searchBaseDn, String searchFilter) throws NamingException {
ArrayList adusers=new ArrayList();

Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, ldapserver);
env.put(Context.SECURITY_AUTHENTICATION, "Simple");
// env.put(Context.SECURITY_PRINCIPAL, "CN=Administrator,CN=Users,DC=msdiv,DC=com,DC=tw");
// env.put(Context.SECURITY_CREDENTIALS, "adtest1111");
env.put(Context.SECURITY_PRINCIPAL, admindn);
env.put(Context.SECURITY_CREDENTIALS, adminpassword);

DirContext ctx = null;
boolean bLogin = false;
try {
ctx = new InitialDirContext(env);
bLogin = true;

// Create the search controls
SearchControls searchCtls = new SearchControls();

// Specify the search scope
// searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
// searchCtls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
searchCtls.setSearchScope(searchScope);

// specify the LDAP search filter
// String searchFilter = "(& (objectClass=user) (!(adminCount=*))
// (!(isCriticalSystemObject=*)) (userPrincipalName=*) )";

// Specify the Base for the search
// String searchBaseDn = "CN=Users,DC=msdiv,DC=com,DC=tw";

// initialize counter to total the group members
int totalResults = 0;

// Specify the attributes to return
// String returnedAtts[] = { "sAMAccountName", "givenName", "mail" };
String returnedAtts[] = { "sAMAccountName", "name", "userPrincipalName" };
searchCtls.setReturningAttributes(returnedAtts);

// Search for objects using the filter
NamingEnumeration answer = ctx.search(searchBaseDn, searchFilter, searchCtls);

// Loop through the search results

String id = "";
String name = "";
while (answer.hasMoreElements()) {
totalResults++;
SearchResult sr = (SearchResult) answer.next();

logger.info(totalResults + ">" + sr.getName());

// Print out the groups

Attributes attrs = sr.getAttributes();
if (attrs != null) {

try {
for (NamingEnumeration ae = attrs.getAll(); ae.hasMore();) {
Attribute attr = (Attribute) ae.next();
logger.debug(" Attribute: " + attr.getID());
if (attr.getID().equals("sAMAccountName")) {
for (NamingEnumeration e = attr.getAll(); e.hasMore();) {
id = (String) e.next();
logger.debug(" value: " + id);
}
} else if (attr.getID().equals("name")) {
for (NamingEnumeration e = attr.getAll(); e.hasMore();) {
name = (String) e.next();
logger.debug(" value: " + name);
}
} else {
for (NamingEnumeration e = attr.getAll(); e.hasMore();) {
logger.debug(" value: " + e.next());
}
}
}
} catch (NamingException e) {
logger.error("Can not retrieve data from AD!");
logger.error("Error: ", e);
throw e;
}
}

if( !id.equals("")) {
adusers.add(new ADUser(id, name));
}
}
logger.info("Total results: " + totalResults);
} finally {
if (ctx != null) {
try {
ctx.close();
} catch (Exception Ignore) {
}
}
}
return adusers;
}
}

2007/9/25

撲克牌的用途

有關撲克牌,大家都覺得,這是一種賭博的工具,但我們家的小朋友,卻早早在三歲時,就很熟悉用撲克牌來玩一些遊戲。我們會在家裡玩的遊戲是,抓鬼跟心臟病。

通常抓鬼是大家發牌後,先把成對的撲克牌丟出來,接下來才開始一個接著一個抓鬼牌,最後一個拿到鬼牌的就輸了,這我們會玩,但我們還會玩一些抓鬼的變形遊戲,例如,把所有的牌蓋起來撲開,接下來就一個一個流輪抽鬼牌,抽到鬼牌的就贏了。或者我們會猜拳,用布猜贏可以抽五張,剪刀可抽兩張,石頭可抽一張。抽牌的部分也可有變化,例如在抽的時候,大家可以直接猜抽到的牌是幾號,假如我先猜是9,抽到的牌也是9的時候,就贏了一次,就這樣一直猜,猜到沒有牌,看誰猜中最多次。

心臟病的遊戲,小朋友一開始是不會玩的,因為心臟病的規則是,最後一個拍的人,得把牌拿走,而手上剩下最多牌的人就輸了,小朋友一開始總是不會知道要搶先一步看到牌的數字,然後拍下去,我突然想到,把規則稍微修改一下,我們改成,最先拍下去的人,就可以把所有的牌拿走,手上拿到最多牌的人就贏了,這樣子一改,小朋友馬上很專注地,看丟出來的牌是不是跟叫牌的數字一樣。

心臟病也是有變形的遊戲,原本叫牌的順序是依照數字順序1~10,11,12,13這樣的順序,我們改成自己隨便叫一個數字,規則一改成這樣,這個遊戲馬上就變成很慢速的遊戲,因為你要仔細聽大家叫牌的數字,同時也要看丟出來的牌面數字,馬上就變成一個更好玩的遊戲。還有幾次我們改成一次丟兩張牌出來。後來我有一次看到他有很多abc字母的字卡,我就想到可以用這個字卡來玩心臟病,他也玩得意猶未盡。

今天我們又發明了一個新玩法,拿撲克牌來說故事,例如,我先抽到7,我會說,有一天小P走在路上遇到了7個小矮人,接下來換他抽一張牌,繼續講故事,雖然他講的故事沒什麼邏輯順序,但他也很高興地玩了兩次。

撲克牌是一個很好用的玩具,我想我會一直使用下去。

撲克牌 Wiki

2007/9/5

我剩下什麼?我擁有什麼?

去掉工作,
我剩下家人,與許久不見的朋友們。
去掉MSN,
我剩下家人,和一個家。
沒有家,
我剩下手機跟一點錢。
去掉電話,
我剩下電腦。
去掉鍵盤與滑鼠,
我剩下什麼?

嗯!
我有電腦、手機、家人、朋友跟一點錢。

僅此而已...

2007/7/23

Chain of Responsibility Implementation using Spring IoC

GoF中提到,Chain of Responsibility的實作方式有兩種,一種是製作successor串列(利用Composite的既有父節點指標,或是自己再另外寫一個,這個實作可以寫在Handler裡面或是定義在這些Event Handlers的外面),一種是直接連接successor。

Spring IoC提供使用者以XML定義的方式,初始化物件,假設我們先定義了一個Event Handler interface,接著先依照自己的需要實作多個物件,implements這個Handeler介面,而以Spring XML設定的方式,初始化多個物件實體。

再來就是靠Spring在ApplicationContext中提供的getBeansOfType這個method,一次將所有定義在xml裡面,實作了EventHandler介面的所有beans,然後依序將Event傳送給所有Event Handler處理。當然這種實作方式,沒有辦法彈性地設定這些EventHandler接收Event的前後順序,只能保證Event確實會傳送給每一個在XML中已經定義的EventHandlers。

如果要讓EventHandler自己決定下一個EventHandler是誰,開發人員可以調整XML定義bean的順序,讓getBeansOfType取到bean的順序,就是xml中定義的順序,但這不是一種彈性的作法。另一種作法,得在EventHandler中增加一個method,傳回下一個EventHandler的bean id,但這樣寫似乎也不是一種好方法。

Anyway我們的issue並不在意處理Event的順序,所以...就先這樣吧。

「我的科普寫作經驗講座」 by 曾志朗院士

7月21日下午免費在科博館聽了一場曾志朗校長的演講,他在第一個小時,只用了那一張演講主題的投影片,而一開始他瀏覽投影片時,我發現他總共準備了80張,這似乎是一個專業演講者的通病,順手拈來就能長篇大論,準備的總遠比講出來的多,我不知道他準備的那些投影片中,後面的到底有沒有用過,但這不是我們能挑戰的地方,因為我們多數都希望,他能多講一點多講一點。

曾校長提到他的第一篇科普文章"Who murdered the dinosaur?"『誰謀殺了恐龍?』,他提到他以謀殺案的方式,兩條平行線索,討論究竟是什麼原因造成恐龍在短時間內滅絕。後來他就講到,在寫這篇文章的一開始,他先講了一個"The Lost World"的故事,他在美國授課時,曾經看過第一版的"The Lost World"。

這本小說,在講亞馬遜河的一塊高原,上頭可能有遠古的恐龍還在那裡生活。後來他在霍金斯研究室竟然看到在那本小說中,手繪高原圖片的真實照片,也提到他跟霍金斯在一起的一些故事,還提到阿里來他們實驗室參訪,決定是否要贊助他們的研究。

科普就是在說明科學的本質,定義問題,尋找共通的標準,測量或實驗,得到結論,被推翻或成為定論。這一連串的過程,就是要透過這些科普文章,潛移默化地傳遞給所有人。不管是什麼科學,能以最貼近大眾生活的方式類比,就能讓這篇科普文章,獲得最大的效益。

但曾校長也提到,既然是科學,讀者就必須要花心力去讀,要不然像很多人買了「時間簡史」,就把書供在書架上,其實是一點用處都沒有的,要有優良的科普作家與作品,也要對應有認真的讀者。一個好作家就得讓大家願意去了解與接受,一個科學知識的新概念,這樣就成功了。

有特殊貢獻的人才,多有「超強的記憶力」,這似乎是不可或缺的一項技能,而這種記憶力,並不單只是記住事情而已,而是能夠舉一反三,從某些事情的特徵,快速地在記憶庫中撈出相關的記憶經驗,當然也要生活經驗夠豐富,才能在瞬間類比出多件事物。

2007/7/6

Spring提供的資源搜尋器 PathMatchingResourcePatternResolver

過去在尋找設定檔的時候,總是得先用context.getRealPath("/")找到webapp實體的硬碟位置後,才能逐步以File的方式,尋找每一個設定檔,但是這種方法,在weblogic裡面卻行不通了,因為weblogic以context.getRealPath("/")回傳的結果跟tomcat不同。

這個時候突然想到先前在weblogic上設定spring-hibernate的方法(ex: classpath*:/**/mappings/mysql/**/*.hbm.xml),於是就先去把spring source code抓回來,然後trace這個部份是怎麼寫的。很幸運的是,這一切都是靠一個類別org.springframework.core.io.support.PathMatchingResourcePatternResolver就做完了,因此我就有了一個能在webapp上尋找設定檔的方式,連同在stand-alone的junit test也能運作。

至於這個PathMatchingResourcePatternResolver到底是怎麼做的,把spring framework log4j的 log level設定為info(log4j.logger.org.springframework=info),就能發現PathMatchingResourcePatternResolver把整個classpath裡所有的資源包含jar檔都能掃描一次,如果設定的參數為硬碟路徑(mappingDirectoryLocations),spring就能把該路徑下所有的子目錄都查過一次。至於classpath,包含jar檔的部分,spring則是以class loader提供的getXXXResource methods做出來的,詳細的實作方式我也不清楚,反正直接把PathMatchingResourcePatternResolver拿來就很好用了。

但因為class loader本身的限制,先前這種pattern的寫法**/config/**/mappings/mysql/**/*.hbm.xml,得把一開始的prefix路徑寫上去,改成classpath*:config/**/mappings/mysql/**/*.hbm.xml。

最後,先把大多數可能的package prefix都列出來
String[] prefix={"config", "tw", "com" ... };
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
for (int i = 0; i < prefix.length; i++) {
if (prefix[i] != null & !prefix[i].equals("")) {
Resource[] resources = resolver.getResources("classpath*:" + prefix[i] + "/**/configsomething/config-*.xml");
}
}

這樣就能找到在classpath裡面包含jar檔裡面,所有符合/**/configsomething/config-*.xml這個pattern的資源,回傳的資源類別為org.springframework.core.io.Resource,只要用getInputStream()這個method就能取得該資源檔案的InputStream。

ps. 這種方式只適合在app server打開時,init webapp時使用,換句話說,Spring的ApplicationContext在一個webapp裡面只需要建立一次,大家共用就可以了。

2007/6/22

Weblogic with Spring - Hibernate

由於Hibernate3使用了ANRLR當作SQL語法的parser,但是weblogic裡內含了舊版的ANTLR,這兩個是不能共用的會相衝突,因此得用classpath設定的方式將weblogic的ANTLR蓋掉。

修改的步驟如下:

Step 1.
把Spring使用的ANTLR jar,原本放在WEB-INF/lib/antlr-2.7.6rc1.jar,複製到C:\bea\wlserver_10.0\samples\domains\wl_server\lib

Step 2.
修改 C:\bea\wlserver_10.0\samples\domains\wl_server\bin\setDomainEnv.cmd
在 set CLASSPATH=%PRE_CLASSPATH%; 的後面加上 C:\bea\wlserver_10.0\samples\domains\wl_server\lib\antlr-2.7.6rc1.jar; 如同下面的範例

set CLASSPATH=%PRE_CLASSPATH%;C:\bea\wlserver_10.0\samples\domains\wl_server\lib\antlr-2.7.6rc1.jar;%WEBLOGIC_CLASSPATH%;%POST_CLASSPATH%;%WLP_POST_CLASSPATH%


另外在設定spring跟hibernate的時候,程式裡面要取得Spring的ApplicationContext可以將spring設定檔放在classes/config裡面,並將路徑設定為:
ApplicationContext springContext = new ClassPathXmlApplicationContext("config/bean/bean-config*.xml");

在spring設定檔中,要建立org.springframework.orm.hibernate3.LocalSessionFactoryBean的sessionFactory bean的時候,可以把hibernate設定檔中資源路徑的地方,設定為以下的方式,總共有四種mappingResources、mappingLocations、mappingDirectoryLocations與mappingJarLocations可以使用,因為weblogic會自動將classes裡面的設定檔跟.class檔案都jar在_wl_cls_gen.jar這個檔案裡面,以前用mappingDirectoryLocations classpath:/config/mappings/mysql這個寫法,是不能運作的,改成這個也不行 mappingLocations classpath*:/**/mappings/mysql/**/*.hbm.xml,最後才試出這種寫法,在tomcat跟weblogic都可以正常運作


config/mappings/mysql/**/*.hbm.xml



Spring的資源處理比我想像的還厲害,Chapter 4. Resources Chapter 4. 資源,在指定資源路徑的時候,支援類似ANT的資源指定方式。

2007/6/21

不舉 的左手

應該是職業傷害的關係,長久以來,我的肩膀總會覺得酸痛,還常常落枕,最近,左手肩膀又開始出毛病了,現在在左手上胳臂跟肩膀的連接處不是酸痛而是有一點刺痛,這是不是五十肩的症狀?我開始試著在沒事的時候將左手往上舉高,也稍微繞繞肩膀,不知道會不會有幫助。

不僅僅肩膀有問題,我可以感覺得到,因為長期低頭面對電腦銀幕的關係,我的脖子兩側因缺乏運動而覺得很緊,這或許是靜態工作者的原罪。現在的社會要求「專業」,但專業的成份得要一分努力一分收穫,得花時間培養這種專業,而經過了這麼久的時間,讓我們的生活養成習慣,可以為了一個搞不清楚為甚麼的bug,一直坐著想辦法解決問題,這是一種不可避免的惡性循環。

公司提供健康檢查的機會,希望大家注意身體健康,但治標不能治本,其實得要更積極地要求工程師,每1~2小時,就強迫離開電腦銀幕,離開座位10分鐘,看看窗外遠方,讓視線與心情暫時跳離一行一行的程式碼。

ps.
才剛解決weblogic與tomcat中,getRealPath的差異(以getResourceAsStream取代),又發現Hibernate 3跟weblogic中舊版的ANTLR相衝這個大問題,得儘快解決。

weblogic這麼昂貴的AP server,在我們的伺服器上卻得耗掉200~300MB的記憶體,速度很慢跑不大動,每一次重開Server就得花掉5 mins(還是Tomcat好用多了),又遇上Hibernate的大問題,真的很麻煩,功能多了反而問題更多,這似乎又是軟體的另一樁原罪。

2007/6/14

Anti-Hack 的解決方案

要對付XSS, SQL injection,How to add validation logic to HttpServletRequest這個網站提供了一個包裝HttpServletRequestWrapper的方式,可以封裝HttpServletRequest,然後再以filter的方式,把這個wrapper套用到 /* 所有網址上。

這篇文章提到的canonicalize與HTMLEntityEncode,我們並沒有採用,其實我還不大瞭解canonicalization的原理,或許在瞭解後會加上去。我們是利用regular expression pattern matching的方式,將一些已知基本的攻擊字元替代掉,也就是這篇文章BadInputFilterValve.java提到的方式,直接用String.replaceAll的方法,將reqest裡的參數都處理掉,把 \" 取代成 " ,把 document(.*)\\.(.*)cookie 取代成 document.cookie 。

2007/5/30

Anti-Hack how to??

最近遇到最棘手的事情,算是要提昇web app的防駭等級,網路上可找到的攻擊資料比較多,Input Validation Modules這個網站還提供視訊的攻防教學,XSS (Cross Site Scripting) Cheat Sheet列出了很多XSS的攻擊檢測方法。

但想要為自己的web app作防駭的時候,卻一直找不到適當的XSS與SQL injection的Regular Expression,用來放在filter上過濾所有user的request parameters,.NET很貼心地提供了 validateRequest設定選項來幫助programmer杜絕駭客,而Java Solution在哪裡,我還找不到......

http://blogged-on.de/xss提供了一個XSS攻擊的遊戲,讓你試著找到XSS攻擊的輸入資料方式,據說有六關,目前我只過到第三關....通往第二關的密碼是:stage2,通往第三關的密碼是:closing_brackets

2007/5/9

日巡者 by 盧基揚年科 & 瓦西里耶夫

這是光明與黑暗之間的競爭,看完日巡者讓我覺得有些故事的情節過程很奇怪,但奇幻、科幻故事就是要在特殊的基本設定上,把不可能的事情寫出來,還要讓讀者覺得理所當然,這就得靠作者過人的寫作功力了。

第一篇閒人莫入,篇名讓我搞不清楚是在講什麼事情。故事的內容是在說,黑暗女巫阿莉莎在「夜巡者」的最後一篇中,沒頭沒腦地非法使用稜鏡之後,這位薩武龍的愛人失寵了,後來在這次跟光明法師對抗,劫走達莉亞的過程中,阿莉莎釋放了最後一丁點的法力,幫助日巡隊成功完成任務,後來才在薩武龍的指引下來到了阿爾捷克營隊,吸收孩子們的惡夢恢復法力。

在營隊的時候,沒有法力的一般人阿莉莎,邂逅伊格爾,兩個人互相吸引,但卻在阿莉莎完全恢復法力的時候,他發現伊格爾也同時恢復了法力,進入幽界,原來伊格爾是個光明法師,他也參與了先前光明與黑暗對抗的戰事而失去法力,伊格爾是來吸收孩子們的快樂情緒。

伊格爾認為黑暗女巫阿莉莎一定是故意設下了陷阱,伊格爾的悲憤讓他失去理智,要求與阿莉莎對決,在最後,阿莉莎呼叫薩武龍的時候,她發現這一切都是圈套,她不過只是薩武龍的棋子,「愛情是偉大的力量」,原以為薩武龍的愛情會幫助她打成平手,卻發現愛情的力量也會生恨,伊格爾的恨意急著想要將對方殺死,阿莉莎終於明白了。

閒人莫入指的應該是黑暗與光明法師對決的時候,應該是閒人莫入的,但迷戀阿莉莎的馬卡爾衝了進來,也付出了代價。

第二篇異界外人,光明界獲得了斯薇塔大法師的協助,頓時使得光明與黑暗之間的實力失衡,幽界為了平衡光明與黑暗之間的拉鋸實力,產生了一個超凡人維塔利,這個憑空出現的超凡人並不清楚他為甚麼這樣,只單純地聽從心裡面聽到的那個聲音的指示,他是鏡子,為了抗衡斯薇塔而產生的鏡子,在途中甚至獲得了「法夫尼爾的指甲」吸收法力。

一連串的違法事件發生了,準備召開法庭做出判決,薩武龍勸安東不要出席,去的話,安東會失去斯薇塔,不去的話,鏡子會吸光斯薇塔的法力,這暫時失去的法力,會平衡黑暗與光明之間的勢力,安東可以得到斯薇塔的愛情,也會停止損失光明法師/朋友的這一連串事件,「要幫助自己,得先幫助敵人」。

在鏡子維塔利帶著跟安東一樣的隨身聽踏入法庭的一瞬間,鏡子明白了,這不過是讓斯薇塔誤認她失去了安東的假象,大審判官雖然心知肚明,但選擇沉默。斯薇塔的法力消失了,安東毫髮無傷的出現,讓斯薇塔喜出望外,薩武龍滿意地向安東致謝,大審判官馬克西姆認定維塔利不受大和約管束,宣告會議結束。

斯薇塔失去了法力,為甚麼不能像伊格爾與阿莉莎一樣,吸收孩子們的快樂情緒來恢復法力,難道大法師比不上一個二級或三級的法師嗎?

第三篇超凡法力的一開始,蓋瑟與安東討論先前審判會議的狀況,安東向蓋瑟說明,如果不讓鏡子毀滅斯薇塔,就是讓她失去法力,要不就是斯薇塔的死,要不就是更多光明隊員的死,最好的選擇,當然是讓斯薇塔暫時失去法力。蓋瑟最生氣的,就是光明勢力無法擴張,而且最近三次鏡子出現的時候,都是站在黑暗那邊。

蓋瑟希望安東帶著伊格爾參加法庭,薩武龍要艾德加出庭,捍衛偷取法夫尼爾的指甲的雷金兄弟會,兩隊人馬都漸漸地推理出,讓法夫尼爾復活的要件與儀式,四個不同膚色的兄弟會成員,一個要跟法夫尼爾交換的艾德加,薩武龍的陰謀當然被拆穿了,但薩武龍似乎並沒有很驚訝,因為大家都已經預見這件事了,究竟薩武龍與蓋瑟兩邊的目的是什麼,可能在第三部才知道結果。斯薇塔成為光明救世主的母親似乎是眾所皆知的事了。

最後是伊格爾與阿莉莎的對決審判,阿莉莎暫時復活,指控薩武龍讓她犧牲,伊格爾無罪,但他也選擇消失在優界中。

2007/5/6

黑夜中散發綠色螢光的 夜巡者 by 盧基揚年科

為甚麼我要說「黑夜中散發綠色螢光」這句話呢?那是因為我在看完這本被譽為近年來最有創意,善惡與光明對立的奇幻小說後,就把書放在桌上,接著看日巡者,後來有一天晚上在書桌前,關了燈準備睡覺的時候,突然發現桌上夜巡者這本書的封面上,標題的三個大字,竟然散發著綠色的螢光,真是特別。

身為光明界的成員,為了維持與黑暗的對立平衡,卻得在黑夜中偵查巡視任何破壞這微妙平衡,違背大和約的事件,一個光明使者卻得生活在黑暗之中,有時候也會搞不清楚自己究竟屬於光明或是黑暗。光明不見得就是為善,他們的行動,不過是眾多選擇中,那項對大家「傷害最小」的決定。

這時候的行動,對某些被犧牲的人來說,光明使者,也是以為惡來揚善。善與惡,就像是光明與黑暗,在日夜交替的過程中,讓人不清楚究竟是白天或是晚上,那麼為善或作惡的分別,也有不得不作的模糊地帶。

「特許憑證」就是一種不得不作的利益交換,光明界期待一個正常運作的世界,而黑暗界的吸血鬼,因為天生就需要人血,因此光明界特地開出特許憑證,讓吸血鬼可以正當獵捕被選中的人類,而夜巡隊存在的目的,就是阻止違法獵捕人類的吸血鬼。小男孩葉格爾雖然沒有作惡的本性,但卻在跟安東討論的過程中,了解這些不可思議的妥協平衡,在斯薇塔接受教育,快速成長的過程中,他也跟安東在面對黑暗巫師的時候,有著善與惡的激烈討論,因為安東說明,在黑暗巫師還沒有作惡之前,夜巡隊並沒有權利能夠對付一個無害的黑暗巫師。

第一篇自己的命運,說的應該是針對安東,因為故事是以安東當第一人稱,描述整個故事的開端,老大明白安東跟斯薇塔未來糾結交織的命運,原本以為日巡隊與夜巡隊是為了葉格爾而來,為了一個未來的大法師而競爭,結果竟然是斯薇塔,雙方不擇手段的競爭過程,讓一個不尋常的小孩葉格爾一針見血地道出「你們都很醜陋」的結語,然而身為光明使者的安東,得依循著光明法則,幫助斯薇塔在短時間內啟蒙成長,這是安東的命運,身為一個光明使者不得不遵循的命運。

第二篇自己人裡的自己人,馬克西姆握著手裡的匕首,當他面對不尋常黑暗界的人,他自稱法官,出面替天行道,為民除害,這是上天賦予他的職責與能力。但當這些被處決的黑暗界的人,都圍繞在安東的身邊,這一切的事件安排,就成了一項陰謀。對安東來說,他得儘快找到這個陷害他的人,對日巡隊來說,得找到這個破壞大和約的光明界兇手,向黑暗界證明自己的清白,避免衝突。

日巡隊老大薩武龍期待對付了安東後,發狂的斯薇塔,而夜巡隊老大蓋瑟希望安東能儘快找到「野人」馬克西姆,證明他的清白。當然在最後,找到了馬克西姆,在法庭中,令人驚訝的是,蓋瑟讓馬克西姆成為高於光明與黑暗勢力的一員,一個嚴厲卻又中立的大審判官。而安東也發現,這個追逐的過程,竟然也是蓋瑟設計,要讓斯薇塔快速成長的一段過程。最後一句話,最讓安東不想聽見的一句話,老大隊安東說,「這裡不需要薩武龍,他在這裡真的沒有任何作用,這完全是夜巡隊自己內部的操練。」

第三篇自己人裡的外人,這應該
夜巡隊所有成員都離開莫斯科到了虎兒的家作客,當安東自己一個人隨著西蒙回到家裏,他竟然發現日巡隊的老大薩武龍在他的家裏,向安東敘述著進一百年來光明勢力的嘗試與夢想,第一次是十月革命,第二次是第二次世界大戰,現在又要再一次,光明勢力希望能再一次促成全球性的社會改革實驗,他告訴安東,斯薇塔並不是要準備領導夜巡隊,而是為了完成奧莉佳沒有成功的使命。

安東認為任何要嘗試改變人類社會的行動,都是必會失敗,不會對人類帶來益處,但蓋瑟認為安東反對斯薇塔肩負的使命,也認為安東無權基於個人利益反對夜巡隊的政策,他不知道行動成功的機會有多大,但他知道在此之前,從沒有過這麼好的機會。

安東需要知道蓋瑟的行動,他要去見薩武龍。然而很奇怪的,在到了莫斯科中心的幽界時,出現了一段小插曲,他發現薩武龍的愛人阿莉莎正非法使用稜鏡,非法使用第三級介入法術。薩武龍向安東提出了第二級法術介入權,交換阿莉莎的違法事證,黑暗法師跟光明法師簽訂了合約。

最後,安東沿途吸收了眾多力量,原本想要以「道德重整術」面對斯薇塔及葉格爾,阻止蓋瑟的行動,但卻將所有吸收到的力量用在自己身上,這讓斯薇塔很驚訝,原本安東有能力阻止這一切,卻將權利交還給斯薇塔自己決定,斯薇塔將葉格爾的命運之書上的字都擦掉了。

薩武龍認定光明界失敗然後離開了,蓋瑟卻說,他剛剛才搞清楚,每一個人都有自己的命運。安東也明白了,這一切都是幌子,在日巡隊專注在斯薇塔身上時,奧莉佳已經拿著另一半的粉筆完成使命,改變了另外一個人的命運。蓋瑟這一切行動,都是為了不損害他的事業前途,還能迫使領導階層撤銷對奧莉佳的懲罰。

沒有絕對的對錯,沒有絕對的善惡,而光明與黑暗的對峙,還在繼續進行當中。

2007/4/20

小朋友第一本英文故事書 The Missing Piece

從來沒想到,這一本書竟然是小朋友第一本純英文的故事書。

這本書是老婆在民國8x年就買的原版英文故事書,在閱讀過「培養孩子的英文耳朵」後,一直想要購買An I Can Read Book系列的英文讀物試試看,但買書的進度拖了很久,一個月前,小朋友自己在書架上發現這本「The Missing Piece」,自己跟我說,要我念給他聽。

所以我就依照「培養孩子的英文耳朵」裡面提到的方法,先用一句英文,一句中文,念了好幾次之後,我就開始只念英文,剛開始只念英文的時候,小朋友會自己作翻譯,因為他已經知道這個故事的內容,大致都能把每一頁的故事內容講出來,所以我念了英文後,他自己會用中文念一次(他印象中的故事內容),後來他翻譯的頁數漸漸變少了,開始會聽我念的英文,然後會突然跟著我念幾句。

不知道未來,會怎麼樣繼續發展下去,但希望這些念故事的功夫,能在他長大成人後,發現我們的辛苦是有代價的。

已經訂了三本 An I Can Read Book Level 1 的書,孩子的童年只有一次,我還得繼續嘗試下去。

2007/4/4

最近一念再念的故事書

小朋友每天都得讀故事書,新買的故事書,故事都比較有深度,國內的故事書故事比較直接,教小朋友貪心、分享這些概念,講得很白很直接,但國外作家的作品就不一樣了,故事有深度,並不是馬上就懂的,我也不清楚小朋友到底懂不懂這些故事的弦外之音。至於有一些老婆小時候看的故事書,圖案比較粗糙簡單,故事書也很小本,但小朋友一樣讀得很高興。

「子兒,吐吐」
「一片披薩一塊錢」
「第一百個客人」
「我不想長大」
「河川」
「髒小孩」
「貪心的國王」
「小木偶奇遇記」
「蝴蝶風箏」
「一個親親」
「兒子的大玩偶」

「勇敢的小裁縫」
「無精打采的小狗」
「爸爸」
「跟老鼠一起長大的貓」
「小男孩與大喇叭」
......

隨意搜尋 by Peter Morville

隨意搜尋 by Peter Morville

網路世界,改變了人們取得資訊的習慣,在速食與精緻的文化衝突,還有搜尋涵蓋度與精確度的衝突下,使用者該如何面對雜亂無章的網路,網站設計者又該怎麼提供適切的資訊,提供具有可尋性的文件資料。

由於
「閱讀網頁是掃視,不是瀏覽」
「許多使用者並不是從首頁開始閱讀的」
「網路世界讓賣方市場轉變為買方市場」
「使用者要花更多時間,交互參照驗證資料的真假,並過濾資料」
「以文字重現概念時,會因為英文一字多義的情況,而使資訊檢索更難達到適切性」
「人類遵循最省事原則」
「Metcalfe's Law: 網路效用=使用者數量的平方,隨著使用者增加,網路系統的價值呈現指數成長」
「跳傘比率bailout rate: 網頁為34k時,跳傘比率為6%,當網頁為40k時,跳傘比率為30%」
「人類有限的非理性:先入為主anchoring、深信不疑confirmation、深刻難忘memorability、維持現狀status quo、呆帳成本sunk cost」
「資訊失調:使用者希望有充足的資訊,卻又選擇忽略大量的資料」
「資料與詮釋資料的邊界在模糊化」
「在真正的網路中,連結不是隨機的,歡迎度會造成吸引力」
「群眾分類法:讓使用者為物件定義多個共享標籤,這種方法無法處理對等性、階層性、與語義的相互關係」

為了要
「從使用者的角度來思考」
「提供環境可尋性:在任何時候、任何地方能夠尋找任何人與物」
「讓使用者能在九十億的網頁資料中,找到正確可信任的網頁」
「解決資訊的貿易逆差:為存入的大量資訊,設計新的出口」
「在資訊的推拉中,尋求平衡點」
「提供充足的資訊,做出正確的決策」
「將文件塑形」
「從群眾分類法,轉變為使用詮釋資料的邊界物件」

網站必須要
「改進網站的導覽性與可用性」
「運用重複的手法傳遞相同的資訊,加深使用者的印象」
「讓每一份資料都成為獨立的可尋物件」
「建立豐富的資訊,並解決注意力貧乏的問題」
「以使用者體驗與中心為設計要務」
「簡化使用介面」
「根據使用者體驗的蜂巢模型,檢視網站的設計:有用度useful、能用度usable、渴望度desirable、可尋性findable、無障礙accessible、信賴度credible、價值度valuable」
「改善網頁溝通中的雜訊比率」
「讓資訊成為環境資訊:包覆使用者,卻不會吸引他們的注意,隨手可得,卻又不會緊迫盯人」
「先將文件分類,類型讓文件提高可尋性」

使用者必須要
「從學習如何學習的能力,轉變為學習資訊辨識能力」
「提昇尋找正確資料的能力」

2007/3/7

餵故事書長大的孩子 by 汪培珽

曾經閱讀過「我這樣教出資優兒」這本書,也受到「讀經」概念的影響,所以從小朋友兩歲多就開始強迫他,或是用他想看的DVD與卡通交換,要求小朋友讀三字經跟唐詩,還有一本台語童謠,後來小朋友漸漸地抗拒讀唐詩跟三字經,只剩下那本台語童謠,會開開心心地去念它。到最近已經一年多了,雖然他多少記得經文的內容,但只知道讀音,完全不了解經文的意義,是強記下來的,所以我也不強求了。

兩個星期前老婆從娘家拿回二十幾本他小時候唸過的故事書,開始一本一本地念故事給小朋友聽,從第一天一本,越念越多,變成一次要念好幾本,幾天後我就在網路上看到這本「餵故事書長大的孩子」,馬上買了回來看,一個小時就看完了。

這本書的作者,會給小朋友買很多童書繪本,讓小朋友一邊看圖,大人一邊用口語講故事給他聽,他完全是從「美學」教育的觀點出發的,這也是當初我看完「我這樣教出資優兒」,所得到的疑問,美術教育從何教起?這個問題的答案就在這本書裡。

小朋友一開始對文字完全沒有興趣,只喜歡看圖片,「繪本」的文字很少,主要都是用圖片來表達故事的內容,而文字的用途是什麼呢?就是讓爸爸或是媽媽「讀」給小朋友聽,

自從看完這本書後,我就開始嘗試在小朋友吃飯吃到一半,大人都吃完的時候,就唸一本故事書給他聽,讓他邊吃飯邊聽故事,另外也在睡覺前,念幾本故事書給他聽,每一次要念故事之前,我都會先問他,你想不想聽故事,只有在他回答想的時候,我才會唸。晚上飯後是遊戲時間,這時候他也會覺得不想聽故事,只想要玩球,我們也不強求,他想玩球就讓他玩球。

我發現這樣的方法非常有效,我會要求自己要保持下去,不能中斷,另外也決定要設置買童書繪本的預算,以往我都認為繪本的字那麼少,每一本卻都賣兩百元,好貴,但我現在不會這樣想了,對小朋友來說,圖畫就是他們的文字,就是他們眼中的故事。

我想在作者第二本「培養孩子的英文耳朵」過一陣子推出後,也該買來拜讀一番。

愛孩子也愛自己的7堂課from汪培珽

2007/3/3

網路服務生態系統 Web (2.0) Service Ecosystem

Ecosysetm這個名詞原本是用在生物界中,但其實就是在描述一個自成一個體系的環境與世界,在Understanding Software Industry Ecosystem這篇文章中作者提到,Cambrian explosion(寒武紀生命大爆發)的時候,短短200萬年間產生了許多高等生物以及多樣性的物種,然而這些生物為了在Natural Selection天擇的演化過程中存活下來,就必須要在被消滅之前,找到自己在這個生態系統中的定位,並成功地消滅其他競爭對手。

現在的網路時代正是等同於寒武紀的時代,因為網路的基礎建設普及,加上WWW HTML語言清楚簡單,Web瀏覽器唾手可得,每一個人透過網際網路連結成一個非常龐大的網路生態體系,在這個巨大的生態系統中,參與其中的成員,也等同於寒武紀的新物種一樣,得在被消滅之前壯大自己。

在網路服務的生態裡,為了求生存,在整個網路生態系統中佔有一席之地,身為網路服務世界中的一個生產者,必須要找到自己不可被取代的定位。針對提供網路服務平台的公司,他們是平台的生產者,必須要製作並提供特殊、不可取代的網路服務,吸收足夠的消費者取用消費資料,網路消費者可以簡單地分為兩種型態:重度的消費與輕度的消費者,舉例來說,一個提供部落格服務的網站,他們必須要讓平台穩定,並吸收足夠的消費者提供文章與照片等等內容,讓消費者轉化為生產者,產出資料。

整合網路服務的服務,這些生產者的消費對象,就是其他網路服務的資料內容,他們負責整合不同的資料,例如收集網址Bookmark的網路服務,一方面利用其他網站的資料內容,一方面產出整合的網頁頁籤提供消費者使用,這樣的服務既是消費者也是生產者。

不僅僅是提供服務的公司需要找到定位,當一般的消費者進入網站使用服務的時候,通常會免費無償地提供資料給該網站使用,這些消費者就成了資料內容的生產者,這些消費者也同樣是為了自己在生態系統中的定位、不可取代性,讓自己成為強而有力的生產者,舉例來說,就像是一般的使用者在部落格網站撰寫文章或提供照片,當這樣的內容生產者越多,就會讓這個生態環境越壯大,而這種生態環境常常會併吞類似的生態環境,這也就是網路服務的世界大者恆大的原因。

有越來越多的網路服務,致力於讓網路的消費者成為強大的生產者,這些生產者產出的內容資料越多,吸引到的消費者就會增加,為該網路服務加值,而這些消費者又會繼續成為下一代的生產者,生生不息。

在這樣的循環下,網路服務公司得致力於提供多數的消費者感興趣的服務,消費者則致力於尋找對自己有用的內容資料,甚至是尋找有用的服務平台,成為新的生產者,所以不管是公司或是個人,身為網路的一份子,隨時扮演者生產者與消費者的角色,貢獻給整個網路生態系統,讓這個系統日益壯大、無遠弗屆。


超越用戶創建的模式:Web 2.0 和語義網絡
wikipedia生態系統
生態系統
Netvibes Ecosystem

2007/2/13

湯瑪士原木小火車

湯瑪士小火車系列玩具有分三種:合金、塑膠跟原木,合金小火車我沒研究,塑膠跟原木的小火車他們的差別很大,塑膠的小火車是配合大型的軌道或情境遊戲組合,車子跟遊戲組合都是需要電池驅動的,也就是說小朋友不需要操作火車,他們就會自己在軌道上依照既定的規則走來走去,這種遊戲組合價格比較便宜,但只適合年齡稍大的小朋友。



大一點的小孩例如上了國小的,有些小朋友就會追求玩具的聲光效果,也沒有心思用「手」操作,那麼就適合看著電動的玩具車自己走來走去,對小朋友來說是比較偷懶的玩法,對大人來說,也是比較節省荷包的方案。



原木的系列跟塑膠系列的小火車是不相容的,車子跟軌道的寬度不同,所以不能交互使用,原木系列的軌道組合就比較多樣化,車子也不少,軌道的零組件很多,選擇多相對的單價就高,就像是樂高積木一樣,買得越多,組合性越高,但荷包就得收緊一點。



一開始我們購買了軌道組合的 LC99556 水塔基本組,裡頭就包含一台1號湯瑪士火車、軌道、一座橋跟一個Water Tower水塔配件,沒有一下子就花大錢買豪華組合,家裏的小朋友就每天拿著車車推來推去,這個basic set除了標準的八字型組合方式外,還可以組成其他的樣式,我們試出了兩種,這個組合對三歲多的小朋友來說,的確是最好的選擇。



後來又再yahoo的購物網站,添購了3號Henry跟5號James兩台原木小火車,另外又再到百貨公司,購買了軌道的LC99951 Figure 8 Set Expansion Pack基本補充包(http://propacific-toys.com.tw/html/ThomasTWR9.html),多了這個軌道的expansion set,軌道的變化更多了,軌道上的路線也從一個一條路,馬上轉變成田字形的道路組合。



我們不知道,湯瑪士小火車對小朋友來說,可以玩到幾歲,也許他某一天會突然覺得不好玩了,但這一個月來觀察他遊戲的狀況,的確是非常適合他的一個玩具,花這些錢是值得的。

2007/1/19

基地與地球

故事的結尾依然讓我驚訝,也留下了未完的伏筆,人腦跟正子腦的整合,不就是跟「攻殼機動隊」一樣,但卻是相反的情境。在尋找地球辛苦的冗長過程中,我回想起來卻突然覺得跟「天下第一味」一樣,在故事情節發展的過程中,詳細描述了每一個人的態度、想法以及轉變的過程,去蕪存菁的作品可讓人一再地思考玩味,鉅細靡遺的作品可讓人掌握全局,其實各有各的味道,第一種作品,辛苦的是讀者,因為要自己拼湊出整個大環境的劇情與情節,第二種作品辛苦的是作者,得有人格分裂的心理準備才行。



直接討論結果,兩萬歲的機器人丹尼爾在第一法則(機器人不得傷害人類,或袖手旁觀坐視人類受到傷害。)的限制下,卻因為在解決銀河問題的時候,都必須要適時地影響某一些人,但這些調整卻不免會受到一些傷害,所以他只能夠選擇傷害最小的作法,然而由於情勢過於複雜,每一次決定都得花上相當冗長的時間,即使做出決定,也不能保證一定正確。



為了解決這個問題,跟丹尼爾一同前往月球的吉斯卡悟出了第零法則(機器人不得傷害人類整體,或袖手旁觀坐視人類整體受到傷害。)的限制,第一法則也因此加上除非違背第零法則的條件。而崔維茲提出了問題:「如何決定對人類整體何者有害、何者無害?」對一個人的傷害容易評估,對人類整體這個抽象概念卻很困難,因此丹尼爾正著手進行將人類整體,轉變為單一生命體的計畫,也就是蓋亞生命體。



但創造這種將整體看得比自己重要的生命體,卻是一件不容易的事,只有將生命體的心理模型套用上機器人學法則才能成功,所以蓋亞生命個體都套用了機器人學法則。但只有人類的生命體並不完整,只有將整個銀河有機無機的生命體全部都納進蓋亞生命體,整個蓋亞星系才算發展成功。而機器人學不允許丹尼爾做出對人類整體有風險的決定,所以才在「基地邊緣」中,需要崔維茲幫丹尼爾做出選擇蓋亞星系的決定(這個地方有點怪怪的,卻又說不出什麼地方怪,需要一個有獨立判斷能力的人,幫另一個獨立個體丹尼爾,為人類整體、甚至是整個銀河生命體下決定?)。



看起來更上一層樓的蓋亞生命體,有個問題,一旦放棄了個體化,轉變為蓋亞生命整體,成為單一生命體,也就是說沒有人類整體,那麼就等於得再加上一個空無(null,比0還小的數字)法則:不得傷害蓋亞生命體,或袖手旁觀坐視蓋亞生命體受到傷害。再來就是,蓋亞生命體下面的組成份子,人類整體、植物整體、土壤整體,究竟孰輕孰重,這些整體得在蓋亞生命體之下分出優先權的高下,因為在判斷的時候,先決定不會傷害蓋亞生命體後,就得判斷會不會傷害人類整體或是植物整體,這時候人類跟植物就得有優先順序才能判斷出結果。這樣思考起來,似乎又跟個體化組成的整體這個概念分不開了,這個問題,又似乎跟本書最後提出的問題類似:人類目前是銀河中唯一重要的智慧物種,但宇宙中有沒有另一種生命智慧物種,可能凌駕於人類之上或是跟人類相抗衡。



最後講講正子腦,丹尼爾因為老化、還有正子腦無法再微型化的關係,選擇必須要跟人腦結合,才能繼續延續生命與智慧,也就是要以人腦為主,整合正子腦的技術。攻殼機動隊中,草椎素子遇到的Stand Alone Complex個體卻不一樣,而是那些SAC意識,選擇要存活在素子的正子腦中,所以是以正子腦為主,整合不同的意識。



看了這麼多文字,看起來似乎了解了更多,但卻又沒有解決什麼事情,反而產生了更多問題,這或許是身為複雜的人類個體,值得高興卻又得感到悲哀的一種狀態。

2007/1/16

基地邊緣

從1/5入手兩本基地後傳之後,我花了五天空閒的時間,漫長的等待就是為了那最終靈光一現的真相。作者冗長的舖陳下,耐心等待的結果真的令人吃驚,既出現了以生命為前提集合了群眾意識的第三勢力蓋亞,又讓最後的答案回到地球本身這個未解的謎團上,看來得看完基地與地球才能知道結論了。



在基地邊緣中,端點星議員崔維茲配合斐洛拉特,取代了謝頓成為思考的主角。一心認為掌握了銀河的第一基地,卻一直籠罩在第二基地傳說的陰影當中,因此端點星市長布拉諾派遣崔維茲與斐洛拉特,出發尋找第二基地的下落。以為掌握了第一基地這個傀儡的第二基地,一心認為操縱了第一基地,未來將會建造第二帝國,但是在堅迪柏的線民康普幫助下,發現第一基地想要消滅第二基地的意圖,在過程中意外發現可能有反騾的存在,也就是當初騾所懼怕的那一股力量,所以第一發言者桑帝斯派遣堅迪柏與阿姆女子諾微追蹤崔維茲,追查第二基地可能會面臨的危機。



這一切都在蓋亞的掌握當中,所有人都在蓋亞星球生命群體意識的引導下,來到蓋亞的星域,原因是因為蓋亞感受到自己即將面臨到的危機,一個足以毀滅蓋亞的危機,寄望與眾不同的崔維茲能協助蓋亞解決問題。這個問題就是第一基地,希望建立軍事第二銀河帝國,而以川陀第二基地為藍本的帝國,卻是一個工於心計的父權式帝國,依靠計算維生。蓋亞星系,是以巨大的融合生命體為藍本,將所有生命形式包含有機與無機的生命體,全都融合成一個群眾意識。



雖然蓋亞擁有操控第一與第二基地的力量,但因為他們一開始是借助機器人建立的,所以也內建了機器人學三大法則,將第一法則擴充至「蓋亞不得傷害生命,或袖手旁觀坐視生命受到傷害。」為了遵循這個定律,蓋亞不知道如何處理三股力量互相牽引的衝突,也不知道如何避免銀河中的生命受到重大傷害,也不知道如何減低犧牲的程度。所以寄望與眾不同的崔維茲能藉由他自身的思考判斷,幫蓋亞決定未來。



然而崔維茲卻在做出決定之後,才說出自己也受到蓋亞間接的影響,才有這樣的決定,他也發現一直在他們身邊的寶綺思是一個監督蓋亞的機器人。崔維茲自一開始就認為在群眾意識的前提下,又能擁有個體的思想,似乎是一件不可思議的事,難道沒有特定的個體不遵循群體意識嗎?但他的抉擇只出於一個簡單的推理,因為建立蓋亞星系要花很長的時間,拖延了這段時間就能讓他有機會修正或扭轉既定的方向。崔維茲在最後也下定決心要尋找突然消失的地球,他覺得只有找到地球才能找到真正的答案。