Tản mạn về Dependency Injection

Tản mạn về Dependency Injection

Như thường lệ, blog của mình sẽ rất ít các định nghĩa, rất nhiều code, hình ảnh minh họa và những câu nói dí dỏm.

OK, hãy bắt đầu với định nghĩa về Dependency

Dependency

Khi trong class A có sự tồn tại của lớp B, dùng lớp B để làm 1 công việc gì đó, ta nói lớp A đang phụ thuộc vào lớp B.

Ví dụ:

CustomerRepository.java

package com.edward.tutorial.di.repository;

import java.util.Collection;

import com.edward.tutorial.di.model.Customer;

public class CustomerRepository {
	public Customer findOne(String customerId) {
		// Code stuff
	}

	public Collection<Customer> findAll() {
		// Code stuff
	}

	public void create(Customer customer) {
		// Code stuff
	}

	public boolean update(Customer customer) {
		// Code stuff
	}

	public void delete(Customer customer) {
		// Code stuff
	}
}

CustomerService.java

package com.edward.tutorial.di.service;

import java.util.Collection;

import com.edward.tutorial.di.model.Customer;
import com.edward.tutorial.di.repository.CustomerRepository;

public class CustomerService {
	
	CustomerRepository customerRepository;
	
	Collection<Customer> findAll() {
		return customerRepository.findAll();
	}

	Customer create(Customer customer) {
		customerRepository.create(customer);
		return customer;
	}
	
	Customer update(Customer customer) {
		customerRepository.update(customer);
		return customer;
	}

	void delete(String customerId) {
		Customer customer = findOne(customerId);
		customerRepository.delete(customer);
	}
	
	public Customer findOne(String customerId) {
		return customerRepository.findOne(customerId);
	}
}

Như ví dụ ở trên, lớp CustomerService đang phụ thuộc vào lớp CustomerRepository, vì CustomerService đang sử dụng 1 instance của CustomerRepository để thực hiện việc lưu trữ dữ liệu xuống database, cụ thể ở đây là customerRepository.

Dependency1

Vậy: customerRepository được khởi tạo ở đâu? Vì nếu không được khởi tạo, thì khi sử dụng chúng ta sẽ nhận được NullPointerException.

Câu trả lời là: để sử dụng được đối tượng customerRepository trong CustomerService, chúng ta sẽ có 2 cách:

  1. Khởi tạo đối tượng customerRepository trong lớp CustomerService.
  2. Truyền (Inject) đối tượng customerRepository đã được khởi tạo sẵn từ bên ngoài vào lớp CustomerService.

Dependency2

Injection

CHÚNG TA VỪA NHẮC ĐẾN INJECT. ĐÚNG VẬY, KHI LÀM THEO CÁCH 2, NGHĨA LÀ CHÚNG TA ĐANG THỰC HIỆN DEPENDENCY INJECTION.

MỘT LƯU Ý KHÁC:
Nếu chỉ muốn hiểu Dependency Injection là gì thì xin chúc mừng, chúng ta đã hoàn thành mục tiêu ở đây.

Tuy nhiên chắc sẽ chẳng ai muốn dừng lại tại đây. Cho nên chúng ta sẽ đi tiếp phần demo và những chủ đề liên quan đến Dependency Injection

Tiếp tục với việc demo cho 2 cách trên.

Dependency3

Vậy các bạn sẽ tự hỏi: cách nào là tốt nhất, lúc nào nên dùng cách nào?

Câu trả lời vẫn như mọi khi: không cách nào là hoàn hảo.

Tightly coupling và Loosely coupling

Trước khi đi vào phân tích ưu nhược của các cách làm, chúng ta cần làm rõ 2 khái niệm: tightly coupling và loosely coupling.

Tightly coupling:

Ở cách 1, khi khởi tạo đối tượng customerRepository ngay trong lớp CustomerService, ta nói lớp CustomerService đang phụ thuộc hoàn toàn vào lớp CustomerRepository. Trong suốt quá trình chương trình được thực thi (runtime), lớp CustomerService sẽ chỉ làm việc với duy nhất thể hiện của lớp CustomerRepository.

Ở cách 2, mặc dù chúng ta không khởi tạo trực tiếp đối tượng customerRepository trong lớp CustomerService mà inject từ ngoài vào, nhưng lớp CustomerService vẫn chỉ làm việc được đối tượng customerRepository, do đó, cách 2 vẫn là Tightly coupling.

Vậy, tightly coupling là việc một lớp TRỰC TIẾP sử dụng (phụ thuộc) trực tiếp vào thể hiện cụ thể của 1 lớp khác.

TightlyCoupling

Loosely coupling:

Trái với tightly coupling, dễ dàng suy ra loosely coupling là việc một lớp KHÔNG TRỰC TIẾP sử dụng (phụ thuộc) vào thể hiện cụ thể của bất cứ 1 lớp cụ thể nào.

Cho đến bây giờ vẫn chưa có 1 ví dụ nào để cho thấy loosely coupling như thế nào.

Trong Java (hoặc các ngôn ngữ lập trình hướng đối tượng khác), để đạt được loosely coupling trong thiết kế, chúng ta cần sử dụng khái niệm “đa hình” (polymorphism). Tính đa hình trong lập trình hướng đối tượng là việc định nghĩa những đối tượng trừu tượng (abstraction) như abstract class hoặc interface. Những đối tượng abstraction này khai báo hoặc đưa ra quy ước (contract) và cho phép nhiều lớp đối tượng cụ thể kế thừa và định nghĩa lại hành vi cho riêng chúng.

Ví dụ về tính đa hình

Một tổ chức tiêu chuẩn ISO nào đó đưa ra định nghĩa về chiếc điện thoại: điện thoại là vật có khả năng nghe và nhận cuộc gọi. Lúc này ISO chưa hề cho ra đời 1 chiếc điện thoại nào có thể cầm nắm được, túm lại, nó chỉ là định nghĩa, để khi có người đang cầm trên tay 1 chiếc điện thoại thực sự thì họ có thể gọi hoặc nhận cuộc gọi.
Sau khi đã có định nghĩa thế nào là điện thoại, các nhà sản xuất điện thoại hàng đầu thế giới mới bắt tay vào việc sản xuất ra những chiếc điện thoại theo tiêu chuẩn đã đặt ra bởi ISO. Hiện tại có 2 nhà sản xuất là Nokia và Samsung. Mục đích của họ là phải sản xuất ra những chiếc điện thoại có thể nghe và gọi được theo tiêu chuẩn ISO.
Khi những chiếc điện thoại được tung ra thị trường, người sử dụng sau đó mua về và thực hiện chức năng nghe gọi mà họ đã từng được nghe ISO công bố trước đó. Tuy nhiên khi thực hiện cuộc gọi từ điện thoại Nokia thì sẽ nghe câu “Hello Microsoft” đầu tiên, còn với Samsung sẽ là câu “Lee Yong-dae” chẳng hạn. (I love Badminton).
Có một nhà sản xuất nghiệp dư (giấu tên) cũng tập tành sản xuất điện thoại nhưng không tuân theo tiêu chuẩn của ISO, vậy nên khi cho ra thị trường, người ta không thể thực hiện nghe gọi, mà chỉ có thể… nghe nhạc. Vậy nên có thể gọi đây là cái máy nghe nhạc (tuân theo tiêu chuẩn của một tổ chức nào đó dành cho máy nghe nhạc), chứ không phải cái điện thoại.
Phần mô tả cho đa hình đến đây là hết. Cười chút đã. =))

Do vậy, một lớp được gợi là loosely couling khi nó chỉ phụ thuộc vào các đối tượng high level (Abstract class hoặc Interface) thay vì phụ thuộc vào các lớp đối tượng cụ thể (concrete class).
(Giống như việc một người tuyên bố, tui biết sử dụng tất cả điện thoại, miễn là nó có thể gọi và nghe, chứ không riêng gì cái điện thoại mà cứ phát ra câu “Lee Yong-dae”.)

Ví dụ với bài Điện thoại theo chuẩn ISO ở trên

package com.edward.tutorial.di.phone;

public abstract class Phone {
	public abstract void call(String number);
	public abstract void answer(String number);
}

package com.edward.tutorial.di.phone;

public class NokiaPhone extends Phone {

	@Override
	public void call(String number) {
		System.out.println("Microsoft... " + number);
	}

	@Override
	public void answer(String number) {
		System.out.println(number + " Buy me...");
	}
}

