Tuesday, October 26, 2010

Proxy. Loại: Structure. Độ khó: Cao

Proxy tiếng Anh có nghĩa là người được ủy nhiệm. Khi việc khởi tạo 1 đối tượng mất nhiều thời gian ví dụ như đọc file từ ổ đĩa hoặc load file từ Internet, chương trình sẽ tạo 1 file giả. Đến khi cần sử dụng thực sự file này thì lúc đó file có thể đã được load xong. Ví dụ như ở trình duyệt, khi load 1 trang web trình duyệt sẽ hiện các image holder trước sau đó mới lấp nội dung ảnh vào.
Xem xét ví dụ sau đây:
Ta có lớp trừu tượng Graphic với 2 phương thức load và draw. Lớp Image hiện thực lớp Graphic. Lớp ProxyImage cũng hiện thực lớp Graphic. Do load Image có thể mất nhiều thời gian nên ta tạo lớp ImageProxy để có sẵn đối tượng một cách nhanh chóng:

abstract class Graphic {
public abstract void load();
public abstract void draw();
}

public class Image extends Graphic {

private String filename;

public Image(String filename) {
this.filename = filename;
load();
}

public void draw() {
System.out.println("Drawing...");
}

public void load() {
System.out.println("Loading...");
}

}

public class ProxyImage extends Graphic {

private String filename;
private Graphic image;

public ProxyImage(String filename) {
this.filename = filename;
}

public void draw() {
if (image==null) image = new Image(filename);
image.draw();
}

public void load() {

}
}

public static void main(String[] args) {
Graphic image1 = new ProxyImage("file1.jpg");
Graphic image2 = new ProxyImage("file2.jpg");

image1.draw();
image2.draw();
}




Facade. Loại: Structure. Độ khó: Trung bình

Facade, đọc là fei sơ, có ý nghĩa như 1 bộ khung. Ví dụ như trong 1 máy tính sẽ có các bộ phận khác nhau như CPU, RAM, Harddrive. Khi bật nút power, các đối tượng như CPU, RAM, và Hard drive sẽ tự động thực hiện các công việc của mình.

public class Computer {

private CPU cpu;

private RAM ram;

private HardDrive hardDrive;

public Computer() {
cpu = new CPU();
ram = new RAM();
hardDrive = new HardDrive();
}
public void start()
{
cpu.boot();
ram.load();
hardDrive.read();
}
}

public class CPU {

void boot()
{
System.out.println("Booting CPU");
}
}

public class RAM {

void load()
{
System.out.println("Load memory from RAM");
}
}

Test:
public static void main(String[] args)
{
Computer facade = new Computer();
facade.start();
}



Composite. Loại Structure. Độ khó: Trung bình

Composite ám chỉ 1 lớp tổng hợp. Ví dụ 1 thư mục sẽ có 1 thư mục cha và nhiều file. Hay 1 nhân viên sẽ có 1 người quản lý và nhiều người dưới quyền. Ví dụ sau đây hiện thực cấu trúc đó:

public class Directory {

private Directory directory;
private File[] files;

public Directory(File[] files) {
super();
this.files = files;
}
public Directory(Directory directory, File[] files) {
super();
this.directory = directory;
this.files = files;
}
boolean isDirectory()
{
return files != null;
}
boolean isFile()
{
return files ==null;
}

public Directory getDirectory() {
return directory;
}

public void setDirectory(Directory directory) {
this.directory = directory;
}

public File[] getFiles() {
return files;
}

public void setFiles(File[] files) {
this.files = files;
}

}

Decorator. Loại: Structure. Độ khó: Thấp

Decorator là trang trí. Decorator cho 1 class nghĩa là thêm thuộc tính hoặc hành vi, hoặc mở rộng hành vi cho lớp đó mà không dùng subclass.

Ví dụ có lớp Number với phương thức in ra số ngẫu nhiên
public class Number {

public void print()
{
System.out.println(new Random().nextInt());
}
}

public class Decorator
{
public Decorator() {
System.out.println("This is random number: ");
new Number().print();
}
}
public static void main(String[] args)
{
Decorator decorator = new Decorator();
}




