Wednesday, December 27, 2006

Ruby (3)

کد بلاک ها:

کد بلاک ها یکی از ساختارهای مهم Ruby هستند. کد بلاک قطعه ای کد هست که می تواند به عنوان یک آرگومان به یک تابع فرستاده شود. بسیاری از زبان ها ساختارهایی مشابه کد بلاک دارند، در جاوا Anonymous Inner Class در سی شارپ Delegate در C و C++ اشاره گر به توابع به جای کد بلاک ها استفاده می شود (البته این ساختارها خیلی با هم فرق می کنند اما کم و بیش برای یک هدف به کار می روند) . ساده ترین کاربردی که برای استفاده از کد بلاک ها می توان تصور کرد، Action Listener ها در واسط گرافیکی کاربر است. معمولا وقتی کاربر بر روی یک دکمه کلیک می کند می خواهیم کد دلخواه ما اجرا شود.

برای درک کد بلاک ها یک مثال می تواند کمک زیادی بکند، قطعه کد زیر یک تابع به نام one_to_ten تعریف می کند که یک کد بلاک را به عنوان آرگومان می گیرد. برای ارسال یک کد بلاک به یک تابع لازم نیست که کد بلاک بصورت صریح به عنوان آرگومان تابع بیان شود(گرچه می توان بصورت صریح نیز این کار را انجام داد)، بلکه هنگامی که از کلمه کلیدی yield در تابعی استفاده کنیم، مفسر Ruby بصورت ضمنی کد بلاکی را به عنوان آخرین آرگومان تابع در نظر می گیرد. مقادیری که در جلوی yield قرار می گیرند به عنوان آرگومان به کد بلاک ارسال خواهند شد.

def one_to_ten

for i in 1..10

yield i

end

end

one_to_ten { |arg| puts arg }

# result : 1 2 3 4 5 6 7 8 9 10

فراخوانی تابع one_to_ten باید شامل کد بلاکی باشد که به آن ارسال می شود. در این مثال کد بلاک ارسال شده آرگومانی با نام arg دارد و تنها کاری که انجام می دهد چاپ مقدار آرگومان است.

کد بلاک ها در Ruby بطور گسترده ای مورد استفاده قرار می گیرند. تقریبا همه کلاس های Ruby به شکلی از کد بلاک ها استفاده می کنند. برای مثال کلاس Array (و بسیاری دیگر از کلاس ها) متدی به نام each دارند که معمولا به جای حلقه foreach در دیگر زبان ها مورد استفاده قرار می گیرد. برای مثال قطعه کد زیر مجموع اعداد موجود در آرایه a را محاسبه می کند:

a = [1, 4, 3, 2]

s = 0

a.each {|i| s = s + i }

puts s # 10

همان طور که مشاهده می کنید کد بلاک ها می توانند به متغیر های خارج از کد بلاک نیز دسترسی داشته باشند و آنها را تغییر دهند. آرایه ها در Ruby متد های جالب دیگری نیز دارند ولی در اینجا قصد ندارم آنها را توضیح دهم، فقط به عنوان یک مثال دیگر، قطعه کد زیر را در نظر بگیرید، خط اول آن حاصل ضرب عناصر آرایه a و خط دوم حاصل جمع آن ها را محاسبه می کند، خط سوم تمام عناصر آرایه را به توان 2 می رساند و در آرایه جدیدی بر می گرداند و بلاخره خط چهارم تمام عناصر آرایه را که مقدار کوچکتر یا مساوی 3 دارند حذف می کند و نتیجه را در آرایه جدیدی بر می گرداند.

puts a.inject(1) { |mult, i| i * mult } # 24

puts a.inject(0) { |sum, i| i + sum } # 10

puts a.collect { |x| x * x } # [1, 16, 9, 4]

puts a.delete_if { |i| i <= 3 } # [4]

کلاس ها و اشیاء:

همان طور که پیش از این گفتم، Ruby یک زبان کاملا شیء گرا است. قطعه کد زیر کلاسی با نام Student تعریف می کند و سپس نمونه ای از کلاس درست کرده و از آن استفاده می کند. کلاس ها در Ruby با کلمه کلیدی class و متد ها با کلمه کلیدی def تعریف می شوند. سازنده کلاس در این زبان همیشه نام initialize را دارد. در Ruby نیازی به تعریف فیلد ها درون کلاس نداریم و با قرار دادهایی محدوده(scope) متغیر ها تعیین می شود. متغیر های سراسری با $، متغیر های استاتیک با @@ و فیلدهای کلاس با @ شروع می شود. متغیرهای محلی پیشوندی ندارند و ثابت ها(constants) با حروف بزرگ شروع می شوند.

class Student

def initialize(name, age, id)

@name, @age, @uid = name, age, id

@lessons = []

end

def << (lsn)

@lessons << lsn

end

def get_name

@name

end

def to_s

"#{@name}(id:#{@uid}, age:#{@age}, lessons:[#{@lessons.join(', ')}])"

end

end

stu = Student.new('mohsen', 23, 1)

stu << 'Math'

stu << 'Data Structure'

stu << 'OS'

puts stu

در Ruby همیشه آخرین خط یک متد به عنوان مقدار بازگشتی در نظر گرفته می شود(گرچه کلمه کلیدی return هم برای بازگرداندن مقدار از متد وجود دارد). در Ruby با عملگرها دقیقا مثل متد ها رفتار می شود(برای مثال stu<<'math' دقیقا مثل فراخوانی stu.<<('math') در نظر گرفته می شود) به همین دلیل عملگرها نیز می توانند به راحتی بصورت یک متد از کلاس تعریف شوند.

کلاس های Ruby در مقابل تغییرات باز هستند به همین دلیل کاربر می تواند به کلاس هایی که قبلا نوشته و حتی کلاس های کتابخانه این زبان متد ها و فیلد هایی را اضافه کند یا آن ها را override کند(بدون اینکه نیازی به ارث بری داشته باشد). این قابلیت خیلی قدرتمندی است که در بسیاری دیگر از زبان ها وجود ندارد. برای مثال قطعه کد زیر متدی به نام to_my_string به کلاس Array که یک کلاس از کتابخانه استاندارد Ruby هست اضافه می کند.

class Array

def to_my_string

# negative indices count from the end of the string

# no return keyword needed, always last line is returned automatically

str = '{'

each{|a| str += "#{a},"}

str[-1] = '}'

str

end

end

a = ['X', '1', '2', '3']

puts a.to_my_string # {X,1,2,3}

در Ruby حتی می توان متدی به یک شیء اضافه کرد یا یکی از متد های یک شیء را override کرد! برای مثال قطعه کد زیر متدی با نام hello به متغیر a اضافه می کند.

a = 'Mohsen'

def a.hello

'hello ' + self

end

puts a.hello # hello Mohsen

این قابلیت Ruby شاید در ابتدا عجیب و یا بدون استفاده به نظر برسد ولی این ویژگی نیز قابلیت بسیار قدرتمندی محسوب می شود. برای پی بردن به این موضوع فرض کنید می خواهیم در برنامه یک connection به پایگاه داده بسازیم که قابلیت بستن نداشته باشد(یعنی فراخوانی متد close باعث بسته شدن آن نشود). این مسئله را قبلا به زبان جاوا مطرح کرده ام و پاسخ آن را هم آورده ام. در جاوا برای اینکه تنها رفتار متد close از یک شیء connection را تغییر دهیم لازم است کد بسیار زیادی نوشته شود(delegate کردن تقریبا 50 متد از کلاس Connection، گرچه این کد را eclipse به آسانی تولید می کند و زحمت زیادی برای ما ندارد، ولی در هر حال حجم زیادی از کد برای این کار لازم است). در Ruby راه حل این مسئله می تواند خیلی ساده تر باشد:

$singleConnection = create_a_connection()

def $ singleConnection.close

# do nothing !

end

$ singleConnection.close() # result : do nothing!

در مجموع ویژگی هایی که در این بخش به آن پرداخته شد(کد بلاک ها، قرار داد های نام گزاری متغیر ها، باز بودن کلاس ها و اشیاء در مقابل تغییر و ...) تا حد بسیار زیادی می تواند حجم کد نویسی را در Ruby نسبت به دیگر زبان ها کاهش دهد.

