2013年5月2日 星期四

[RB] class, instance, class instance variable

Instance variable

在程式碼中我們常能看到單一個@開頭的變數,但是可別一味把它當成是instance variable喔,如果是在instance method中被創造出來的那肯定是instance變數,但要小心這種情況:
class Med

  @@c='This is class variable'
  @c='This is class instance variable'

  def self.class_instance_c
    @c
  end
  def class_c
    @@c
  end
  def instance_c
    @c
  end
end

m = Med.new
puts m.class_c #->'This is class variable'
puts m.instance_c #->''
puts Med.class_instance_c #->'This is class instance variable'
上面的@c其實是class instance variable,是不能被物件實例存取的。
而當物件實例想要存取的@c是instance variable,並沒有被定義出來,所以才會是nil,印不出東西。


Class instance & Class instance variable

相較Class instance variable,Class variable比較好辨認,"@@"開頭的就是了。
這兩種變數的差別在於,Class variable是共用於繼承鏈上的,當Class variable被改動時,所有繼承鏈上的其他類別的此變數也會被改動。
可以想像成是直接改動javascript中的prototype
但是Class instance variable是各個類別獨立的,「可以被繼承,但是繼承給子類別後就沒有任何關係了」



cattr_accessor

就像是attr_accessor一樣,只是他提供的是對Class variable的讀寫,而不是instance variable



參考資料

[ihower] 深入Rails3: ActiveSupport 的 class_attribute
[ihower] 一些 Ruby Dynamic Features 記事 (1) OOP

2013年4月25日 星期四

[RB] Ruby中的Proc及Block

Block:

在寫ROR時,大家或多或少會碰到這樣的語法:
posts.each do |post|
  puts post.tiltle
end
或
posts.each {|post| puts post.tiltle}
上面兩段程式碼中用do-end和大括號{}包起來的就是block,但用{}包裹程式碼的方法只適用於程式碼是一行的情況。
學ROR以來有好長一段時間我都不太清楚他的妙用,只覺得這麼普通的東西為什麼要寫的那麼拐彎抹角,但是在看了code school的教學影片後發現他跟javascript的callback機制有異曲同工之妙,這樣的設計讓我們在做一些enumeration操作的時候能將block中的code傳入,之前之所以對這個概念一直搞不清楚就是因為很難具體的想像在each中視怎麼call這段code,現在我們就用這個demo來剖開這個黑盒子:
class Child
  def initialize
    @toys=['gun','hammer','puzzle']
  end
  def each_toy
    for toy in @toys 
      yield toy
    end
  end
end

mark = Child.new
mark.each_toy do |toy|
  puts "I'm playing #{toy}~"
end

在這個範例中block像參數一樣被傳入each_toy這個method,並在裡面的loop內被多次呼叫。這樣的設計除了使用起來很靈活、有彈性以外,也能減少重複的程式碼。如果你對javascript比較了解的話可以看這一段程式碼來幫助了解:
function Child() {
    var toys = ['gun','hammer','puzzle'];
    this.eachToy = function (cb) {
        for (var _i in toys) {
            cb(toys[_i]);
        }
    }
}

mark = new Child();
mark.eachToy( function (toy) {
  console.log("I'm playing " + toy + "~");
});

但是上面的程式碼中,block看起來一點都不像是物件,也不能被重複使用。如果要像是javascript一樣能將函數像物件一樣被宣告、保存起來,並傳入其他函數作為callback function,我們還少學了一些東西,那就是接下來要介紹的Proc物件。

Proc:

proc物件可以將ruby的程式碼保存起來,在需要的時候再執行他,或當做block傳入其他函數。proc有兩種實作方法:

By Proc.new

routine = Proc.new {|variable| Do something... }
或
routine = Proc.new do |variable| 
  Do something...
end

By lambda

routine = lamda {|variable| Do something... }
或
routine = -> {|variable| Do something... }

直接執行