package com.edward.tutorial.di.phone;

public class SamsungPhone extends Phone {

	@Override
	public void call(String number) {
		System.out.println("Kpop Kpop Kpop... " + number);
	}

	@Override
	public void answer(String number) {
		System.out.println(number + " I wanna cry...");
	}
}

package com.edward.tutorial.di.phone;

public class User {
	Phone phone;
	
	public User(Phone phone) {
		this.phone = phone;
	}
	
	public void bringMePhone(Phone phone) {
		this.phone = phone;
	}
	
	public void call(String number) {
		phone.call(number);
	}
	
	public void answer(String number) {
		phone.answer(number);
	}
}

package com.edward.tutorial.di.phone;

public class Main {

	public static void main(String[] args) {
		User bill = new User(new SamsungPhone());
		bill.call("0123");
		bill.bringMePhone(new NokiaPhone());
		bill.answer("0124");
	}
}

Quay lại ví dụ của chúng ta

Extract CustomerRepository class thành CustomerRepository interface

package com.edward.tutorial.di.repository;

import java.util.Collection;

import com.edward.tutorial.di.model.Customer;

public interface CustomerRepository {

	public abstract Customer findOne(String customerId);

	public abstract Collection<Customer> findAll();

	public abstract void create(Customer customer);

	public abstract boolean update(Customer customer);

	public abstract void delete(Customer customer);

}

CustomerRepositoryImpl sẽ implement CustomerRepository interface

package com.edward.tutorial.di.repository;

import java.util.Collection;

import com.edward.tutorial.di.model.Customer;

public class CustomerRepositoryImpl implements CustomerRepository {
	@Override
	public Customer findOne(String customerId) {
		// Code stuff
	}

	@Override
	public Collection<Customer> findAll() {
		// Code stuff
	}

	@Override
	public void create(Customer customer) {
		// Code stuff
	}

	@Override
	public boolean update(Customer customer) {
		// Code stuff
	}

	@Override
	public void delete(Customer customer) {
		// Code stuff
	}
}

Và inject CustomerRepository interface thay vì CustomerRepositoryImpl

package com.edward.tutorial.di.service;

import java.util.Collection;

import com.edward.tutorial.di.model.Customer;
import com.edward.tutorial.di.repository.CustomerRepository;

public class CustomerService {
	
	CustomerRepository customerRepository;
	
	public CustomerService(CustomerRepository customerRepository) {
		this.customerRepository = customerRepository;
	}
	
	Collection<Customer> findAll() {
		return customerRepository.findAll();
	}

	Customer create(Customer customer) {
		customerRepository.create(customer);
		return customer;
	}
	
	Customer update(Customer customer) {
		customerRepository.update(customer);
		return customer;
	}

	void delete(String customerId) {
		Customer customer = findOne(customerId);
		customerRepository.delete(customer);
	}
	
	public Customer findOne(String customerId) {
		return customerRepository.findOne(customerId);
	}
}

LooselyCoupling

Nên design theo Tightly coupling hay Loosely coupling

Câu trả lời là Loosely coupling. Vì sao? Đọc tiếp…

Loosely coupling = Dependency Injection + Abstraction

Dependency Injection không giúp chúng ta cải thiện tính flexibility trong thiết kế. Tuy nhiên khi chúng ta kết hợp với Abstraction bằng cách inject các đối tượng abtract, thì chúng ta đã có một bản thiết kế loosely coupling và flexibility.

Dependency inversion principle

Dependency inversion principle là 1 trong 5 nguyên lý thiết kế hướng đối tượng (S.O.L.I.D). Xem thêm tại (https://edwardthienhoang.wordpress.com/2013/11/27/the-dependency-inversion-principle/)

Nói đơn giản, Dependency inversion principle giúp chúng ta có được 1 thiết kế loosely coupling

Dependency inversion principle = Loosely coupling = Dependency Injection + Abstraction

Inversion of Control (IoC)

Nếu như Dependency inversion là principle thì Inversion of Control là pattern tuân theo principle này.

Trong Java, IoC pattern xuất hiện khá nhiều. Lấy 1 ví dụ rất quen thuộc, đó là hàm public static void main(String[] args)

package com.edward.tutorial.di;

public class Main {

    public static void main(String[] args) {
        System.out.println("Developer's dream starts from here");
        doMyWish();
    }

    private static void doMyWish() {
        System.out.println("I wish this app would be the most favourite one in the world");
    }
}

Tất cả các lập trình viên Java đều quá quen thuộc với hàm main này – entry point của 1 chương trình Java truyền thống. Lần này chúng ta sẽ không bàn đến cú pháp, mà bàn đến một việc mà được cho là “chẳng ai quan tâm là mấy”. Câu hỏi là: hàm doMyWish() được gọi bởi hàm main(), vậy hàm main() được gọi bởi hàm nào?

Câu trả lời là: Java sẽ gọi hàm main() của chúng ta.

Vậy làm thế nào mà Java (chính xác hơn là javac.exe hay đại loại là một cái cao siêu gì đó) có thể gọi được hàm main của chúng ta?

Hollywood principle: “Don’t call us, we’ll call you.”

Dịch nôm na: “Đừng có gọi cho tui, để lại số điện thoại, tui sẽ gọi bạn.” Ngôi sao mà, có quyền chảnh.

Nếu nói Java là 1 ngôi sao Hollywood, và hàm main là 1 ai đó, thì việc chúng ta cần làm là nói với ngôi sao đó: “Đây! Địa chỉ của tui đây”. Cung cấp cho Java đường dẫn tới class com.edward.tutorial.di.Main chứa hàm main. Việc còn lại cứ để Java lo, Java sẽ gọi hàm main trong class com.edward.tutorial.di.Main và
“Developer’s dream” will be started.

LƯU Ý: Hollywood principle chỉ là cách gọi khác của Dependency inversion principle.

Thêm 1 ví dụ dễ thấy.

JButton helloButton = new JButton("Hello");
helloButton.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("I would say Hello to everyone!");
    }
});

Chúng ta không hề gọi hàm actionPerformed(ActionEvent e), mà chỉ khai báo như vậy, và nói: “Hey! Java Swing or EventDispatchThread or something like that, call me when someone click the helloButton”. Một lần nữa, Java sẽ lo việc đó.

Inversion of Control Container (IoC Container)

Chúng ta đã thấy sự tuyệt vời với những đoạn code ở trên, Inversion of Control Container chính là những ngôi sao Hollywood thực thụ, họ định nghĩa ra Hollywood principle, công việc của lập trình viên là: viết ra những đoạn code và nhờ IoC Container đâu là điểm entry point

Trong Eclipse, chúng ta dễ dàng config entry point cho 1 chương trình Java tại:
Config_Main

Inversion of Control Container Framework (IoC Container Framework)

Trong ứng dụng Java truyền thống, IoC Container có thể được hiểu như những ví dụ ở trên. Tuy nhiên, trong những ứng dụng Java Enterprise, IoC Container mới có nhiều đất diễn, có khá nhiều Framework hỗ trợ IoC Container tạm gọi là IoC Container Framework như: JEE Framework hoặc Spring Framework. Nơi mà những khái niệm về: Dependency Injection, Inversion of Control hay Inversion of Control Container sẽ được nhắc đến như là những Core Feature. Tất cả đều hướng đến một mục đích duy nhất: tạo ra ứng dụng loosely coupling, flexibility cũng như giúp lập trình viên tập trung 99% vào công việc cho business flow.

Loosely coupling và Testing

Một lợi ích không thể nhắc đến khi thiết kế chương trình theo loosely coupling style chính là Testing. Thay vì phải inject những đối tượng thực vào trong SUT (System under test, hay nôm na là class cần test), chúng ta có thể inject các stub đã implement interface vào trong SUT. Chi tiết sẽ được bàn trong bài viết về UnitTesting. :)

Kết thúc bài tại đây, hi vọng trong giới hạn hiểu biết cùng với cách giải thích của mình sẽ giúp các bạn có thêm 1 cái nhìn khác về các khái niệm: Dependency Injection, Dependency Inversion Principle, IoC.. Trong những bài sau, chúng ta sẽ bàn luận về IoC Container trong Spring Framework.

Happy learning

NGUỒN: HTTP://EDWARDTHIENHOANG.WORDPRESS.COM/
EMAIL: EDWARDTHIENHOANG@GMAIL.COM

Đôi nét về Internet of Thing