Prototype. Loại: Creation. Độ khó: Thấp

Prototype theo tiếng Anh là mẫu vật. Dựa trên mẫu vật mà người ta copy ra nhiều đối tượng khác có cùng tính chất và hành vi. Java hỗ trợ 1 interface Cloneable với phương thức clone.
public class Complex implements Cloneable {

int[] nums = {1,2,3,4,5};
public Object clone() {
try {
return super.clone();
}catch(CloneNotSupportedException cnse) {
System.out.println(cnse.getMessage());
return null;
}
}

int[] getNums() {
return nums;
}

}

public static void main(String[] args)
{
Complex complex1 = new Complex();
Complex copy = (Complex)complex1.clone();

complex1.getNums()[3] = 10000;

for(int i = 0; i < copy.getNums().length; i++)
System.out.println(copy.getNums()[i]);
}

Singleton. Loại: Creation. Độ khó: Dễ

Lúc mới học lập trình Java, tôi rất lười sử dụng từ khóa new để tạo mới class, có lẽ do ảnh hưởng của phương pháp lập trình thủ tục. Không ngờ sau khi làm quen với Java, tôi phát hiện ra rằng việc dùng static và final đôi khi có lợi: đó là lớp static chỉ có 1 và chỉ 1 instance mà thôi.

public class Singleton {

private static final Singleton INSTANCE = new Singleton();

public Singleton() {
}

public static Singleton getInstance()
{
return INSTANCE;
}

public String sayHello()
{
return "This method is called by only 1 instance";
}

public static void main(String[] args)
{
System.out.println(Singleton.getInstance().sayHello());
}
}

Monday, October 25, 2010

Abstract Factory. Loại: Creation. Độ khó: Trung bình

Abstract Factory trừu tượng hơn Factory 1 mức. Thay vì chỉ cần 1 interface Factory, pattern này đòi hỏi thêm 2 lớp Factory hiện thực interface này. Ví dụ interface Factory có 1 phương thức là createButton(), 2 lớp hiện thực là WindowFactory và MacFactory. Hai lớp này sẽ trả về lần lượt 2 đối tượng là WindowButton và MacButton tương ứng. Trong đó WindowButton và MacButton lần lượt là 2 lớp hiện thực của interfact Button.

Code mẫu:
public interface Button {
void paint();
}

public class OSXButton implements Button{
public void paint() {
System.out.println("Draw OSX button");
}
}

public class WindowButton implements Button {
public void paint() {
System.out.println("Drawing Window button");
}
}

public interface Factory {
Button createButton();
}

public class WindowFactory implements Factory {

public Button createButton() {
return new WindowButton();
}
}

public class OSXFactory implements Factory {
public Button createButton() {
return new OSXButton();
}
}

Chạy test:
public static void main(String[] args) {
Factory factory = new WindowFactory();
Button button = factory.createButton();
button.paint();
}

Factory. Loại: Creation. Độ khó: Trung bình

Factory là 1 nhà máy sản xuất. Ý tưởng đưa ra là 1 nhà máy có thể sản xuất ra nhiều loại đối tượng khác nhau tùy theo yêu cầu đầu vào.
Ví dụ, ta có lớp Namer có 2 thuộc tính: First Name và Last Name. Tùy theo văn hóa từng nước mà thứ tự của First Name và Last Name sẽ xuất hiện khác nhau. Giả sử ta có 2 lớp con của Namer là FirstNameFirst và LastNameFirst. Hai lớp này sẽ căn cứ vào chuỗi tên truyền vào, bóc tách ra họ, tên, sau đó gán vào 2 thuộc tính firstName và lastName.

public class Namer {
protected String firstName;
protected String lastName;
}

public class LastFirst extends Namer {

public LastFirst(String fullName) {
int i = fullName.indexOf(",");
if(i>0)
{
lastName = fullName.substring(0,i);
firstName = fullName.substring(i+1, fullName.length());
}
else
{
lastName = fullName;
firstName = "";
}
}
}