Monday, December 25, 2006

How to Create a Single Non Closable JDBC Connection

When using embedded databases (e.g. Apache Derby in embedded mode) this is a good idea to have just one connection to the db, because multiple connections to a single db in embedded mode usually cause deadlocks or runtime exceptions. To create a single connection we can use a static method which returns a single connection every time called, but the problem is that when the close method is called on the connection everything is destroyed! To solve this problem I have used a mixture of Singleton and Proxy design patterns, here is the code:

import java.sql.*;

import java.util.*;

public class SingleConnection implements Connection {

private Connection connection;

private static SingleConnection instance = new SingleConnection();

public static SingleConnection getInstance() {

return instance;

}

private SingleConnection() {

try {

Class.forName("org.apache.derby.jdbc.EmbeddedDriver");

String connectionString = "jdbc:derby:testdb;create=true";

connection = DriverManager.getConnection(connectionString);

} catch (Exception e) {

throw new RuntimeException(e);

}

}

public void close() throws SQLException {

// throw new RuntimeException("Not Allowed to Close Connection");

}

public void clearWarnings() throws SQLException {

connection.clearWarnings();

}

// delegate to rest of methods ...

}

SingleConnection implements the Connection interface and wraps an instance of connection, every call to SingleConnection is delegated to actual connection instance (proxy), except close which runs a custom code(does nothing!). The constructor is made private and an accessor method is added to return the single instance of SingleConnection (Singleton pattern).

Wednesday, December 20, 2006

Ruby (2)

قبلا درباره Ruby و ویژگی های آن(بطور کلی) مطالبی نوشتم. در این قسمت قصد دارم جزئیات بیشتری را مطرح کنم. در این بخش بیشتر به این مسئله خواهم پرداخت که چرا کد نوشته شده به زبان Ruby خلاصه تر از کدی هست که به بسیاری از زبان های دیگر مانند جاوا، سی شارپ و C++ نوشته می شود. ساده ترین دلیلی که برای این امر می توان بیان کرد این است که Ruby چیز اضافه ای ندارد. این جمله در نگاه اول شاید عجیب به نظر برسد ولی در ادامه سعی می کنم منظور خودم را واضح تر بیان کنم. بسیاری از ویژگیهایی که در اینجا بیان شده می توانند مزایا و معایبی داشته باشند ولی در هر حال هدف اصلی در Ruby افزایش Productivity بوده است. هنر طراحان Ruby در این بوده که در عین افزایش Productivity توانسته اند تا حد زیادی دیگر ویژگی های مثبت زبان مانند قابلیت نگهداری(Maintainability)، مقیاس پذیری (بیشتر منظورم مقیاس پذیری در جهت انجام پروژه های بزرگ و با حجم زیاد کد است تا مقیاس پذیری از انواع دیگر که یک مسئله پیاده سازی است) و وضوح کد را حفظ کنند. در ادامه گرچه به برخی از ویژگی های Ruby پرداخته ام ولی قصدم آموزش این زبان نیست.

انواع داینامیک:

همان طور که قبلا اشاره کردم در Ruby نوع(Type) متغیر ها داینامیک هستند و در زمان اجرا مشخص می شوند(و البته داینامیک بودن به معنی weakly typed بودن نیست). این امر باعث می شود نیازی به تعریف نوع متغیر ها و مقدار بازگشتی توابع وجود نداشته باشد. از طرفی در زبان های کامپایلری بسیاری از کد ها صرفا بخاطر توجیه کامپایلر نوشته می شود! وقتی با Ruby کار می کنید کم کم به این نتیجه می رسید که تعداد کلاس هایی که برای تولید یک برنامه باید بنویسید بسیار کمتر از تعداد کلاس های نوشته شده در بسیاری دیگر از زبان ها است. برای مثال بسیاری از Super Class ها و متد هایی که Overload شده در Ruby مورد نیاز نیست. حتی در Ruby چیزی با نام Overload کردن متد وجود ندارد.