Khi mà cuộc sống của con người chúng ta ngày càng có những mối quan hệ chặt chẽ với Internet thì sự phát triển của nó rất được quan tâm. Hãy cùng tìm hiểu đôi nét về một trong những xu hướng phát triển của công nghệ hiện nay mang tên Internet of Thing

Các bạn đã từng đọc, từng nghe những câu chuyện về ngôi nhà thông minh, nơi các máy nướng bánh có thể “giao tiếp” với máy hút khói. Nhưng chúng kết nối với nhau bằng cách nào? Khi nào nó sẽ trở thành xu hướng chủ đạo trong cuộc sống và thực sự trở nên hữu ích? Những câu hỏi thường gặp mà chúng tôi giải thích dưới đây sẽ giúp bạn có cái nhìn tổng quát nhất về khái niệm này.

Internet of Things là gì?Internet of Thing

Cho đến nay, vẫn chưa có một định nghĩa chính xác nào về cụm từ Internet of Things (IoT) – Vật dụng kết nối Internet. Nhưng thật ra, vẫn có những cách để xác định liệu một thứ gì đó có phải là một phần của IoT hay không Hãy đặt ra những câu hỏi sau: Liệu một sản phẩm của một nhà sản xuất nào đó có hoạt động tương thích với sản phẩm của một nhà cung cấp khác không? Liệu một bộ khóa cửa thông minh của hãng này có thể giao tiếp với công tắc đèn thông minh của hãng khác, hoặc tất cả những thứ đó có thể kết nối với bộ cảm biến nhiệt đến từ một hãng sản xuất khác nữa?

Để dễ hiểu hơn, hãy thử tưởng tượng về hệ thống sau: Khi bạn đến gần cửa trước của ngôi nhà, một bộ điều khiển từ xa được tích hợp vào chìa khóa sẽ mở cửa. Thông điệp phát ra từ ổ khóa sẽ khiến đèn hành lang bật sáng. Hệ thống điều khiển nhiệt trong nhà vốn đang hoạt động ở chế độ tiết kiệm điện (hoặc tắt hẳn), sẽ điều chỉnh nhiệt độ ở mức thoải mái nhất. Tất cả mọi thứ đều hoạt động nhịp nhàng như một buổi hòa nhạc. Theo đó, định nghĩa này hoàn toàn giống với mô tả của Paul Williamson, Giám đốc về công nghệ dây công suất thấp của nhà sản xuất chíp bán dẫn CSR: “Internet of Things thực sự là sự phối hợp nhịp nhàng giữa nhiều thiết bị với nhau”.

Điều gì có thể làm cho Internet of Thing có thể biến đổi theo hoàn cảnh?

Chỉ trong một từ: Cảm biến. Nhiều thiết bị IoT có các loại cảm biến có thể ghi nhận sự thay đổi nhiệt độ, ánh sáng, áp lực, âm thanh và chuyển động. Những thứ đó cũng tương tự như đôi mắt và đôi tai của bạn với những gì đang xảy ra trên thế giới.

Trước khi nói về những gì các cảm biến này làm được, chúng ta hãy mô tả chúng. Các cảm biến này là thuộc danh mục các thiết bị hệ thống vi cơ điện tử (Microelectromechanical System – MEMS) và được sản xuất tương tự như việc làm ra các bộ vi xử lý, thông qua một quá trình in thạch bản. Các cảm biến này có thể kết hợp với một mạch ứng dụng tích hợp cụ thể hoặc một ASIC. Đây là một mạch với khả năng lập trình hạn chế về và được điều khiển để làm một thứ gì đó cụ thể. Nó cũng có thể được kết hợp với bộ vi xử lý và có thể gắn liền với radio không dây cho truyền thông.

Một ví dụ về cách mà IoT làm việc?

Hãy tưởng tượng: Bạn đang đi nghỉ và để nhà trống. Một cảm biến độ ẩm sẽ phát hiện nước trên sàn tầng hầm. Cảm biến đó được xử lý bởi một ứng dụng – vốn cũng nhận được báo cáo khác từ cảm biến nhiệt độ phát hiện dòng chảy của nước trong đường ống nước chính (khi nước chảy, nó mất đi nhiệt và làm giảm nhiệt độ).

Cả 2 cảm biến đó đều phát hiện ra những dấu hiệu bất thường đáng quan tâm. Tỉ lệ nước rò rỉ cao có thể là dấu hiệu của việc hư hại ống dẫn, sẽ khiến các van nước tự độn ngắt. Lưu lượng nước nhỏ có thể do nhà vệ sinh bị rò rỉ, hoặc do nhà bạn vừa trải qua trận mưa lớn. Trong cả hai trường hợp, bạn đều nhận được tin nhắn tự động mô tả lại những phát hiện đó.

Và đây là cách bạn điều tra về những trường hợp đó. Thông qua một ứng dụng di động, bạn sẽ có hai mã khóa dùng một lần để mở khóa cửa trước, một cho hàng xóm và một cho một thợ sửa ống nước. Khi cánh cửa được mở khóa, một cảnh báo văn bản sẽ được gởi đến bạn về những người vừa sử dụng mã để vào nhà. Việc luôn luôn theo dõi và thấu hiểu chính căn nhà của bạn chính là lợi ích lớn nhất của IoT.

Cảm biến của IoT hoạt động thế nào ở không gian công cộng?

Ở những bãi đậu xe công cộng của những nước phát triển. Công nghệ được thực hiện bởi hãng Streetline có thể phát hiện và thông báo cho tài xế biết vị trí đậu gần đó. Tài xế đang tìm kiếm chỗ đậu xe có thể sử dụng ứng dụng trên smartphone của hãng này để dò các điểm đậu xe đang trống, hoặc được thông báo khi nào xuất hiện chỗ trống gần đó.

Ở các khu vực công cộng, một chiếc smartphone có thể sẽ rất hữu ích. Ở Boston, khi người ta lái xe xuống đường, cảm biến gia tốc của điện thoại sẽ bắt đầu theo dõi. Cám biến này sẽ theo dõi chính xác các hoạt động tăng – giảm tốc độ khi lái xe của chủ nhân, và trở thành một công cụ giám sát hành trình. Đó là một cách rất mới để sử dụng chiếc smartphone của bạn.

Bạn có muốn phòng tắm “nói chuyện” được với chiếc tủ lạnh?

IoT mở ra rất nhiều cơ hội cho các nhà lập trình ứng dụng. Hãy bắt đầu với một chiếc tủ lạnh thông minh. Bạn mua từ cửa hàng tạp hóa trực tuyến và họ đã gửi hàng đến tận nhà bạn. Một lợi thế rất lớn của lĩnh vực này là các cửa hàng thực phẩm và các nhà sản xuất thực phẩm có thể thêm các thẻ RFID vào những sản phẩm được bán ra của họ. Tủ lạnh thông minh sẽ biết những gì đang có bên trong nó thông qua các kệ chứa có cảm biến cân nặng và giao tiếp với các thẻ RFID đó để biết ngày hết hạn của thực phẩm. Nó cũng có thể giúp bạn giữ một danh sách mua hàng, tự động đặt hàng và cung cấp thông tin dinh dưỡng.

Ví dụ, giả sử khi bạn quyết định lấy một que kem ngon lành ra khỏi tủ đông. Khi điều đó xảy ra, một loa không dây kết nối với tủ lạnh sẽ thông báo lớn: “Hãy xem xét lại lựa chọn này. Theo yêu cầu của bạn, đây là thứ có thể ảnh hưởng đến trọng lượng và chỉ số đo hình thể của bạn”. Loa không dây này sẽ có kết nối với cái cân thông minh mà bạn đặt trong phòng tắm. Tất nhiên, cái cân đó ko kết nối trực tiếp với tủ lạnh, mà giao tiếp với ứng dụng trung gian có nhiệm vụ thu thập, liên kết và đánh giá dữ liệu. Kết hợp cái cân với tủ lạnh nghe có vẻ ngớ ngẩn, nhưng lại là đặc điểm lớn của IoT: Mọi thứ đều có thể kết nối và tạo ra những chức năng mới.

Các thiết bị IoT giao tiếp với nhau như thế nào?

Một thiết bị IoT sẽ có bộ phận phát sóng không dây để có thể gửi và nhận thông tin liên lạc. Giao thức không dây IoT được thiết kế để thực hiện một số dịch vụ cơ bản: vận hành trên điện năng thấp, sử dụng băng thông thấp và làm việc trên một mạng lưới. Một số thứ được thực hiện trên băng tần 2,4 GHz hoặc Wi-Fi và Bluetooth, và các tần số phụ GHz. Các tần số phụ GHz bao gồm 868 và 915 MHz, có thể có lợi thế ít can thiệp.