public class FirstFirst extends Namer {

public FirstFirst(String fullName) {

int i = fullName.indexOf(" ");
if (i > 0) {
firstName = fullName.substring(0, i + 1);
lastName = fullName.substring(i, fullName.length());
} else {
lastName = fullName;
firstName = "";
}
}

}

Ta dùng 1 factory ở đây để trả về đối tượng phù hợp căn cứ vào dữ liệu đầu vào:
public class FactoryNamer {

public Namer getNamer(String enterText)
{
if(enterText.indexOf(",")>0)
return new LastFirst(enterText);
else
return new FirstFirst(enterText);
}
}


Lập trình phần mềm điều khiển bằng VB6

VB6 đã outdated từ lâu và thay bằng .NET. Tuy nhiên, với các ứng dụng điều khiển, VB6 vẫn còn đất sống bởi vì nó không đòi hỏi cấu hình máy tính cao, dễ lập trình, và bảo trì.
RS232 hay còn gọi là cổng COM hay cổng tuần tự là loại cổng thường được dùng trong các thiết bị cần giao tiếp với máy tính. Ưu điểm của nó là đơn giản, rẻ tiền. Nhược điểm của nó là tốc độ truyền và khoảng cách.

Sau đây là đoạn code mẫu lấy dữ liệu từ 1 máy đo và lưu trữ dữ liệu thu được vào máy tính.

Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)


Private Sub Command4_Click()
Dim downloadedData, data As String
If MSComm1.PortOpen = False Then MSComm1.PortOpen = True

Dim i As Integer
i = 5
Do While data <> "" Or i > 1
MSComm1.Output = "D"

Sleep 3000

data = MSComm1.Input

downloadedData = downloadedData & data
i = i - 1

Loop

Open "C:\data_from_awa.txt" For Output As #1

Write #1, downloadedData

Close #1

End

End Sub

Wednesday, October 20, 2010

Iterator. Loại: Behavioral. Độ khó: Thấp

Iterator là một dạng vòng lặp. Đối với 1 danh sách các đối tượng không đồng nhất nhau thì việc đưa vào các danh sách như List, Sort, Map sẽ giúp cho việc duyệt qua danh sách dễ dàng mà không cần quan tâm đến sự hiện diện bên trong của danh sách.

Ví dụ có interface Employee và 2 class hiện thực là HourlyEmployee - nhân viên làm theo giờ và RegularEmployee - nhân viên lãnh lương tháng.

interface Employee {

double earnings();

}

public class HourlyEmployee implements Employee {

private double salaryPerHour;

private String name;

public HourlyEmployee(double salaryPerHour, String name) {
super();
this.salaryPerHour = salaryPerHour;
this.name = name;
}

public double earnings() {
// TODO Auto-generated method stub
return 40*salaryPerHour;
}

public String toString() {
return name;
}
}

public class HourlyEmployee implements Employee {

private double salaryPerHour;

private String name;

public HourlyEmployee(double salaryPerHour, String name) {
super();
this.salaryPerHour = salaryPerHour;
this.name = name;
}


public double earnings() {
return 40*salaryPerHour;
}


public String toString() {
return name;
}

}

Sử dụng pattern này:

public static void main(String[] args)
{
List list = new ArrayList();

list.add(new HourlyEmployee(30, "Petter"));

list.add(new RegularEmployee(20000000, "Mary"));

Iterator itr = list.iterator();

while(itr.hasNext())
{
Employee em = (Employee)itr.next();
System.out.println(em + " earns "+em.earnings());
}

}


Command. Loại: Behavioral. Độ khó: Trung bình

Command nghĩa là ra lệnh. Sĩ quan chỉ huy gọi là commander, người này không làm mà chỉ ra lệnh cho người khác làm. Như vậy, phải có người nhận lệnh và thi hành lệnh (receiver).

Bây giờ ta lấy ví dụ sau. Bóng đèn Light có 2 phương thức switchOn và switchOff.

class Light()
{
void switchOn(){
System.out.println("Switch light on");
}

void switchOff(){
System.out.println("Switch light off");
}
}

