Traffic Count

GREENFOOT - Hướng đối tượng vui nhộn với Java

1. Làm quen với GREENFOOT

1.1  Greenfoot là gì?

Greenfoot là một môi trường phát triển phần mềm được thiết kế đặc biệt để học lập trình Java thông qua việc tạo ra các game và mô phỏng. Thay vì nhìn chằm chằm vào những dòng code khô khan, bạn sẽ thấy ngay kết quả trực quan: những con vật di chuyển, tàu vũ trụ nổ tung, con cua ăn giun

Giao diện chính của Greenfoot gồm 3 vùng chính:

  • Thế giới (World) — vùng lớn nhất, nơi chương trình chạy và mọi thứ xảy ra trực quan
  • Sơ đồ lớp (Class Diagram) — vùng bên phải hiển thị cấu trúc các lớp của kịch bản
  • Nút điều khiển (Execution Controls) — nút Act, Run, Reset và thanh trượt tốc độ


Hình 1.1 — Giao diện chính của Greenfoot: World (trái), Class Diagram (phải), Execution Controls (dưới)

1.2  Object và Class — "Khuôn Bánh" và "Chiếc Bánh"

Class (Lớp) — là bản thiết kế, cái khuôn. Class Wombat mô tả khái niệm chung về con wombat — mọi thứ một con wombat có thể làm.

Object / Instance (Đối tượng) — là sản phẩm cụ thể tạo ra từ class. Mỗi con wombat thả vào thế giới là một object của class Wombat. Trong Greenfoot: right-click lên class → new Wombat() → kéo vào thế giới.

Hình 1.2 — Menu class

Khái niệm cốt lõi

Từ một class có thể tạo ra VÔ SỐ object.

"object" và "instance" là hai từ hoàn toàn đồng nghĩa trong lập trình.

Class = bản thiết kế chung. Object = sản phẩm riêng lẻ từ bản thiết kế đó.

1.3  Tương tác với Object — Gọi Method

Sau khi đặt object vào thế giới, right-click lên nó để thấy Object Menu. Menu hiển thị mọi hành động object có thể làm. Trong Java, các hành động này gọi là method (phương thức). Để ra lệnh cho object, ta invoke (gọi) method từ menu.


Hình 1.3. Menu của một Object

1.4  Return Type — Method trả về gì?

Mỗi method signature bắt đầu bằng return type — kiểu dữ liệu mà method trả về khi gọi:

  • void — "trống không", không trả về gì, chỉ thực hiện hành động (= mệnh lệnh)
  • boolean — trả về true hoặc false (= câu hỏi đúng/sai)
  • int — trả về số nguyên như 3, 42, -5 (= câu hỏi con số)

Hình 1.4 — Giải phẫu Method Signature: Return type | Tên method | Danh sách tham số

Mẹo nhớ nhanh

void = mệnh lệnh → "làm đi, không cần báo cáo kết quả"

 non-void = câu hỏi → "hỏi thì phải có giá trị trả lời"

 Ví dụ: move() là lệnh, canMove() là câu hỏi "mày đi được không?"

1.5  Parameter — Thêm thông tin cho Method

Cặp dấu ngoặc tròn () sau tên method chứa danh sách tham số (parameter list). Tham số là thông tin bổ sung mà method cần để chạy:

 // Không cần tham số — gọi thẳng:

move();

turnLeft();

// Cần tham số — phải cung cấp giá trị:

turn(5);                 // quay 5 độ

setDirection(2);    // đặt hướng về phía 2 (0-3)

Method Signature

 Toàn bộ mô tả method: return type + tên + danh sách tham số.

 Ví dụ: "void setDirection(int direction)" là một method signature đầy đủ.

 Nhìn vào signature biết ngay: làm gì, cần gì, trả về gì — không cần đọc code bên trong.

1.6  Điều khiển thực thi

 Act — Gọi act() của TẤT CẢ object một lần. Tiến từng bước một.

 Run — Gọi act() liên tục. Bấm Pause để dừng.

 Phân biệt: nút Act gọi act() của MỌI object; Object Menu chỉ ảnh hưởng một object duy nhất.

Khái niệm: Actor

 Object có thể đặt vào thế giới Greenfoot gọi là Actor.

 Wombat, Leaf, Rocket, Asteroid — tất cả là Actor.

 World (thế giới) không phải Actor; World là "sân chơi" chứa các Actor.

1.7  Sơ đồ lớp & Inheritance (Kế thừa)

Mũi tên trong sơ đồ lớp thể hiện quan hệ is-a (là một) — còn gọi là kế thừa (inheritance). Ví dụ trong game Asteroids:

Hình 1.5 — Sơ đồ lớp game Asteroids: mũi tên = quan hệ "là một dạng của" (is-a / inheritance)

Subclass là lớp chuyên biệt hóa của lớp khác. Rocket là subclass của Mover, còn Mover là subclass của Actor. Do đó Rocket cũng kế thừa mọi method từ Actor qua nhiều cấp!