Vì sao điện năng và băng thông thấp lại quan trọng với IoT?

Một số thiết bị IoT sẽ phải sử dụng hệ thống điện, chẳng hạn như ổ khóa cửa, trong khi các cảm biến độc lập sẽ sử dụng pin. Các thiết bị này gửi và nhận lượng nhỏ thông tin liên tục hoặc định kỳ. Do đó, tuổi thọ pin của một thiết bị IoT có thể dao động từ 1,5 năm đến một thập kỷ. Một nhà sản xuất thiết bị IoT, Insteon, sử dụng cả 2 cách thức giao tiếp là radio và hệ thống điện. Việc sử dụng cả 2 cách thức giao tiếp này sẽ giúp thiết bị IoT gia tăng độ tin cậy.

Vai trò của Bluetooth trong IoT?

Công nghệ Bluetooth Low Energy thích hợp với công nghệ thiết bị đeo người, bởi sự giới hạn kết nối khiến nó không phù hợp với diện rộng của các vật dụng IoT. Nhưng vào đầu năm 2014, CSR, một hãng sản xuất chất bán dẫn, đã công bố một giao thức kết nối Bluetooth mới có thể kết nối với hàng ngàn thứ.

Việc Bluetooth có mặt khắp trong các thiết bị di động, kết hợp với công nghệ Bluetooth mới sẽ tạo ra một nền tảng vô cùng có lợi cho IoT. Nếu trong phạm vi của căn nhà, Bluetooth sẽ biến chiếc smartphone thành một trung tâm điều khiển. Mặt khác, nếu bạn rời xa nhà, như đi làm chẳng hạn, thì công nghệ này là hoàn toàn vô dụng.

Theo Computerworld

We Are Complicating Things a Bit too Much (TDD & DDD)

The other day, I saw something that really encompasses one of the biggest hurdles for younger developers to get through. That hurdle is terminology. These days, there is all sorts of “___ driven development”. In our team, we even joke about “assumption driven development”.

Naming things is one of the hardest things to deal with in programming. It gets worse when we are doing something that is uncomfortable. Make it worse, before we ever started programming there are tons of people who have already named things (and naming things was hard for them too).

And see, this is where we as a community come in and can help people understand things a lot easier. Of course it doesn’t hurt if we don’t muck up some serious patterns and terminology even more (no I’m not talking about Facades, the argument over that hurt the community more).

All The Driven Development

One of the big things that bothered me the other day was hearing someone say “you thought TDD was hard, wait until you change over to using DDD”. Maybe it was out of context, but I think that at heart, a lot of young developers (especially those that are freelancing or the most senior on a small young team) get confused. It used to be information on software patterns was a bit more limited: you hear something from a close coworker, that one message board you subscribe to, a professor, a senior dev, etc then you use it. Now, we face massively social communities where everyone spouts their ideas on Twitter, G+, SO, and more. A developer that would have been slowly introduced to design patterns, Extreme Programming/Agile Development, and best practices are instead bombarded with everything at once.

So I hope to clarify some confusion of thinking that TDD (Test Driven Development) and DDD (Domain Driven Development) are somehow in competition or mutually exclusive. They aren’t in fact! So I’ll dig in to what I’ve taken out of the two methods and where they fit together.

What Is TDD

TDD is the process of writing tests based on a particular desired pieced of functionality, letting them fail. Then you write the least amount of code possible to make the test pass, then write another test. Cycle this over and over until you have finished a feature and then go back and refactor. It’s a bit simplified, I know, but this is the heart of TDD. Don’t get stuck in the mud about it! Don’t worry about techniques, code structure, etc.

Don’t let people fool you!

TDD is just the process of testing code before writing it!

What Is DDD

DDD takes a bit more explaining. You see, a Domain is a set of functionality that you are attempting to mimic that lies outside of your application. Maybe this is a physical event like an election, maybe it’s something a bit more arbitrary such as a source code project initializer. You will want to assign a Domain Expert, this could be your project manager, yourself when you put on your spec writing hat for a new side project (that is really dangerous though), but most likely, your Domain Expert will be your client. The point is, to create something, you must first understand what it is you are trying to create! DDD describes the process of consulting and deciding on explicit definitions of each piece of the domain that you are trying to mimic. And you make sure that these definitions are written before ever writing code. Then these definitions and naming structures from your domain study are used to create a more informed code base. Then when you pick your head up for the next change, error, fix, whatever: you consult with the Domain Expert and turn programming talk back into the Domain terminology.

Don’t let people fool you!

DDD is the process of being informed about the Domain before each cycle of touching code!

Mixing the Two Together

Alright, so now lets see WILL IT BLEND!? That is the question. DDD lives mostly in how you research before coding, how you name things, and how you interact with the client. On the other hand, TDD is the process of actually writing your code. So, as you can see, there is no conflict of interest between the two strategies and they fit together quite well.

Ok, I Lied… A Bit…

If you read Eric Evan’s book on Domain Driven Development and Martin Fowler’s Blog, you will see that their process of Domain Driven Development goes beyond just getting an informed view of the domain and describing it with “ubiquitous language”. They include code coverage, continuous integration, a testing strategy, and code organization as part of the DDD life cycle. While these things do happen between getting domain information from a client before coding and then translating things back to the domain expert, I’m not so sure that these are really part of DDD. Let me explain.

If as good programmers (and good DDD programmers at that), we decide to break things down into their unique single role parts we’d see a few things when examining the formation of the DDD mindset. First, is that it borrows heavily from Extreme Programming. EP was a multi pronged approach to developing software in a more iterative and maintainable fashion. It included planning phases, writing tests, using continuous integration, and more. EP claimed that code was the most important thing in a software project, and developers should be prepared to code efficiently.

Evan’s came in and took a lot from the toolbelt in front of him and created his own method to attacking software problems and he put understanding the domain first. This meant that while code was still really important, the best code in the world is only as good as the understanding of the problem you are trying to solve. At the end of the day, what makes Evan’s process unique was the communication with a Domain Expert. It’s this communication that I would argue IS DDD. Everything else was slight modifications on existing techniques.

A Word On Code Structure

I made a point that TDD as a philosophy doesn’t care how you structure your code. There are those that would probably think that this transfers to DDD as well. Unfortunately it’s a bit stranger than that. While the simplified view of DDD makes me want to say that code structure isn’t part of DDD, I can’t really tell you that. With a deep understanding of the domain and wanting to mimic the domain within the code base, your code structure will and should be influenced. Also on the other side of things, it makes the translation of software output to the domain expert much easier for you: if you are stuck remembering how this SuperDuperSoftwarePatternThing relates back to your domain, you aren’t doing yourself any favors. Stick with efficiency and mimic the language you set up when talking with the client. If things are hard to structure, that’s a sign that you may not understand the specifications for the different actors in the domain.

A Word On BDD

BDD is an implementation of TDD which brings in some aspects of DDD. BDD focuses on how to name tests and instructs that tests should start from outside interaction in. For instance if you want to go to a page and see some blog posts you’d write the test for the route, say “hey I want to see some posts”, then you’ll say “I need to get some posts from somewhere”, then keep going until collide with unit tests at the low level.

BDD also goes to the effort of giving a more conversational way of writing tests. Since many tests start from the outside of a system and work their way in, BDD lends itself well to testing a pass or fail condition for a user story or feature. This means that you describe what actors are setting up a feature to be tested and then what result is to be expected. If you caught that, you may see that BDD lends itself as a TDD strategy for teams that have a solid domain definition from DDD. BDD is also often heralded because BDD testing tools can be arguably more human readable to non-developers such as Domain Experts.

http://ryantablada.com/post/we-are-complicating-things-a-bit-too-much-(tdd-and-ddd)

Working Effectively with Legacy Code (Robert C. Martin Series)

Thêm 1 cuốn sách gối đầu cho lập trình viên, đặc biệt với những ai đang làm trên legacy system. Mọi câu hỏi, khó khăn hay những cách làm việc ưu việt với legacy code sẽ được tác giả giải đáp trong suốt 25 chương của cuốn sách này. :)

LegacyCode

From Amazon

Get more out of your legacy systems: more performance, functionality, reliability, and manageability