routine.call value


把proc轉成block

要把proc物件轉為block時,我們要在這個物件的前面加上"&",例如:
posts.each &routine

在method中判斷是否有block傳入

要知道有沒有傳入block,我們可以使用block_given?來判斷:
if block_given?
  yield
else
  puts "No block past in"
end


小結

現在我們就來改寫一下一開始的範例:
class Child
  def initialize
    @toys=['gun','hammer','puzzle']
  end
  def each_toy
    if block_given?
      for toy in @toys 
        yield toy
      end
    else
      puts "No block past in"
    end
  end
end

mark = Child.new
routine = lambda { |toy| puts "I'm playing #{toy}~" }
mark.each_toy &routine

2013年4月13日 星期六

[JS] javascript OO設計 - 繼承

接觸javascript有一定的人應該都知道在js中,你所看到的幾乎都是物件。
函數是物件的第一型,Function和Object都是物件,也是函數的實例,而所有的function都是Function的instance。

關於原形(prototype)

prototype是函數物件特有的屬性,使用函數建構出來的物件會藉由原型鏈(prototype chain)而存取得到建構函數的prototype的屬性,用文字說清楚很難,不如我們直接看範例:
function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.nation = "Taiwan";
var kevin = new Person("Kevin", "18");
var laura = new Person("Laura", "17");
alert(kevin.nation); // "Taiwan"
alert(laura.nation); // "Taiwan"
雖然kevin和laura本身並沒有被定義"nation"這個屬性,但是當使用這些不存在的屬性時,會找他們的建構函數中的prototype,再沒有的話還會看這個prototype物件的建構函數中有沒有這個屬性,直到最高層,也就是Object建構函數的prototype,這就是所謂的prototype chain。為了證明這點,我們再次修改一下上面這個例子:
function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.nation = "Taiwan";
Function.prototype.whatTypeAmI = "Funciton";
Object.prototype.whatTypeAmI = "Object";
var kevin = new Person("Kevin", "18");
alert(kevin.nation); // "Taiwan"
alert(kevin.whatTypeAmI); // "Object"
alert(Person.whatTypeAmI); // "Function"

從上面可以看出雖然kevin這個物件沒有whatTypeAmI這個屬性,但是他會往上找Person.prototype物件有沒有這個屬性,再找不到就會找Person.prototype的建構函式,也就是預設的Object函式,這時我們會在他的prototype中找到whatTypeAmI這個屬性是"Object"。
而我們在呼叫Person的whatTypeAmI時也會發生找不到的情形,這時就會往上找到他的建構函數也就是Funciton的prototype,如範例所示,這時回傳的是"Function"。


利用prototype來實踐javascript繼承模式

我們可以利用上面所提到的prototype chain來實踐多層祖孫繼承的模式,我們用範例來解釋一下這點:
// 哺乳綱
function Mammals() {
    this.blood = "warm";
}

// 靈長目
function Primate() {
    this.tail = true;
    this.skin = "hairy";
}
Primate.prototype = new Mammals();

// 人科
function Homo() {
    this.skin = "smooth";
}
Homo.prototype = new Primate();

var human = new Homo();
human.name = "Kevin";

alert(human.name); // "Kevin", from self
alert(human.skin); // "smooth", from Homo
alert(human.tail); // "true", from Primate
alert(human.blood); // "warm", from Mammals
上例應該很清楚的顯示出以原型實踐繼承模式的原理了。


原型設計模式的漏洞

雖然原型設計模式能夠很方便的實踐物件間的共用屬性及繼承模式,但是在操作上要對prototype chain有一定的了解,再加上細心的邏輯驗證,才不會出現如下的錯誤:
function Human() {}
Human.prototype.blood = "red";
Human.prototype.body = ["foot","hand"];

var john = new Human();
var kevin = new Human();

john.blood = "purple";
john.body.push("wing");

alert(kevin.blood); // "red"
alert(john.blood); // "purple"