2. Chương trình đầu tiên — LITTLE CRAB

2.1  Kịch bản Little Crab

Kịch bản little-crab có một con cua trên bãi biển.

Hình 2.1. Kịch bản con cua

Ban đầu chạy, cua không làm gì vì code trống:

 import greenfoot.*;

 /** Lớp định nghĩa con cua. Cua sống trên bãi biển. */

 public class Crab extends Animal

 {

  public void act()

  {

  // Thêm code hành động vào đây.

  }

 }

 Method act() là trái tim của mọi Actor trong Greenfoot. Mỗi khi bấm Act/Run, Greenfoot gọi act() của tất cả Actor. Phần code giữa hai dấu {} gọi là body (thân) của method.

2.2  Làm cua di chuyển

Thêm lệnh move(); vào body của act():

 public void act()

 {

  move();   // Di chuyển về phía trước một bước

 }

Method Call — Lời gọi phương thức

 move(); là một method call — lệnh ra cho object thực hiện hành động đã định nghĩa.

 Dấu () là phần bắt buộc của mọi method call.

 Dấu ; ở cuối mỗi lệnh là bắt buộc trong Java! Thiếu → lỗi compile ngay.

2.3  Thêm xoay — turn()

Method turn(int angle) nhận số độ cần quay (dương = theo kim đồng hồ, âm = ngược lại).

 public void act()

 {

  move();

  turn(5);    // Quay 5 độ mỗi bước → cua đi theo đường cong

 }

Sequence — Thứ tự thực thi

 Nhiều lệnh thực thi tuần tự từ trên xuống dưới, từng lệnh một.

 move() chạy TRƯỚC, rồi mới turn(5) — dù nhìn có vẻ đồng thời.

 Thứ tự lệnh ảnh hưởng trực tiếp đến hành vi chương trình!

2.4  Xử lý cạnh màn hình — if-statement

Cua bị kẹt khi đến cạnh màn hình. Dùng if-statement kết hợp method atWorldEdge() (kế thừa từ Animal) để cua tự quay đầu:

 public void act()

 {

 if ( atWorldEdge() )   // NẾU ở cạnh màn hình...

 {

 turn(17);          // ...THÌ quay 17 độ

 }

 move();                // Luôn luôn di chuyển

 }

Hình 2.2 — Luồng thực thi if-statement: turn(17) chỉ chạy khi atWorldEdge()==true; move() luôn chạy

Khái niệm: if-statement

Cú pháp: if (điều_kiện) { lệnh1; lệnh2; ... }

 Lệnh trong {} CHỈ thực thi khi điều kiện là true.

 Nếu false → bỏ qua hoàn toàn, tiếp tục lệnh tiếp theo bên ngoài if.

 Điều kiện phải là biểu thức trả về kiểu boolean.

3. Cua thông minh hơn — Lập trình nâng cao

3.1 Hành vi ngẫu nhiên

Con cua đi thẳng băng không tự nhiên. Thêm xác suất 10% mỗi bước cua quay ngẫu nhiên bằng Greenfoot.getRandomNumber(int limit) — trả về số ngẫu nhiên từ 0 đến (limit-1):

Dot Notation — Gọi Method từ class khác

 Khi method không nằm trong class hiện tại hay class cha, dùng dot notation:

  TênClass.tênMethod(tham_số)

 Ví dụ:  Greenfoot.getRandomNumber(100)

 Method static thuộc về class (không phải object cụ thể) → gọi bằng tên class.

 if ( Greenfoot.getRandomNumber(100) < 10 )   // 10% xác suất

 {

 turn( Greenfoot.getRandomNumber(90) - 45 );

 // Xoay ngẫu nhiên từ -45 đến +44 độ (trái hoặc phải)

 }

Tại sao < 10 là 10%?  getRandomNumber(100) cho số 0–99 đều nhau. Trong 100 số đó, có đúng 10 số nhỏ hơn 10 → xác suất = 10/100 = 10%.

3.2  Thêm Worm — Mồi cho cua!

Thêm class Worm bằng right-click lên class Animal → New subclass…. Đặt tên "Worm", chọn ảnh worm.png. Worm kế thừa Animal vì "a Worm IS AN Animal" — quan hệ is-a.

Hình 3.1. Tạo mới subclass

Hình 3.2. Tạo một class mới

3.3  Ăn Worm — canSee() và eat()

Class Animal cung cấp hai method để tương tác với object khác để khi cua thấy sâu (worm) sẽ lại ăn.

 boolean canSee( ... )

 // Trả về true nếu thấy worm

 

 void eat( ... )

 // Ăn (xóa) worm

 

 // Sử dụng

 if ( canSee(Worm.class) )

 {

  eat(Worm.class);

 }