Is your code easy to change? Can you get nearly instantaneous feedback when you do change it? Do you understand it? If the answer to any of these questions is no, you have legacy code, and it is draining time and money away from your development efforts.

In this book, Michael Feathers offers start-to-finish strategies for working more effectively with large, untested legacy code bases. This book draws on material Michael created for his renowned Object Mentor seminars: techniques Michael has used in mentoring to help hundreds of developers, technical managers, and testers bring their legacy systems under control.

The topics covered include

  • Understanding the mechanics of software change: adding features, fixing bugs, improving design, optimizing performance
  • Getting legacy code into a test harness
  • Writing tests that protect you against introducing new problems
  • Techniques that can be used with any language or platform—with examples in Java, C++, C, and C#
  • Accurately identifying where code changes need to be made
  • Coping with legacy systems that aren’t object-oriented
  • Handling applications that don’t seem to have any structure

This book also includes a catalog of twenty-four dependency-breaking techniques that help you work with program elements in isolation and make safer changes.

 

Link download ePub:

https://drive.google.com/file/d/0BxrbqNV1YSlmckpLZW9Wc0tKNVk/view?usp=sharing

I Don’t Write Unit Tests Because…. : The Excuses

Print

As someone who’s seen the benefits of the approach, I’m a huge believer in test driven development. It adds a level of quality and maturity to the field of software development, yet it’s still not a widespread practice across development projects. When it comes to a choice between the features, time and quality, it’s always the quality that suffers. We don’t want to add extra time for testing and we don’t want to compromise on the feature set of the delivery. If you haven’t set out to do test driven development at the start of the phase, then it’s difficult to fit in.

We’ve all heard excuses for not taking the test driven approach, but nowhere compiles them better than “Pragmatic Unit Testing in Java With JUnit“, from the Pragmatic Bookshelf.  I read the book a few years ago, and afterward I thought there was no way that any responsible developer could read the book without truly believing that unit tests were one of the most important aspects of the development activity.

The most worrying excuse I’ve heard is that it’s too difficult to test my code. This can be for one of two reasons. One is that your code is mainly UI related, and automating the UI tests is too difficult. I’ll concede that UI automation is a tricky area (but not impossible, as we’ll see in a later article), but every effort should be made to automate it if possible: think about regression tests. If you have a fully automated test suite, including the UI behaviour, you can make a change to your code, and have full confidence that you haven’t broken anything if you have tests to re-run.

The second reason that your code is too difficult to test is that you’ve messed up your design. Maybe your logic and UI code are too tightly coupled, and without that automated UI layer existing, the dependency between the logic and the UI will cause a problem. This is where test driven development helps to ensure a clean design, following best practices. Using JUnit is simple, so if I can just point my test at a clean logic layer, I can test all logic that the UI will ever touch.  What’s that? You’re missing your data model? Well then just use mock objects – there are many frameworks for that!

For those who haven’t read the book yet, I’ll give a quick summary of the excuses for not testing as outlined in the introduction chapter:

I don’t have time to unit test.
Really? But you have to time to fix your mistakes later?  The simple fact is that nobody, not even you, can write bug free code.  Unit tests greatly speed up the time it takes to identify a bug because they narrow the suspect code down to a very specific unit.  Other types of testing, such as Quality Assurance or User Acceptance Testing, identify problems at a much higher level, forcing the developer to spend extra time plodding through the various units looking for the root cause. Unit tests can also serve as verification that the bug is corrected without having to go through the hassle of creating a new application build and deploying it for further testing.  Simply put, unit tests speed up overall development.  It might take slightly longer to create each new feature, but the development time for the entire application, from inception to delivery, is considerable shorter.
The client pays me to develop code, not write unit test.
News Flash – Unit tests are code.  They are as integral to the application as any other piece of code you are writing, and should be included in the original estimate and statement of work.  It might also help to mention to the client that unit tests lower both development cost (see previous excuse) as well as maintenance cost.  If you are not writing unit tests, then you are doing your client an injustice by forcing them to incur extra expense.
I am supporting a legacy application without unit tests.
That still doesn’t preclude you from writing your own.  Granted, you are not going to be able to go in and write tests to bring the entire code base up to an acceptable level of coverage.  You most likely don’t have the time or resources to do that.  But as support issues are raised, write the unit tests for any code that you modify.  When I am working on a support task, one of the very first things I do is write a unit test to recreate the bug.  When the unit test starts passing, I know that I have fixed the problem.  Now, not only have I resolved a bug, but I have added some unit test coverage to the code base.  Over time, this coverage will become more and more robust, and actually reduce future maintenance costs by preventing the introduction of regression bugs.
QA and User Acceptance Testing is far more effective in finding bugs.
No arguments there.  But Unit Testing is far more effective at preventing bugs.  Just because my smoke alarm is better at detecting potential fires doesn’t mean that it is safe to leave the stove on all the time.  QA and UAT are smoke alarms – they alert you to potential existing trouble.  Unit testing is turning off the stove when you are done – it helps you prevent trouble.  Unit testing, QA, and UAT are all important parts of the testing process, and they each play very different roles.  Trying to replace one with another doesn’t work.  I wouldn’t release a project that was well unit tested without QA, and I wouldn’t release a project that was thoroughly QA’ed without unit testing.
I don’t know how to unit test, or I don’t know how to write good unit tests.
Well, this is probably the most valid excuse that is out there.  The sad truth is that a great many number of developers and engineers do not know how to unit test.  I didn’t when I started.  And nothing discourages a developer more than struggling to write bad unit tests.  Luckily there are resources out there.  There are so many books, webinars, conference presentations, blogs, etc on writing good unit tests that the inability to write them simply isn’t going to cut it as an excuse any more.  When someone offers me this excuse, I don’t look at it as a roadblock, but rather as a teachable moment.  It usually only takes a time or two of seeing how well written unit tests can help one develop code to turn them from the dark side…

Are there other excuses out there?  Absolutely.  But they are just that – excuses.  I have yet to encounter a valid reason to skip unit testing.  If you think you have one, post it here.  I look forward to the challenge of changing your mind! :)

http://www.sundoginteractive.com/sunblog/posts/top-five-excuses-for-not-unit-testing

Lập trình theo kiểu Aspect Oriented Programming (AOP) sử dụng Spring Framework

Aspect Oriented Programming

Trong khoảng 5 năm trở lại đây, một khuynh huớng lập trình mới xuất hiện. Nó được gọi là AOP để phân biệt với kiểu lập trình OOP đã có sẵn. Nguời viết muốn cung cấp cho bạn đọc một kiến thức cơ bản về kiểu lập trình mới mẻ này, và sử dụng springframework, một open-source phổ biến, để minh hoạ. Nếu bạn muốn tìm hiểu chi tiết về cách sử dụng springframework, thì bài viết này không nhằm mục đích đó, mà chỉ trình bày khái quát về những thuật ngữ và nguyên tắc trong việc sử dụng 1 chức năng phổ biết của spring: declarative transaction .

Aspect Oriented Programming (AOP) và Object Oriented Programming (OOP)

Một điểm quan trọng nữa cần đuợc nêu ra, AOP đuợc xem là cái bổ sung cho OOP, chỗ mà OOP còn thiếu sót trong việc tạo những ứng dụng thuộc loại phức tạp. AOP khônng phải là cái thay thế OOP. Nguời lập trình bắt buộc phải quen thuộc với OOP để bắt đầu với AOP. Cũng vì lí do này mà AOP thuờng đuợc xem là dành cho các bạn đã có kinh nghiệm, chứ không dành cho các bạn mới bắt đầu làm quen với Java. Bạn có thể làm quen với OOP qua lí thuyết và sách vở từ truờng học. Và nếu bạn thấy vẫn còn lạc lối trong việc tìm kiếm những danh từ, tính từ, hay trả lời những câu hỏi ai, cái gì, làm gì… đại loại như vậy, trong việc tìm kiếm class, thì bạn có thể đọc lại những bài viết về OOP mà nguời viết đã trình bày truớc đây xem có giúp ích hơn không. Dù học OOP theo kiểu nào thì kết quả cuối cùng của OOP là chia ứng dụng thành nhiều phần nhỏ với những chức năng riêng biệt, theo kiểu các hộp đen (black box). Các hộp đen này làm cho việc tái sử dụng và bao quản chúng đuợc dễ dàng hơn….và còn thêm nhiều ưu điểm khác nữa không kể ra ở đây.

Hạn chế của Object Oriented Programming