Tuy vậy, để giống trong quân đội, ta làm 1 interface tên là Command không trực tiếp tắt bật đèn mà chỉ ra lệnh cho bóng đèn bật tắt.

interface Command(){
void execute();
}

Ta hiện thực interface này bằng 2 class: CommandOff và CommandOn

class CommandOff(){
Light light;
public CommandOff(Light light){
this.light = light;
}

public void execute(){
light.switchOff();
}
}


class CommandOn(){
Light light;
public CommandOn(Light light){
this.light = light;
}

public void execute(){
light.switchOn();
}
}

Bây giờ đã đóng gói các command này vào trong 1 bộ điều khiển gọi là Remote Control

class RemoteControl(){

private Command command;

public setCommand(Command command){
this.command = command;
}

public void pressButton(){
command.execute();
}

Sử dụng remote control này như sau:
public static void main(String[] args){

RemoteControl rc = new RemoteControl();

Light light = new Light();
Command c1 = new CommandOff(light);
Command c2 = new CommandOn(light);

rc.setCommand(c1);
rc.pressButton();

rc.setCommand(c2);
rc.pressButton();

}

Như vậy, ta có thể truyền bất cứ command nào vào Remote control để yêu cầu thực hiện. Khi đó, yêu cầu thực hiện đã được đóng gói vào trong 1 object như mô tả trong GoF book:

Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations


Adapter - Loại: Creation - Độ khó: trung bình

Adapter nôm na là một bộ chuyển đối. Laptop cũng cần adapter để chuyển từ điện AC sang DC. Đi nước ngoài thì phải xem họ xài ổ cắm tròn hay dẹt, 3 chấu hay 2 chấu. Nếu không phải mua adapter.
Vậy adapter là 1 bộ chuyển đổi dùng để kết nối 2 hay nhiều thiết bị khác nhau lại.

Bây giờ xét ví dụ sau đây: mỗi loài động vật có tiếng kêu khác nhau, chó sủa, bò rống, vượn hót, chim kêu.

public class Cow {

public void roar()
{
System.out.println("o o o o o");
}

}

public class Dog {

public void bark()
{
System.out.println("gau gau gau gau");
}
}

Ta cần 1 interface AnimalAdapter để có thể phiên dịch tiếng nói tất cả động vật

public interface AdapterAnimal {

public void say();

}

Để hiện thực interface này ta cần có AdapterCow và AdapterCat.

public class AdapterCow implements AdapterAnimal {

Cow cow = new Cow();
public void say(){
cow.roar();
}
}

public class AdapterCat implements AdapterAnimal {

Dog dog= new Dog();
public void say(){
dog.bark();
}
}

Bây giờ, ta sử dụng Adapter này:

public static void main(String[] args)
{
AdapterAnimal[] aas = {new AdapterCow(), new AdapterDog()};

for(AdapterAnimal aa: aas)
{
aa.say();
}

}

Ta thấy: nhờ có AdapterAnimal mà việc cho các loài động vật khác nhau cùng lúc lên tiếng trở nên dễ dàng.


Design pattern trong Java

Design pattern là một thuật ngữ nổi tiếng trong hướng đối tượng. Tuy nhiên, design pattern cũng khá khó hiểu. Có hơn 200 pattern trong đó khoảng 24 pattern là phổ biến nhất. Để lấy chứng chỉ SCJD chỉ cần biết khoảng 3,4 mẫu gì đó. Còn để lấy được SCEA thì cần biết hết 24 mẫu. Design pattern cũng được ứng dụng trong .NET, Ruby....

Các framework mã nguồn mở sử dụng design patterns rất nhiều. Chúng ta có thể nhận dạng chúng thông qua cách đặt tên ví dụ: Proxy, Adapter, Singleton, Prototype....

Trong quá trình nghiên cứu design pattern tôi nảy sinh ý định viết 1 loại bài tóm tắt lại những cái đã học được. Tài liệu trên Internet về design pattern thì rất nhiều nhưng để hiểu design pattern cần có các ví dụ tốt. Tôi sẽ trình bày các ví dụ này kèm theo mỗi pattern. Ví dụ được tổng hợp từ nhiều nguồn khác nhau