alert(kevin.body.toString()); // "foot, hand, wing"
alert(kevin.body.toString()); // "foot, hand, wing"
從上面的例子可以看到,john因為不明原因而突變以後,不只血變成紫色的,也長出翅膀來了!但是在john突變之後,kevin的血雖然沒有變色,但是卻莫名其妙長出了翅膀。很明顯的,我們不小心改動到了Human的prototype。
原來在我們為john的blood指定顏色時,javascript會為john這個物件增加一個屬於自己的"blood"屬性,這種情況就跟為物件增加屬性的方式一樣。於是在後來的呼叫時,會先找到john自己的blood屬性。但要john的body屬性執行push函式時,會發生在john中找不到body的狀況,於是就往上找到了Human.prototype的body屬性,並由他來執行push函式,此時改動到的便是Human.prototype.body了,也就連帶的影響到了無辜的kevin。

其他繼承模式設計

javascript是個很活的語言,除了prototype的實踐方式以外,我們也可以使用別的方式來實現繼承:
// 哺乳綱
function Mammals() {
    this.blood = "warm";
}

// 靈長目
function Primate() {
    Mammals.call(this); // 記得放前面,不然會蓋掉重複的屬性
    this.tail = true;
    this.skin = "hairy";
}
Primate.prototype = new Mammals();

// 人科
function Homo() {
    Primate.call(this); // 記得放前面,不然會蓋掉重複的屬性
    this.skin = "smooth";
}

var human = new Homo();
human.name = "Kevin";

alert(human.name); // "Kevin", from self
alert(human.skin); // "smooth", from Homo
alert(human.tail); // "true", from Primate
alert(human.blood); // "warm", from Mammals
這種方式是將父類別的建構函式放在子類別的建構函式中以「this」的身分來執行,為自己建置父類別的屬性。這樣的作法有個好處,就是不會因為不當的操作,改動到別的物件的屬性,但是相對的也失去了共用屬性的便利性。

這種方式也能讓我們很方便的實作多重繼承,只要在子類別的建構函數中呼叫多個父類別的建構函式即可。

2013年2月19日 星期二

[SQL] SQL tuning 奇技淫巧 (in Oracle)

雖然真的很討厭SQL,但是只要從事web開發的多多少少都會碰到,實在是無法避免。最近碰到了SQL從一個table取資料去update另一個table的效能問題,最初的寫法是這樣的:
UPDATE NEW_DATA N
SET N.A_COLUMN =  (   
    SELECT O.A_COLUMN
    FROM OLD_DATA O
    WHERE O.ID = N.ID) 
)
這樣的寫法跑了大概半個小時都還沒跑完(一共4萬多筆資料),最後受不了把他停下來,改成以下寫法:
UPDATE (
    SELECT  N.A_COLUMN NEW_A, 
            O.A_COLUMN OLD_A
      FROM  NEW_DATA N, OLD_DATA O
     WHERE  O.ID = N.ID
)
SET NEW_A = OLD_A
結果不到兩分鐘就跑完了~原因是第二種寫法會先將兩個table依select條件join成一個暫存的view,這個view再用自己的資料對自己做update,而不是像第一種寫法一樣每寫一筆資料之前都要先查詢一次。

Partition

Select ID, GROUP
    From (
        Select  ID,
                GROUP,
                Row_Number() Over (Partition By GROUP Order By Abs(ID) Desc) Sequence
                -- 針對同一個GROUP的資料依ID大小排序,並給定序號
        From DATA
    ) TEMP
WHERE TEMP.SEQUENCE = 1
-- 只取出每個GROUP最大ID的資料

2013年2月16日 星期六

[JS] function - 函數物件

前言

javascript引擎要執行javascript時,會先"預編譯",即對所有宣告的變數及函式進行處理,但要注意的是,在函數中被宣告的local變數只有在函式被呼叫時才會被處理,函數return之後這些變數就會被釋放。