Lấy ví dụ về transaction trong việc truy cập database. Trong Java, truy cập database đòi hỏi nhiều buớc: tạo ra Connection, bắt đầu transation, commit, clean up connection… Để đơn giản hoá, ta hãy tạo ra 1 class hay black box chuyên làm việc này.

public class DBTransaction {
     public DBTransaction () {
          //mã nguồn tạo ra Connection và bắt đầu 1 transaction
     }

     public Connection getConnection () {
          //mã nguồn return Connection đã tạo ra
     }

     public void commit () {
          //mã nguồn commit transaction và clean up resources
     }
}

Giả sử trong 1 ứng dụng, có 1 class (hay blackbox) XuatNhapHang làm chức năng xuất nhập hàng và nó chứa 1 method làm chức năng nhập hang.

public void nhapHang ( int monHang, int soLuong )
{
     //sử dụng blackbox để bắt đầu 1 transaction
     DBTransation tx = new DBTransactiơn ();

     // ...mã nguồn truy cập database để nhập hàng...

     tx.commit ();
}

Bạn dễ dàng hình dung đuợc mục đích sử dụng của blackbox này.
Mỗi khi cần truy cập database, nguời sử dụng chỉ việc gọi các public methods của nó mà không cần biết tới bên trong nó hoạt động ra sao. Việc thay đổi bên trong của blackbox cũng không làm ảnh huởng tới nguời sử dụng nó. Đó là cái đẹp của việc tạo ra blackbox. Mọi nguời hình như đều vui vẻ và độc lập với công việc của mình. Thực tế không hoàn toàn như vậy. Giả sử ứng dụng sau này đòi hỏi phải theo dõi tất cả mọi hoạt động liên quan đến việc truy cập database. Mỗi khi database đuợc truy cập, tên nguời sử dụng và thời gian sẽ đuợc lưu trữ lại. Ta có thể tạo ra 1 blackbox đơn giản chuyên làm việc này.

public class DBTruyCapTheoDoi {
     public DBTruyCapTheoDoi (Connection con, String tenNguoi) {
          //mã nguồn lưu lại trong database nguời sử dụng và thời gian
          .....
     }
}

Method nhapHang hay những nơi trong ứng dụng đã truy cập database đều phải sửa đổi như sau

public void nhapHang ( int monHang, int soLuong )
{
     //sử dụng blackbox để bắt đầu 1 transaction
     DBTransation tx = new DBTransactiơn ();
     //theo doi truy cap
     DBTruyCapTheoDoi (tx.getConnection (), tenNguoi);

     // ...mã nguồn truy cập database để nhập hàng...

     tx.commit ();
}

Sửa đổi không nhiều nhưng nó rộng khắp trong toàn ứng dụng. Đây chính là điểm làm cho nhiều nguời không hài lòng. Những nguời lập trình kì cựu đều đồng ý với nhau rằng, trong thực tế, đòi hỏi của ứng dụng luôn thay đổi theo thời gian. Cứ mỗi 1 yêu cầu mới của ứng dụng thì một (hay nhiều) blackbox đuợc tạo thành, và mã nguồn sẽ bị thay đổi rộng khắp trong ứnng dụng để sử dụng blackbox mới này. Thay đổi mã nguồn rộng khắp ứng dụng là điều tối kị vì nó đòi hỏi phải testing lại toàn bộ . Những bug xuất hiện lúc này thuờng rất khó tìm vì nguời lập trình thuờng cho rằng “mình sửa có 1 chút thì không sao…” .

Câu hỏi đặt ra là làm sao tạo đuợc một kiểu lập trình uyển chuyển với sự phức tạp của ứng dụng . Mô hình này phải cho phép tạo dựng nhanh chóng ứng dụng khi nó ở trong giai đọan đầu đơn giản, và thay đổi nhanh chóng để thích ứng kịp thời với đòi hỏi mới . OOP cho phép tạo ra những blackbox và điều này vẫn đuợc xem là không thể thiếu trong việc lập trình . Bản thân những blackbox không phải là vấn đề, mà chính việc sử dụng chúng mới là vấn đề . Việc gọi trực tiếp những public method của 1 blackbox khi phải sử dụnng nó trong mã nguồn, tạm gọi là nối cứng (hard wired) blackbox vào mã nguồn. Có cách nào để sử dụng 1 blackbox mà khônng cần gọi trực tiếp nó trong mã nguồn hay không? Cần có 1 cách nào đó để gọi gián tiếp những public method, hay tạm gọi là nối mềm (soft wired) khi sử dụng những blackbox. Đây là chỗ OOP bỏ sót, và AOP ra đời để đáp ứng nhu cầu này .

Đến đây bạn có thể thấy rằng, OOP là buớc đầu tiên cần phải có để tạo thành các blackbox. AOP là buớc kế tiếp để nối mềm những blackbox tạo thành 1 ứng dụng hoàn chỉnh . Việc nối mềm đòi hỏi nguời thiết kế phải có kinh nghiệm trong việc chia ứng dụng thành nhiều lớp (layer) để ứng dụng đuợc tạo ra và chạy 1 cách hữu hiệu . Điều này sẽ đuợc nói kĩ hơn sau đây. Một rắc rối nữa của việc nối mềm là nó thuờng đòi hỏi tạo nhiều cấu hình (configuration) phụ trợ .

Làm thế nào để nối mềm 1 ứng dụng?

Đây cũng chính là câu hỏi mà AOP cần phải trả lời . Java cho phép là đuợc việc này .

Nối mềm là cho phép sử dụng 1 class (hay blackbox) mà không cần gọi trực tiếp public methods của nó trong mã nguồn, và bạn có thể ngạc nhiên nếu biết rằng điều này thực sự đã đuợc làm từ lâu . Nó đuợc làm bởi các container chẳng hạn như của EJB, portlet, servlet. Lấy EJB container làm ví dụ, vì nó rất gần với khái niệm AOP . Muốn sử dụng 1 EJB, bạn phải gọi create… chứ không bao giờ sửng dụng new . EJB trong thực tế chạy bên trong 1 container. Container trực tiếp tạo ra instance của EJB và chuyển cho nguời sử dụng khi method create… được gọi . Mỗi lần nguời sử dụng gọi method của EJB, container sẽ đón đầu (intercept) cú gọi này, rồi mới chuyển giao cho instance của EJB . Truớc khi chuyển giao cho EJB, thuờng container có thể sẽ làm nhiều việc khác nữa …. Container có thể bắt đầu 1 transaction, hay kiểm tra xem nguời gọi có đuợc phép gọi hay không, tùy theo cách cấu hình của EJB đó bên trong file ejb-jar.xml . Nếu xem EJB là 1 blackbox thì việc nối giữa EJB và nguời sử dụng nó mặc dù là gián tiếp nhưng chưa đuợc xem là nối mềm . Bản thân EJB container có thể chứa sẵn bên trong nó 1 blackbox làm công việc bắt đầu 1 transaction, hay 1 blackbox khác làm công việc kiểm tra mức độ cho phép nguời gọi . Việc nối kết giữa EJB và những blackbox bên trong của container mới chính xác là mềm … Bản thân EJB hay nguời viết nó hoàn toàn không biết đến có tồn tại 1 blackbox có sẵn bên trong container làm công việc transaction . Nguời viết EJB chỉ việc thông tin với container qua file cấu hình ejb-jar.xml rằng phải bắt đầu 1 transaction hay làm thêm những chuyện khác mỗi khi method của nó đuợc gọi . Cũng dựa trên ý tuởng này, spring framework chính là 1 container thuộc loại nhẹ và nó có thể chứa đựng những Java object thông thuờng chứ không phức tạp như EJB .

Trở lại ví dụ xuất nhập hàng ở trên, nếu class XuatNhapHang chạy bên trong spring framework thì method nhapHang ở trên có thể viết lại như sau

public void nhapHang ( int monHang, int soLuong )
{
     // ...mã nguồn truy cập database để nhập hàng...
}

