Article:
10 điều LTV Java cần biết về Ruby
3453
dangtrieu.myopenid.com 5Updated over 4 years ago |
“Ruby là một ngôn ngữ mà tôi tin rằng sẽ thay đổi tư duy lập trình của bạn.”—javaeater
Trước hết đây không phải là bài viết so sánh kiểu “Ruby hay hơn, tốt hơn Java”, như đã từng được bàn luận sôi nổi trên diễn đàn Java Vietnam. Trái lại, mục đích chính của bài viết là giúp LTV Java đang học Ruby tiếp cận nhanh hơn ngôn ngữ này, thông qua một số cách như so sánh điểm tương đồng giữa 2 ngôn ngữ. Nếu chưa học Ruby, với căn bản Java chắc bạn cũng hiểu được đa phần bài viết này.
Đây là bản chuyển ngữ tiếng Việt, đã được điều chỉnh cho dễ hiểu đối với lập trình viên Việt Nam. Bản gốc ở đây.
Điều 10: Qui cách viết chương trình Ruby
Mỗi ngôn ngữ đều có một số qui cách viết chương trình như cách đặt tên hàm, tên biến… mà qua đó các LTV có kinh nghiệm chỉ cần nhìn sơ qua là có thể đoán biết chức năng của các hàm, biến này là gì.
Đây là một số qui cách (conventions) trong Ruby. Có một số là bắt buộc và một số chỉ là qui định chung trong cộng đồng Ruby.- Các từ trong tên lớp (class) viết hoa và không có dấu ngăn cách.
- Các hàm trong class viết thường và các từ cách nhau bằng dấu gạch dưới.
- Các biến có cách đặt tên tương tự như tên hàm.
methods_asking_a_question?: Các hàm có tính query, trả về true hoặc false.slightly_dangerous_methods!: Các hàm có thể gây “nguy hiểm”.@instance_variables: Các biến riêng.$global_variables: Các biến chung, biến toàn cục.SOME_CONSTANTSorOtherConstants: Các hằng.
Điều 9: Mọi thứ trong Ruby đều là các Object (hay đối tượng)
Trong đa số các ngôn ngữ, các kiểu như integer hay double không phải là các object, còn trong Ruby gần như tất cả đều là object. Thế nên chúng ta có vài điều thú vị thế này:
+ Tạo một Array: Array là một tên hằng gắn với lớp (class) Array. Việc tạo một object mới không cần đòi hỏi một cú pháp nào đặc biệt, chỉ cần gọi hàm new của object ấy:
a = Array.new
+ Sản xuất object: Vì các object có thể “tự sinh sản”, nên nó chính là kiểu object công xưởng (factory). Chúng ta hãy làm thử một xường sản xuất nhỏ thế này:
def create_from_factory(factory)
factory.new
end
obj = create_from_factory(Array)
+ Làm đủ thứ với integer: Ngay cả số integer cũng là một object đầy đủ. Nên bạn có thể viết thế này:
0.zero? # => true
1.zero? # => false
1.abs # => 1
-1.abs # => 1
1.methods # => xem danh sách các hàm của object 1
2.+(3) # => 5 (giống như 2+3)
10.class # => Fixnum
(10**100).class # => Bignum
+ nil là một object: Trong Java null không có nghĩa là “không có tham chiếu tới” object đó, nếu bạn cố gọi hàm từ một null object thì sẽ gặp lỗi. Trong Ruby, nil cũng là một object, nên bạn sẽ không bao giời gặp lỗi “null pointer error”.
a = nil
a.nil? # => true
a.methods # => danh sách các hàm của nil
+ Nhưng lưu ý: Tên biến không phải là object. Bạn không thể có một biến trỏ tới một biến khác. (Tuy có cách đi vòng để thực hiện điều này nhưng gần như không có ai cần tới nó trong Ruby).
Điều 8: (Hầu hết) Mọi hoạt động đều thông qua các message.
Mọi quá trình tính toán trong Ruby đều trải qua các bước:- Gán tên vào các object.
- Chạy các cấu trúc logic (như
if/else, while) hay các toán tử (như.defined?). - Gửi các message tới các object.
Đây là ví dụ về các message:
string.index("x")
Gửi :index (với đối số "x")
string.length
Gửi :length (không có đối số)
run_status_reports
Gửi :run_status_reports (tới self)
1 + 2
Gửi :+ (với đối số 2) tới object 1
array[i]
Gửi :[] (với đối số i) tới array
Lưu ý: Các message trên (như :index) bắt đầu bằng dấu hai chấm, nghĩa là dấu hai chấm là một phần của message. Các message này trong Ruby gọi là symbol.
Với LTV Java thì obj.method() là tìm một hàm và chạy nó, còn trong Ruby obj.method có nghĩa là gửi một message đến object đó. Vậy sự khác biệt là gì? Đó không phải là sự khác biệt lớn nhưng rất quan trọng.
Chúng ta hãy xem một lớp có tên là VCR (Video Cassettes Recorder – tức là cái đầu máy video), lớp này ghi lại tất cả các message người ta gửi tới và chuyển chúng tới một object khác.
class VCR
def initialize
@messages = []
end
def method_missing(method, *args, &block)
@messages << [method, args, block]
end
def play_back_to(obj)
@messages.each do |method, args, block|
obj.send(method, *args, &block)
end
end
end
Rồi cho chạy thế này:
vcr = VCR.new
vcr.sub!(/Java/) { "Ruby" }
vcr.upcase!
vcr[11,5] = "Universe"
vcr << "!"
string = "Hello Java World"
puts string
vcr.play_back_to(string)
puts string
Kết quả sẽ như sau:
Hello Java World
HELLO RUBY Universe!
Hiểu về message sẽ giúp bạn làm được nhiều điều thú vị.
Điều 7: Ruby linh hoạt hơn là bạn tưởng
Một trong những điều làm cho Java hấp dẫn hơn C++ là linh hoạt (dynamic) của ngôn ngữ này. Bạn có thể (trong Java) tải các lớp lúc chạy, tìm các lớp và hàm của một object và thậm chí gọi các hàm mà bạn vừa mới được biết đó (xin đọc thêm về Reflection của Java).
Nhưng tính linh hoạt của Ruby còn hơn Java vài bậc. Ví dụ:
Reflection dễ dàng hơn
+ Tạo object với Java reflection:
public static Object create(Class c, String value)
throws Exception
{
Constructor ctor = c.getConstructor(
new Class[] { String.class } );
return ctor.newInstance(
new Object[] { "Hello" } );
}
public static void main (String args[])
throws Exception
{
Greeting g = (Greeting) create(
Greeting.class, "Hello");
g.show();
}
+ Còn đây là cách làm với Ruby:
def create(klass, value)
klass.new(value)
end
g = create(Greeting, "Hello")
g.show
Lớp mở
Có thể đưa một hàm vào object bất kỳ lúc nào, ngay cả với những object dạng built-in như Integer:
class Integer
def even?
(self % 2) == 0
end
end
p (1..10).select { |n| n.even? }
# => [2, 4, 6, 8, 10]
Hàm singleton
Hàm singleton có thể định nghĩa ở cấp object (thay vì cấp class như Java).
class Dog
end
rover = Dog.new
fido = Dog.new
def rover.speak
puts "Red Rover"
end
rover.speak # => "Red Rover"
fido.speak # => NoMethodError
Hooks
Hook (cái móc câu) cho phép “móc” vào để nắm quyền điều khiển chương trình lúc nào bạn thích. Có thể làm thế này trong Ruby.
class MyClass
def MyClass.method_added(name)
puts "Adding Method #{name}"
end
def new_method
# Yada yada yada
end
end
Kết quả là:
Adding Method new_method
Code Eval
Viết code (mà code này) có thể tự lượng giá (evaluate) chính mình:
class Module
def trace_attr(sym)
self.module_eval %{
def #{sym}
printf "Accessing %s with value %s\n",
"#{sym}", @#{sym}.inspect
@#{sym}
end
}
end
end
class Dog
trace_attr :name
def initialize(string)
@name = string
end
end
Dog.new("Fido").name # => Accessing name with value "Fido"
Điều 6: Object Ruby là strongly-typed, không phải là statically-typed
Nói chung trong Ruby việc khai báo kiểu, ép kiểu không bao giờ là vấn đề.
+ Đây là 1 hàm tính giai thừa (fractorial) của một số viết bằng Java:
public class Fact {
static long factorial(long n) {
long result = 1;
for (long i=2; i<=n; i++)
result *= i;
return result;
}
public static
void main (String args[]) {
System.out.println(factorial(2));
System.out.println(factorial(21));
}
}
Khi chạy cho kết quả là:
2
-4249290049419214848
Kết quả giai thừa của 21 sai vì kết quả vượt quá khả năng chứa của kiểu long.
+ Còn của Ruby:
def factorial(n)
result = 1
(2..n).each do |i|
result *= i
end
result
end
x = factorial(2)
y = factorial(21)
puts x
puts y
puts x.class
puts y.class
Cho kết quả là:
2
51090942171709440000
Fixnum
Bignum
Điều 5: Bạn không phải quan tâm tới interface trong Ruby
Trong Ruby có một nguyên tắt thế này “Nếu vật gì (object) có thể đi như vịt và kêu như vịt thì bạn có thể coi nó là vịt. Đừng quan tâm tâm nó thực sự là gì”.
Xin xem đoạn code sau:
class Duck
def talk() puts "Quack" end
end
class DuckLikeObject
def talk() puts "Kwak" end
end
flock = [
Duck.new,
DuckLikeObject.new ]
flock.each do |d| d.talk
Như bạn thấy bạn có thể “điểm mặt” từng con vịt trong đàn (flock) và bắt nó kêu “cạc” hay “kẹc” tùy theo đó là giống vịt nào, miễn là trong object đó có hàm talk(). Trong Java bạn sẽ phải tạo một interface có hàm talk() mà tất cả các loại vịt muốn kêu được sẽ phải implement cái interface này.
Điều 4: Trộn code bằng Mix-in
Mặc dù trong Ruby không có interface nhưng có thể định nghĩa các mix-in ở cấp Module. Vậy module là gì?
- Module giống như một namespace.
- Nó có thể có các hàm (giống như class).
- Nhưng không giống như class nó không thể có instance.
- Và bạn có thể trộn module vào class. Khi ấy các hàm trong module sẽ trở thành các hàm trong class.
Chúng ta sẽ xem hàm này, nó có các biểu thức để so sánh hai object cùng loại:
class Pair
attr_accessor :first, :second
# ...
def <(other)
(first < other.first) ||(first == other.first && second < other.second)
end
def >(other)
other < self
end
# Other methods defined in terms of less than:
# <=, >=, ==
end
Nhưng nếu có 1 hàm thường được dùng bạn có thể định nghĩa trong module và trộn vào hàm này như sau:
module ComparableUsingLess
def >(other)
other < self
end
# Other methods defined in terms of less than:
# <=, >=, ==
end
class Pair
include ComparableUsingLess
attr_accessor :first, :second
# ...
def <(other)
(first < other.first) ||
end(first == other.first && second <other.second)
end
Điều 3: Tính bao đóng khép kín (embrace closures)
Tính bao đóng trong Ruby rất kín. Ví dụ:
Duyệt danh sách:
.each do |item| puts item end
Đọc nội dung file:
file_contents = open(file_name) { |f| f.read }
Phản hồi:
widget.on_button_press { puts “Got Button Press” }
Điều 2: ri và irb là những người bạn của Ruby
ri -> là lệnh dùng để xem thông tin của các object Ruby chuẩn. irb -> là tiện ích console để trực tiếp gõ lệnh và biên dịch tức thời.
Ví dụ: xem thông tin về Array:
$ ri Array
---------------------------------------------------------- Module: Array
Arrays are ordered, integer-indexed collections of any object.
Array indexing starts at 0, as in C or Java. A negative index is
assumed to be relative to the end of the array---that is, an index
of -1 indicates the last element of the array, -2 is the next to
last element in the array, and so on.
------------------------------------------------------------------------
Includes:
---------
Enumerable(all?, any?, collect, detect, each_with_index, entries,
find, find_all, grep, include?, inject, map, max, member?, min,
partition, reject, select, sort, sort_by, to_a, zip)
Class methods:
--------------
[], new
Instance methods:
-----------------
&, *, +, -, <<, <=>, ==, [], []=, assoc, at, clear, collect,
collect!, compact, compact!, concat, delete, delete_at, delete_if,
each, each_index, empty?, eql?, fetch, fill, first, flatten,
flatten!, frozen?, hash, include?, index, indexes, indices, insert,
inspect, join, last, length, map, map!, nitems, pack, pop, push,
rassoc, reject, reject!, replace, reverse, reverse!, reverse_each,
rindex, select, shift, slice, slice!, sort, sort!, to_a, to_ary,
to_s, transpose, uniq, uniq!, unshift, values_at, zip, |
Hay hỏi về hàm last của Array:
$ ri Array#last
------------------------------------------------------------- Array#last
array.last => obj or nil
array.last(n) => an_array
------------------------------------------------------------------------
Returns the last element(s) of _self_. If the array is empty, the
first form returns +nil+.
[ "w", "x", "y", "z" ].last #=> "z"
Tất cả lệnh này và các lệnh khác đều có thể thực hiện trên irb.
Điều 1: Không cần phải viết nhiều code
Nói chung với Ruby bạn sẽ phải viết ít code hơn nhiều để giải quyết cùng một vấn đề so với các ngôn ngữ khác.
Những điều có thể bạn cần biết khác:
- Các namespace (các class và module) không phụ thuộc vào các package.
- Có rất nhiều hàm built-in trong String.
- ”. (dot) khác với” (double colon).
- Có một dự án đưa Ruby vào Java: JRuby
- Có những điểm giống và khác nhau giữa một hàm static trong Java và một hàm trong class của Ruby.
- finally trong Java có tên ensure trong Ruby
- flexible quoting.
Ruby căn bản
5
over 4 years ago
over 4 years ago
over 4 years ago
Updated over 4 years ago