函數的定義

函數有三種定義方式:
1. 以function宣告(ex. function abc(){})
2. 匿名函數(ex.function(){})
3. Function建構函數(ex. var fn = new Function("return true;"))
前面有提到在預編譯時會先處理被宣告的函數及變數,所以第一種的宣告方式或是將匿名函數指定給一個變數時....
var fn = function(){/*Do something*/};
會在預編譯時期先被處理,但在直接執行匿名函數....
(function(){/*Do something*/})();
或是用Function建構函數來建立函數時都會在執行到該行程式碼的時候才會被處理。因為這樣的特性使得我們能用Function來比較動態的宣告函數,但因為必須以字串方式傳入函式的敘述內容(函式中要執行的程式碼),所以相對不好維護,而且因為是動態編譯的,效率也會較差,我們可以用一段程式碼來證明:
var startTime = (new Date()).getTime();
for(var i=0; i<100000; i++){
    function fn(){}
}
var total = (new Date()).getTime() - startTime;
alert("function*100,000 takes "+total); // 62 secs

var startTime = (new Date()).getTime();
for(var i=0; i<100000; i++){
    new Function();
}
var total = (new Date()).getTime() - startTime; // 18991 secs
alert("new Function*100,000 takes "+total);


函數的作用域(scope)

就像上一段所說,因為宣告function會在預編譯時期就被處理,所以函數的作用域是取決於宣告時所在的位置,立即執行函式(IIFE,直接執行匿名函數)也是。但是以Function建構函式所建立的函數作用域是Global。
var a = 1;
function fn(){
    var a=2;
    var fn1 = function(){alert(a);}
    var fn2 = new Function("alert(a)");
    fn1(); //"2"
    fn2(); //"1"
}
fn();


arguments物件

在function中可以使用arguments這個array物件,其中包含了所有被傳入此函數的參數,即使這個函數並不需要任何的輸入,例:
function fn(){
    for(var i in arguments){alert(arguments[i]);}
}
fn(1,2,3,4,5,6,7,8,9);
順帶一提,要如何取得一個函數預定接收幾個參數呢?可以使用function.length


.call() & .apply()

之前剛好寫了一篇關於這兩個方法的文章。
這兩個方法能讓function很靈活的視作不同object的屬性來使用,切換不同的scope,用完之後馬上消失,不會真的成為該object的屬性。


別忘了function物件也有屬性

function也是個物件,當然也有自己的屬性。下面的這段程式碼想用迴圈來依次輸出1~10,但這樣的寫法用到了全域變數,缺乏封閉性。
function fn(){
    return x++;
} 
var x = 1;
for(var i=1; i<=10; i++){
    alert(fn());
}
所以我們可以利用函數自己的屬性來暫存這個值
function fn(){
    return fn.x++;
} 
fn.x = 1;
for(var i=1; i<=10; i++){
    alert(fn());
}


閉包(Closure)

在C、JAVA等語言中,當呼叫函數並傳回後,所有的區域變數就會被釋放,即刪除他們所在的堆疊。但在Javascript裡,如果宣告了一個內嵌函數,區域變數將在函數傳回後仍能被存取。我們可以利用這種特性來設計具有封閉及包裹性的閉包架構:
var mod = (function(){
    var d=0;
    var foo=function(){return d;};
    return foo;
})();
alert(mod()); // "0",還是存取得到閉包中的變數
關於Javascript的模組化可看一下之前的這篇文章。


2013年2月12日 星期二

[JS] deferred.promise()的應用

今天在看jQuery的.animation()程式碼的時候注意到的。

之前就有稍微研究一下jQuery的deferred物件,今天看到他被用在animation的程式碼裡面,而且是我之前沒學過的用法,於是在這邊筆記一下。
var obj = { speak: function(name){alert(name);} }
    deferred = $.Deferred();