Như vậy phương thức act() sẽ như sau

 public void act()

 {

 if ( atWorldEdge() )

 {

 turn(17);

 }

 

 if ( Greenfoot.getRandomNumber(100) < 10 )

 {

 turn(Greenfoot.getRandomNumber(90)-45);

 }

 move();

 

 if ( canSee(Worm.class) )

 {

 eat(Worm.class);

 }

 }

3.4  Tạo Method Riêng — Chia Nhỏ Code

Khi act() ngày càng dài, nên tách ra thành nhiều method nhỏ với tên rõ ràng. Code trở nên dễ đọc như văn xuôi!

Hình 3.3 — Tiến trình phát triển code: từ act() đơn giản → tách thành nhiều method có tên rõ ràng

 public void act()

 {

 turnAtEdge();   // Xử lý cạnh màn hình

 randomTurn();   // Xoay ngẫu nhiên

 move();         // Di chuyển

 lookForWorm();  // Tìm và ăn giun

 }

 

 /** Kiểm tra và ăn worm nếu nhìn thấy. */

 public void lookForWorm()

 {

 if ( canSee(Worm.class) )

 {

 eat(Worm.class);

 }

 }

 Comments — Ghi chú cho con người

 // là single-line comment — máy tính bỏ qua hoàn toàn.

 /** ... */ là multi-line comment — thường dùng mô tả phía trên method.

 Comments giúp người đọc (kể cả bạn 6 tháng sau) hiểu code ngay lập tức.

 Thói quen viết comment = dấu hiệu của lập trình viên chuyên nghiệp!

3.5  Thêm Lobster — Kẻ thù của Cua!

Thêm class Lobster (kế thừa Animal, dùng ảnh lobster.png). Copy toàn bộ code từ Crab, sửa Worm → Crab để tôm hùm đi săn cua. Đặt 1 cua + 3 tôm + nhiều giun vào thế giới và chơi thử!

Hình 3.4. Trò chơi với crab (cua) với worms (sâu/giun) và lobsters (tôm hùm)

3.6  Điều khiển bằng bàn phím

Greenfoot.isKeyDown(String key) trả về true khi phím đang được nhấn:

 /** Kiểm tra phím bấm và điều khiển cua. */

 public void checkKeypress()

 {

 if (Greenfoot.isKeyDown("left"))

 {

 turn(-4);    // Quay trái 4 độ

 }

 if (Greenfoot.isKeyDown("right"))

 {

 turn(4);     // Quay phải 4 độ

 }

 }

 

 public void act()

 {

 checkKeypress();   // Kiểm tra phím

 move();            // Luôn di chuyển

 lookForWorm();     // Tìm giun

 }

 3.7.  Kết thúc game và âm thanh

 

Bài viết chuyên môn giúp học được gì?

Có nền tảng vững chắc về lập trình hướng đối tượng với Java:

Khái niệm về OOP

  • Class — bản thiết kế định nghĩa thuộc tính và hành vi
  • Object / Instance — thể hiện cụ thể được tạo ra từ class
  • Actor — object có thể đặt vào thế giới Greenfoot
  • Subclass / Superclass — quan hệ kế thừa (is-a). Subclass kế thừa TẤT CẢ method của superclass

Khái niệm về Method

  • Method — hành động của object, định nghĩa bằng code Java
  • Method Call — lời gọi thực thi: tên() kết thúc bằng dấu chấm phẩy
  • Method Signature — return type + tên + danh sách tham số
  • Return Type: void = mệnh lệnh; non-void = câu hỏi có giá trị trả về
  •  Parameter — thông tin bổ sung truyền vào method qua dấu ngoặc tròn

Khái niệm về code và lập trình

  • Sequence — lệnh thực thi tuần tự từ trên xuống dưới
  • if-statement — thực thi có điều kiện, chỉ chạy khi điều kiện là true
  • Dot Notation — gọi method từ class/object khác: TênClass.method()
  • Static method — method thuộc về class, không phải object (VD: Greenfoot.stop())
  • Comment — ghi chú trong code, bị máy tính bỏ qua, dành cho người đọc
  • Compilation — dịch source code → machine code. Bắt buộc sau mỗi lần sửa!
  •  API Documentation — tài liệu tra cứu tất cả class và method có sẵn

Chặng đường phía trước

Ba phần trên mới chỉ là khởi đầu! Bạn cần tìm hiểu:

  •  Variables (biến) — lưu trữ và thay đổi dữ liệu
  •  Loops (vòng lặp) — lặp lại hành động nhiều lần
  •  Constructors — khởi tạo object với trạng thái ban đầu
  •  Fields — thuộc tính riêng của từng object
  •  ... và cuối cùng tự viết lại game Asteroids!

Chúc bạn code vui và yêu thích lập trình hướng đối tượng!

Tài liệu tham khảo

 [1] https://www.greenfoot.org/door

 [2] Michael Kölling (2015), Introduction to Programming with Greenfoot: Object-Oriented Programming in Java with Games and Simulations

ThS. Trương Châu Long - Trưởng bộ môn ngành CNTT & HTTT