Không hề có 1 đọan mã nguồn nào bên trong method nhapHang nói đến việc tạo ra transaction hay theo doi truy cap . Bản thân các blackbox transaction và theo dõi try cập cũng đuợc chạy trong spring. Nguời viết class XuatNhapHang chỉ việc truyền đạt cho spring, qua 1 file cấu hình, rằng mỗi khi method nhapHang đuợc gọi , hãy gọi blackbox transaction và theo dõi truy cập truớc khi chuyển nó đến nhapHang . Bằng cách nối mềm này, việc bao gồm hay loại bỏ những blackbox đuợc thực hiện qua việc thay đổi file cấu hình, chứ không cần phải thay đổi mã nguồn của class XuatNhapHang . Đây chính là lập luận mạnh nhứt trong việc cho ra đời AOP . Những nguời ủng hộ AOP trong ví dụ này lí luận rằng class XuatNhap hay method nhapHang chỉ nên lo lắng việc xuất nhập hàng . Việc bắt đầu 1 transaction hay theo dõi truy cập dữ liệu , đành rằng phải có do yêu cầu của ứng dụng, cũng không nên chen lấn 1 cách thô bạo vào trong mã nguồn của class XuatNhap, hay bất kì vào trong những blackbox nào khác khi chúng cũng cần truy cập dữ liệu . Có như vậy thì 1 blackbox mới giữ đuợc tính độc lập của nó với những thay đổi của yêu cầu của ứng dụng, dẫn đến việc tái sử dụng blackbox đuợc triệt để hơn .

Những khía cạnh như transaction hay theo dõi truy cập, nếu cần phải ‘chen lấn ‘ thì mã nguồn không phải là chỗ tốt để làm . Có những chỗ tốt hơn chẳng hạn như dùng file cấu hình, và spring framework làm đúng như vậy . Công việc của spring framework là đón đầu các cú gọi và thực hiện các chức năng thể hiện qua file cấu hình . Đây cũng chính là nguyên tắc cơ bản của AOP: thay các cú gọi trực tiếp, bằn các cú gọi qua file cấu hình ..

Khó khăn khi áp dụng Aspect Oriented Programming

Đọc đến đây, có thể bạn không mấy hào hứng với AOP . Thực tế các file cấu hình cho spring hay các AOP framework khác như AspectJ không đơn giản chút nào . Gọi trực tiếp method chỉ mất 1 dòng và cũng dễ theo dỏi logic của ứng dụng . Thay đổi file cấu hình liệu có an toàn hơn thay đổi mã nguồn hay không ? Những nguời ủng hộ AOP thì cho rằng nó rất nên làm . Những nguời còn hoài nghi thì luỡng lự . Các AOP framework khác nhau đều có cách tạo file cấu hình khác nhau. Không giống như EJB có thể chạy trong mọi EJB container, ứng dụng viết cho AOP framework này không chạy đuợc trong AOP framework khác . Những cố gắng để thống nhứt các AOP framework cho đến bây giờ vẫn chưa kết thúc . Việc container đón đầu các cú gọi có thể ảnh huởng đến tốc độ chạy của ứng dụng . Thêm nữa, không phải mọi class trong ứng dụng đều cần đuợc quản lí bởi AOP framework cũng như không phải mọi class đều trở thành EJB. Nếu ứng dụng thiết kết kém cỏi có thể dẫn đến số luợng class mà AOP container phải quản lí cùng với các file cấu hình tăng lên nhanh chóng . Có lẽ cũng chính vì những lí do này mà AOP vẫn chưa cất cánh nhanh chóng như OOP . Điểm khó khăn cuối cùng nữa của AOP là lí thuyết hoá nó để truyền đạt . AOP đưa ra những khái niệm lạ hoắc khó nắm bắt, dễ làm nản lòng những nguời mới bắt đầu . Phần kế tiếp nguời viết sẽ trình bày những khái niệm cơ bản của AOP, và nguyên tắc Inversion of Control hay còn gọi là Dependency Injection của springframework trong việc triển khai AOP .

Những khái niệm trong Aspect Oriented Programming

Khái niệm đầu tiên thuờng gặp trong AOP là concern (1 mối lo âu) hay aspect ( 1 khía cạnh của vấn đề). Concern hay aspect trong AOP chỉ là 1, và nó tuơng đuơng với chức năng của 1 blackbox trong OOP. Lấy ví dụ trong ứng dụng cho 1 cửa hàng mua bán . Những yêu cầu của ứng dụng bao gồm : chi thu tài chính, xuất nhập hàng, kết toán cuối tháng, cuối năm, quản lí nhân viên … Và có thể thay đổi trong tuơng lai theo yêu cầu của thực tế . Việc chia nhỏ 1 yêu cầu cùng với OOP sẽ đưa đến các blackbox như xuất nhập hàng, transaction, hay theo dõi truy cập như ở trên . Chức năng của mỗi blackbox có thể đuợc xem là 1 khía cạnh của ứng dụng mà nguời thiết kế phải lo lắng . Tới đây bạn có thể thấy vì sao 2 chữ concern và aspect mang nghĩa không mấy ăn nhập với nhau lại đuợc dùng để chỉ chung 1 khái niệm . Có những khía cạnh, như transaction, cần phải ‘chen lấn ‘ vào các khía cạnh khác . Những khía cạnh đó đuợc gọi là crosscutting aspect, tạm gọi là những khía cạnh cắt ngang . Trong thực tế, điểm chen lấn của những crosscutting aspect không tùy tiện mà nó thuờng là các điểm đặc biệt . Ví dụ như khía cạnh trasaction chỉ chen vào khi bắt đầu method nhapHang để khởi tạo 1 transaction và lại chen vào 1 lần nữa khi method nhapHang return để commit transaction. Những điểm đặc biệt có thể kể ra là bắt đầu hay kết thúc 1 method, khi xảy ra exception. Spring chỉ cho phép chen vào các điểm đặc biệt, còn AspectJ thì cho phép hầu như vào bất cứ điểm nào trong mã nguồn . Việc chen lấn tuỳ tiện cần được cân nhắc kĩ lưỡng và thuờng đuợc khuyên nên tránh . Điểm chen lấn đuợc gọi là joinpoint (điểm nối ) . Tập họp các điểm nối đuợc gọi là pointcut (cắt điểm). Bản thân blackbox XuatNhapHang bị chen lấn nên nó đuợc gọi là target object. Bản thân blackbox transaction, triển khai transaction aspect, làm công việc chen lấn, đuợc gọi là advice. Vì có nhiều điểm chen lấn đặc biệt kể trên, nên có nhiều loại advice.
_ Around advice: là loại chen vào truớc khi cú gọi đuợc chuyển tới method và sau khi method thực hiện xong. Transaction advice chính là loại này
_ Before advice: chen vào truớc khi cú gọi đuợc chuyển tới method
_ Throws advice: chen vào khi bản thân method thows excpetion
_ After returning advice: chen vào sau khi method thực hiện và không có exception .

Ta có thể hình dung như sau: mỗi khi ứng dụng gặp điểm ‘chen lấn ‘, container sẽ đón đầu cú gọi và chạy mã nguồn của advice cho điểm ‘chen lấn ‘ đó rồi mới chuyển giao cho method.
Tới đây có lẽ khá đủ cho nguời mới làm quen với AOP. Để thực sự có thể lập trình theo kiểu AOP, bạn cần phải thông thạo với 1 AOP framework như spring hay aspectJ, đặc biệt là cách tạo file cấu hình cho mỗi framework.

Trở lại spring framework, nó đuợc đề cập ở đây vì 1 chức năng phổ biến : declarative transaction, giống như trong EJB, nhưng đơn giản và dễ sử dụng hơn nhiều. Để sử dụng chức năng này, chỉ cần khái niệm của AOP ở trên là đủ . Khi sử dụng spring, 1 khái niệm luôn gặp phải là Inversion of Control (IoC) hay còn gọi là Dependency Injection, nó sẽ đuợc nói rõ duới đây .

Inversion of Control (IoC) và Dependency Injection (DI)

IoC (tạm dịch là đảo nguợc kiểm soát) thuờng được thực hiện bởi các loại container như servlet, portlet, hay EJB.

Lấy ví dụ EJB container, nguời sử dụng không trực tiếp tạo ra instance của EJB, mà container tạo ra nó và chuyển giao nguời sử dụng khi cần tới . Khi nào instance đuợc tạo ra nằm ngoài sự kiểm soát của nguời sử dụng . Container có thể tạo instance ra truớc và chờ đến khi nguời sử dụng, hoặc tạo ra ngay vào lúc đuợc cần tới . Tên gọi IoC cũng nhằm chỉ lí do này: container giành sự kiểm soát từ nguời sử dụng (trong lập trình thông thuờng nguời sử dụng giành kiểm soát bằng cách gọi new để tạo ra instance). Khi instance của EJB đuợc tạo ra và trong truờng hợp session bean, container luôn gọi method setSessionContext(SessionContext sc) để cho EJB sử dụng SessionContext của nó .