deferred.promise(obj);
obj.done(function(name){obj.speak(name)});
deferred.resolve("Kevin");

After being promised, the object would would be extended with some of the deferred object's function like "done", "fail", "always".

As the above, though the promised object can't resolve it self, but the deferred object who promise it can. And it can even pass an argument on resolving or rejecting it.

The deferred object really helps dealing with asyncronise problems in javascript.


2013年2月11日 星期一

[前端] Responsive Web Design - 自適應網頁設計技巧整理

自己在開發網站前端時往往會希望在任何device上的UX都能呈現到最好,又不想為每種顯示模式特別再寫一份layout,就開始研究RWD,畢竟很多實驗性或是新創網站是沒那麼多成本去特別寫一個mobile版的。

"Responsive Web Design",或"自適應網頁設計"其實比較像是個設計概念,而不是什麼特定技術,但是在實踐他的過程中我們還是需要一些小技巧,以下是到目前為止累積的一點小心得:


1. CSS: width:X%

一般在設計網頁常用的兩欄式、三欄式設計,往往是用固定的欄寬來對side area做定義,但當螢幕寬度小於網頁的總寬度的話就會出現橫向的scroll-bar,這是在行動裝置上所不樂見的,最好的方法是利用比例來做切割,例如1:3的兩欄式就可以利用個別設定他們的寬度為"25%"、"75%"
(但事實上你往往會需要為他們留一點margin,所以比較實際的配置應該是25:1:74之類的)
Fluid 960 Grid、Bootstrap fluid layout 等都是可以拿來現成使用的樣板。


2. CSS: max-width: X%

雖然直接用比例來定義欄位或container的寬度十分方便,但有時候會遇上「寬度100%在小螢幕剛剛好,但在寬螢幕上看就是大的很奇怪」或是「寬度60%在大螢幕剛剛好,但在小螢幕就太小」的這種尷尬情況。這種時候可以用接下來會講到的Media Query來解決,但畢竟要多寫一種css配置實在有點麻煩,畢竟有的時候這只是局部性的小配置罷了。這種令人尷尬的小壞蛋只要用max-width就可以輕鬆的切換寬/窄螢幕下適合的寬度設定!解決的方式如下:
.ding-dong-box{
    width: 480px;
    max-width: 100%;
}
這樣一來只要這個box的container的寬度大於480px,ding-dong-box會維持原樣,而當縮到比480px窄的時候,ding-dong-box也會跟著一起縮小。實際應用可以參看這個網頁的試算表格。


3. CSS: Media Query

Media Query就是讓瀏覽器判定目前的devise寬度套用不同的css設定,很多前輩都已經寫得很棒了,小弟在這邊就恭敬的附上幾篇連結:
用CSS3的Media Queries進行自適應網頁設計(Responsive Web Design)
Mobile Web 前端技術筆記(二): Media Queries 與 CSS
比較要注意的是,在行動裝置上的配置不能用@media  screen and (max-width: 480px)來達成,
針對行動裝置的偵測必須寫成:@media screen and (max-device-width: 480px)。
而Media Query 的最終目的就是要讓layout能在大型裝置的橫向配置和行動裝置的縱向配製作切換。至於這些配置間的不同,可以看這篇文章的介紹:"Responsive Design with CSS3 Media Queries" 或是參考 "Bootstrap fluid layout" 這個template,總之概括來說就是要讓兩欄(ex. 30:70)或三欄(ex. 20:60:20)這種配置可以在行動裝置上切換成縱向的配置(每個區塊的寬度都是100%)


4. align="center" VS style="margin: 0 auto;"

很常見的水平置中問題,除了在外面套一層align屬性為center的container以外,你也可以設定margin為"0 auto",利用margin來平均補齊左右空間達到水平置中的結果。
總而言之,拜託不要再用<center></center>了.....


5. Javascript

放大絕,特殊情形不知道該怎麼解決的時候就用$(window).resize();和$("").css()吧!