یکی دیگر از ساختار هایی که به نظر من برای توجیه کامپایلر ایجاد شده، همان چیزی است که در Java 5 و C# 2 به آن Generics و در C++ به آن Templates می گویند(گرچه این ساختارها در سه زبان ذکر شده تفاوت زیادی با هم دارند ولی هر سه تقریبا به یک منظور در زبان گنجانده شده اند). در زبان های داینامیک (مانند Ruby) اصلا نیازی به چنین ساختاری وجود ندارد.

قبلا به این مسئله اشاره کردم که تصمیم گیری در مورد بسیاری از ویژگی های زبان یک tradeoff را تحمیل می کند و هر مزیتی می تواند عیبی را نیز به همراه داشته باشد. یکی از مشکلات عمده ای که زبان های داینامیک(و از جمله آنها Ruby) دارند این است که چون اطلاعات نوع ها در زمان توسعه نرم افزار موجود نیست، به همین خاطر عموما IDE های این زبان ها نمی توانند به اندازه IDE های زبان های کامپایلری مانند Java و C# به برنامه نویس کمک کنند. البته از آنجایی که IDE های زبان Ruby هنوز به اندازه ابزار های مشابه خود در Java و C# توسعه نیافته و به تکامل نرسیده اند شاید این مقایسه درست نباشد ولی در هر حال حداقل در ابتدای کار دل کندن از IDE های قدرتمندی مثل eclipse و intellij برای یک برنامه نویس جاوا و Visual Studio.NET برای کسانی که تحت پلت فرم .NET کار می کنند سخت است.

انواع داده توکار:

Ruby دارای انواع داده توکار (build in) زیر است:

· String: مانند بسیاری دیگر از زبان ها، رشته ها در Ruby جزء انواع توکار هستند. ولی رشته ها در Ruby نسبت به بسیاری دیگر از زبان ها از قدرت بیشتری برخوردار هستند. برای مثال رشته هایی که داخل double quotation قرار می گیرند، می توانند شامل متغیر ها و یا عبارات نیز باشند(همانند Perl)

a = 10

puts "#{a} plus one is #{a + 1}"

#10 plus one is 11

· Fixnum, Float: بیانگر اعداد اعشاری و صحیح هستند. نکته جالب در مورد اعداد در Ruby این است که در حالت عادی اعداد به اندازه word کامپیوتری هستند که برنامه روی آن اجرا می شود ولی اگر اندازه اعداد از اندازه word کامپیوتر بزرگتر شود(overflowRuby بطور خودکار اندازه اعداد را بزرگ کرده و محاسبات لازم را نیز خود انجام می دهد. به همین خاطر هیچ وقت در Ruby سرریز محاسباتی رخ نمی دهد.

· Range: Ruby نوع داده توکاری دارد که در دیگر زبان ها خیلی متداول نیست. 1..10 دنباله اعداد 1 تا 10 را نشان می دهد و 'a'..'c' دنباله ای از کاراکترهای a تا c است. Range ها نیز مانند اعداد در Ruby شیء هستند. قطعه کد زیر فاکتوریل عدد 50 را تولید می کند. دقت کنید که عدد تولید شده بزرگ تر از آن است که در word کامپیوتر ذخیره شود.

n = 50

fact = 1

(2..n).each {|i| fact = fact * i}

puts fact

#30414093201713378043612608166064768844377641568960512000000000000

· آرایه ها: آرایه ها یکی دیگر از انواع داده توکار در Ruby هستند. آرایه های Ruby می توانند انواع مختلفی را در خود جای دهند و در هنگام نیاز بطور خودکار اندازه آنها افزایش پیدا می کند.

a = [1, 2, 0]

a << 3

a = a.sort

a.each{|i| puts i}

#0 1 2 3

· Hash: نوع داده Hash در Ruby مشابه Hashtable یا Dictionary در بسیاری دیگر از زبان ها است. پشتیبانی توکار Ruby از این نوع بسیاری از کارها را آسان کرده است.

map = {'one'=>1, 'two'=>2}

map['three'] = 3

map.each{|key, val| puts "#{key} = #{val}"}

#three = 3

#two = 2

#one = 1