Tuơng tự trong servlet container, khi instance của servlet đuợc tạo ra, container luôn gọi method init(ServletConfig sc) để servlet sử dụng . Trong cả 2 truờng hợp, container đuợc xem là nạp (injection) cho instance cái mà nó cần . Cái đuợc cần như SessionContext hay ServletConfig gọi là dependency .

Tên gọi Dependency Injection cũng từ đây mà ra. Có 1 điểm bất đồng giữa cách gọi tên Dependency Injection và định nghĩa về mối quan hệ giữa class trong UML . Dựa trên tài liệu “Mastering UML with Rational Rose” thì instance của EJB sử dụng SessionContext nên instance mới chính là dependency phụ thuộc vào SessionContext . Nếu bạn giải thích đuợc tại sao có sự bất đồng này thì làm ơn cho mọi nguời cùng biết . Dù sao thì chúng ta không nên mất quá nhiều thời giờ cho vấn đề định nghĩa và tên gọi ở đây .

Aspect Oriented Programming trong Spring Framework

Muốn sử dụng Spring, ít nhiều bạn phải làm quen với Spring container . Class tiêu biểu cho spring container là ApplicationContext . Nguời sử dụng phải trực tiếp tạo ra instance của spring container truớc khi có thể sử dụng những object mà nó chứa . Có nhiều cách tạo ra spring container. Cách thông thuờng nhứt là (những ví dụ theo sau đuợc dựa trên tài liệu spring-reference.pdf vversion 1.2.8)

ApplicationContext ac = new ClassPathXmlApplicationContext( new String[] {"applicationContext.xml", "applicationContext-part2.xml"});

Spring container tạo ra theo cách này sẽ đọc những file config duới dạng xml để load tất cả các Java ojbect mà nó cần quản lí .
Như ví dụ sau

<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>

ExampleBean và ExampleBeanTwo có thể là những java object thông thuờng chứ không có gì đặc biệt . Muốn truy cập chúng qua container rất dễ dàng

ExampleBean eb = (ExampleBean)ac.getBean("exampleBean");
ExampleBeanTwo eb2 = (ExampleBeanTwo)ac.getBean("anotherExample");

Theo mặc định thì container sẽ tạo ra singleton instance cho mỗi bean, có nghĩa là chỉ 1 instance của 1 bean đuợc tái sử dụng cho những lần gọi sau . Ta có thể sử dụng Dependency Injection của container như sau

<bean id="exampleBean" class="examples.ExampleBean">
<property name="beanOne"><ref bean="anotherExampleBean"/></property>
<property name="beanTwo"><ref bean="yetAnotherBean"/></property>
<property name="integerProperty"><value>1</value></property>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean {
     private AnotherBean beanOne;
     private YetAnotherBean beanTwo;
     private int i;

     public void setBeanOne(AnotherBean beanOne) {
          this.beanOne = beanOne;
     }
     public void setBeanTwo(YetAnotherBean beanTwo) {
          this.beanTwo = beanTwo;
     }
     public void setIntegerProperty(int i) {
          this.i = i;
     }
}

Bản thân ExampleBean phải chứa những setter method để container gọi ngay sau khi tạo ra chúng và truớc khi chuyển cho nguời sử dụng .

Nguyên tắc sử dụng spring container chỉ đơn giản như vậy thôi . Nó cho phép định nghĩa qua file cấu hình những logic phức tạp hơn mà bài viết không trình bày hết ra đây . Tới đây bạn có thể thắc mắc như vậy thì sức mạnh của spring nằm ở đâu ? Tiện lợi chỉ đơn giản như vậy thì có đáng đuợc sử dụng không ?

Cần nhắc lại 1 nguyên tắc làm việc của mọi dạng container là đón đầu những cú gọi . Spring làm việc này bằng cách dựa trên file cấu hình để tạo ra những object phụ trợ khác vào lúc runtime . Cái return từ method getBean đuợc xem là 1 proxy đuợc tạo ra vào lúc runtime của java object mà spring quản lí . Đằng sau proxy này là 1 tập họp các phụ trợ object thực hiện các chức năng theo yêu cầu của file cấu hình truớc khi chuyển cú gọi tới cho java object . Chẳng hạn như các phụ trợ object có thể chạy mã nguồn của advice, hoặc bắt đầu 1 transaction, một chức năng đã được làm sẵn trong Spring, sẽ đuợc nói tới duới đây .

Trong việc truy cập database, ứng dụng có thể sử dụng các phuơng pháp khác nhau như: trực tiếp sử dụng SQL, Hybernate, EJB Entity … Để đơn giản hoá vấn đề, ta lấy truờng sử dụng SQL để cập nhật database .

Trong Java, connection thường tạo ra duới dạng connection pool hay DataSource, là 1 tập họp những connection để xử dụng chung cho toàn bộ ứng dụng . Giả sử apache datasource đuợc xử dụng ở đây . Để container có thể tạo ra datasource cho oracle, ta xử dụng cấu hình duới đây

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@//localhost:1521"/>
<property name="username" value="scott"/>
<property name="password" value="tiger"/>
</bean>

Nếu datasource đã đuợc tạo ra xử dụng JNDI, ta có thể dùng cấu hình

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryB ean">
<property name="jndiName" value="jdbc/jpetstore"/>>
</bean>

Bản thân datasource sẽ đuợc xử dụng bởi 1 transaction manager, 1 object quản lí transaction có sẵn trong spring như sau:

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSou rceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

Trở lại class XuatNhapHang ở trên, muốn spring tự động bắt đầu và kết thúc 1 transaction mỗi khi 1 method của class này đuợc gọi , cần có những cấu hình cho pointcut hay advice khá phức tạp theo kiểu AOP . Rất may là spring đã gói gọn trong 1 class TransactionProxyFactoryBean đơn giản sau

<bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor .TransactionProxyFactoryBean">
<property name="transactionManager" ref="txManager"/>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED, -MyException</prop>
</props>
</property>
</bean>

Chúng ta sẽ kiểm tra kĩ lưỡng cấu hình này .
_ abstract=”true” có nghĩa là ứng dụng không đuợc phép gọi method getBean(“txProxyTemplate”) . Cấu hình này chỉ đuợc dùng để các cấu hình khác extends . Giống như tính chất inheritance trong OOP
_ property name=”transactionManager” ref=”txManager” cho biết class TransactionProxyFactoryBean sẽ đuợc nạp bean txManager bởi container.
_ prop key=”*” : áp dụng cho mọi method của những class extends cấu hình này
_ PROPAGATION_REQUIRED: bắt đầu 1 transaction nếu chưa có hoặc tiếp tục 1 transaction có sẵn .
_ MyException: nếu method throw MyException, thì dấu – đằng truớc sẽ làm transaction bị rollback, và dấu + sẽ commit transaction truớc khi throw MyException.

Cấu hình của class XuatNhapHang sẽ extends cấu hình này như sau:

<bean id="xuatNhapBean" parent="txProxyTemplate">
<property name="target">
<bean class="XuatNhapHang"></bean>
</property>
</bean>

Ứng dụng có thể xử dụng class XuatNhapHang như sau:

XuatNhapHang xnh = (XuatNhapHang)ac.getBean("xuatNhapBean");
xnh.nhapHang(...);

Bằng cách extends cấu hình của txProxyTemplate qua việc sử dụng attribute parent, mỗi khi 1 method của class XuatNhapHang đuợc gọi, 1 transaction sẽ tự động bắt đầu và kết thúc . Transaction sẽ bị rollback trong truờng hợp exception MyException xảy ra . Spring còn giúp mô hình JDBC operations như những java object qua việc xử dụng class MappingSqlQuery, SqlUpdate, StoredProcedure .

Bài viết đuợc kết thúc ở đây chỉ với mục đích giúp bạn làm quen với những khái niệm của AOP . Vì AOP là cái bổ sung cho những thiếu sót của OOP, AOP không dành cho những bạn mới bắt đầu lập trình . Spring framework mặc dù triển khai AOP đơn giản hơn so với AspectJ, cũng khác phức tạp với rất nhiều chức năng có thể dùng đuợc, và nguời viết chỉ tóm tắt 1 chức năng phổ biến nhất của nó là declarative transaction. Dù sao thì bạn vẫn cần tham khảo những tài liệu huớng dẫn về Spring truớc khi có thể dùng nó 1 cách hữu hiệu . Chúc bạn may mắn.

Sưu tầm