Thứ Năm, 25 tháng 4, 2019

Lambda expression trong Java 8

Nếu là 1 java developer, và đã xài qua java 8, chắc chắn bạn sẽ nghe nói tới lambda expression. Đây là 1 phần mới đc thêm vào trong java 8, và nó thực sự rất hữu ích nếu chúng ta biết tận dụng để viết code. Nhưng trước khi đi vào tìm hiểu lambda expression, bạn phải tìm hiểu một khái niệm khác gọi là : Functional interface.

Functional interface là gì?

Functional interface là một interface chỉ có chứa 1 abstract method duy nhất.
Ví dụ, interface dưới đây là functional interface.
public interface MyInterface{
    double getValue();
}
Còn interface dưới đây không phải là functional interface, vì có đến 2 abstract method.
public interface MyInterface{
    double getValue();
    double getUnit();
}
Trước khi có lambda expression, để sử dụng MyInterface chúng ta phải code như sau:
MyInterface myInterface = new MyInterface() {
   @Override
   public double getValue() {
    return 1;
   }
  };
Đoạn code trên chúng ta đã hiện thực hàm getValue để trả về 1. Nhưng vấn đề nằm ở chỗ, MyInterface của chúng ta chỉ có duy nhất 1 method nên việc ta phải viết lại cả tên method khi hiện thực là thừa thải. Đó là lí do ông nội lambda expression ra đời.

Lambda expression

Lambda expression trong java là dấu "->", nó giúp ta định nghĩa 1 method bằng dòng code siêu ngắn gọn vì ta không cần phải viết lại tên method đang hiện thực nữa. Ví dụ ta có hàm getValue() khi viết bình thường sẽ như sau:
double getValue(){ return 1;}
Nhưng với lambda expression ta sẽ viết được như sau:
() -> 1
Bạn thấy đấy, code của chúng ta ngắn hơn 1 cách đáng kinh ngạc. Minh sẽ đưa ra một số ví dụ cho những trường hợp khác khi ta dùng lambda expression. Ví dụ ta có 1 method void như sau:
void sayHello(){
    System.out.println("Hello");
}
Method trên có chức năng viết ra "hello". Với lambda expression, ta có thể viết như sau:
() -> System.out.println("Hello");
Cũng với method trên, nếu ta muốn in ra hai dòng là "hello" và "world" thì ta có:
void sayHello(){
    System.out.println("Hello");
    System.out.println("World");
}
Với lambda expression:
() -> {
    System.out.println("Hello");
    System.out.println("World");
}
Với method có parameter như method sau:
void sayHelloToPerson(String name){
    System.out.println("Hello" + name);
}
Ta cũng viết lại được dưới dạng lambda expression:
(name) -> System.out.println("Hello" + name);
Vậy ta dùng lambda expression như thế nào? Lamdba expression ko phải cứ muốn tùy tiện viết là viết đâu nhé. Nó phải viết dựa trên 1 functional interface đã có từ trước. Ví dụ với "MyInterface" của chúng ta, bây giờ ta có thể sử dụng nó bằng cách:
MyInterface myInterface = () -> 1;
myInterface.getValue(); //trả về 1
Với lamdba expression, mọi thứ đều trở nên ngắn hơn, vậy nên hãy tin dùng lambda expression.!!! Ta đi tiếp 1 ví dụ nữa nhé! Giả sử ta có 1 functional interface thứ 2.
public interface MyInterface2{
    double sum(double a, double b);
}
Ta có thể sử dụng interface này với lambda expression như sau:
MyInterface2 myInterface2 = (a,b) -> a+b;
System.out.println(myInterface2.sum(2, 3)); //in ra 5.0
Chúng ta cũng có thể tạo ra 1 interface generic hơn như sau:
public interface MyInterface {
 public T func(T t);
}
Từ đó ta có thể định nghĩa interface đó với đầu vào là String
MyInterface myInterface1 = (str) -> str + " is my friend";
System.out.println(myInterface1.func("cuong")); //cuong is my friend
Hoặc có thể định nghĩa parameter là Double
MyInterface myInterface2 = (number) -> number + 1;
System.out.println(myInterface2.func(1.0)); //2.0

Lambda expression kết hợp Stream trong java 8

Java 8 không chỉ show hàng mỗi ông nội lambda, mà còn cho chúng ta thêm 1 thứ nữa gọi là stream. 2 cái mới này nếu ta biết sử dụng nó thì đúng là code ngắn 1 cách kinh khủng khiếp. Ví dụ ta có đối tượng:
public static class Name {
  public Name(String first, String last){
   this.firstName = first;
   this.lastName = last;
  }
  private String firstName;
  private String lastName;
  public String getFirstName(){
   return firstName;
  }
  public String getLasttName(){
   return lastName;
  }
 }
Và ta có danh sách 3 người như sau:
List names = Arrays.asList(new Name("le","cuong"),new Name("nguyen","hung"),new Name("le","nam"));
Nhiệm vụ bây h là ta sẽ lọc ra những người họ "le", và sau đó in ra tên (lastname) của người đó. Nếu như không sử dụng stream và lambda expression, thực sự lượng code của bạn phải viết ra khó có thể viết được trong 1 hay 2 dòng. Nhưng với stream + lambda expession, code của bạn siêu đẹp và siêu ngắn như sau:
System.out.println(names.stream().filter(name -> name.getFirstName().equals("le")).map(name -> name.getLasttName()).collect(Collectors.toList())); //in ra [cuong, nam]
Quá ngắn đúng không? Và đó là lambda expression. Bài viết được viết dựa trên bài viết của programiz. Cám ơn mọi người đã ghé đọc.
Share:

Thứ Sáu, 28 tháng 12, 2018

Facade Pattern

Đây là 1 pattern rất dễ, cái khó nhất có lẽ là đọc cho đúng tên pattern. =)) Facade không đọc là "pha cây" , đọc đúng phải là "phờ sát" (fəˈsɑːd).
oke. Vậy pattern này là gì. Đơn giản lắm! Bạn thử tưởng tượng trong quá trình bạn code, bạn sẽ phân chia các class khác nhau. Theo thời gian thì sẽ có rất nhiều class khác nhau, và các class này sẽ tương tác qua lại với nhau. Điều này thật sự rất rối rắm.
Và để giảm bớt sự rối rắm này, Facade pattern được áp dụng vào. Nó sẽ tạo ra 1 lớp Facade bao bọc bên ngoài, và xử lý những kết nối rối rắm dùng chúng ta.
Facade Pattern: tạo ra 1 giao diện cao hơn giúp người dùng dễ dàng sử dụng các giao diện thấp
Okie. Bây giờ ta vào ví dụ và code nào. Giả sử ta đang thiết kế 1 chiếc xe hơi chẳng hạn. Chiếc xe hơi thì có thể rẻ phải rẻ trái. Vậy lúc rẻ thì nó sẽ làm gì?
Đầu tiên nó sẽ nhả chân côn để mô tơ của xe chạy chậm lại tránh cho xe bị lật khi qua. Sau đó nó sẽ xoay vô lăng để cho bánh xe rẻ qua 1 bên. Đếm sơ sơ qua là ta có 4 class rồi đấy.
public class Wheel {   
   public void turnLeft() {
      // re trai
   }
   public void turnRight() {
      // re phai
   }
}
public class Steering {  
   private Wheel wheel = new Wheel(); 
   // xoay vo lang nguoc kim dong ho
   public void rotateLeft() {
      this.wheel.turnLeft();
   }
   // xoay vo lang cùng chieu kim dong ho
   public void rotateRight() {
      this.wheel.turnRight();
   }
}
public class Motor{   
   public void speedUp() {
      // tang toc
   }
   public void slowDown() {
      // giam toc
   }
}
public class Pedal {  
   private Motor motor = new Motor();    
   public void pressDown() {
      this.motor.speedUp();
   }
   public void release() {
      this.motor.slowDown();
   }
}
Thay vì mỗi lần rẻ trái, chúng ta phải xử lý rối rắm các class trên thì thay vào đó, ta sẽ tạo 1 cái facade Car cho dễ xài.
public class Car{   
   private Steering steering = new Steering();
   private Pedal pedal = new Pedal();
   public void turnLeft() {
      this.steering.rotateLeft();
      this.pedal.release();
   }
   public void turnRight() {
      this.steering.rotateRight();
      this.pedal.release();
   }
}
Giờ muốn xe rẻ phải thì chỉ cần.
Car car = new Car();
car.turnRight();
Share:

Adapter Pattern

Mấy năm gần đây, Apple loại bỏ jack tai nghe rồi, chắc mọi người cũng biết. Vậy mọi người có biết nếu ko có jack tai nghe, thì làm sao để nghe được bằng tai nghe có dây bình thường không? Rất đơn giản, người ta chế ra 1 cục chuyển đổi từ đầu cắm jack tai nghe thành đầu sạc iphone, và cái cục này có thể gọi nó là Adapter.
Trong design pattern, chúng ta cũng có 1 khái niệm tương tự. Chúng ta có 1 đoạn class A, muốn xài 1 method x của class B. Nhưng vì lí do nào đó, A không thể xài B được.
Thì chúng ta sẽ tạo ra 1 adapter trung gian là class C. Và nhờ vào class C, A có thể xài được B.
Adapter Pattern: cho ta 1 lớp có thể xài 1 class khác dựa vào interface của nó
Vấn đề ở đây là, tại sao lại có chuyện A không thể xài B được? Chỉ cần khởi tạo B trong A và xài thôi, có gì khó khăn đâu?
Nhưng nếu bạn làm dự án thực tế, bạn sẽ gặp phải những trường hợp mà bạn ko có quyền sửa code. Tức là project của bạn có xài class A, nhưng bạn không có quyền sửa code của A. Lí do là vì, A có thể là thư viện có sẵn, hoặc A có thể là code của một nhóm khác lập trình....
Pattern này quá đơn giản, nên mình sẽ đi vào code luôn.
public class A {
   private ICanUse canUseInterface;
   public A (ICanUse canUseInterface) {
      this.canUseInterface = canUseInterface;
   }
   private void doSomething() {
      this.canUseInterface.request();
   } 
}
Đầu tiên, ta thấy ở trên, ta có class A đã được lập trình sẵn là xài hàm request của ICanUse. Nhưng bây giờ ta lại muốn A xài được hàm specificRequest của interface B dưới đây.
public Interface B {   
   public void specificRequest();
}
public class randomB implements B {
   public void specificRequest() {
      //do something 
   }
}
Để làm được điều này, ta tạo 1 cái Adapter
public class Adapter implements ICanUse {   
   private B b;
   public A (B b) {
      this.b = b;
   }
   public void request() {
       b.specificRequest();
   }
}
Vậy thôi, giờ muốn A có thể xài được specificRequest của B thì chỉ cần.
A a = new A(new Adapter(new B()));
a.request();
Share:

Command Pattern

Thế giới đã đi vào giai đoạn công nghệ bốn chấm không, mọi thứ đều là công nghệ, áp dụng công nghệ vào tất cả mọi thứ trong cuộc sống. Và hiển nhiên, IoT , internet of thing là một xu hướng tất yếu. Và bạn chuẩn bị khởi nghiệp với công ty iot, sản xuất thiết bị thông minh điều khiển mọi thứ trong nhà. Ồ, nghe xịn sò nhể. Cơ mà đầu tiên phải thử làm một sản phẩm đề mô nhỏ nhỏ trước xem có khả thi không đã nào.
Thử tưởng tượng xem, nếu mình dấn thân vào lĩnh vực Smart Home, kết nối tất cả thiết bị ở trong nhà thì nên bắt đầu như nào nhỉ? À đúng rồi, trước hết phải có 1 cái remote dùng để điều khiển. Remote này có thể điều khiển bóng đèn tắt mở nên nó sẽ có 2 cái nút, 1 nút để tắt, 1 nút để mở.
Nhưng trong nhà thì có nhiều loại bóng đèn lắm, phòng khách có đèn chùm, nhà vệ sinh có đèn huỳnh quang, phòng ăn có đèn sợi tóc... cả tá đèn thì lập trình thế quái nào được đây.
A, đúng rồi. Học strategy pattern rồi, áp dụng vào thôi! Đơn giản quá đi mà.
Mình sẽ tạo 1 interface là ILight, interface này có 2 chức năng là on và off. Từ đó có thể tạo bao nhiêu hiện thực của nó tùy thích.
Bây giờ rất đơn giản, chỉ cần ghán 1 nút của remote chạy hàm on, 1 nút chạy hàm off là được. Muốn điều khiển bóng đèn nào thì chỉ cần set hiện thực của nó vào remote là được. Đơn giản.
Nhưng, ngoài bóng đèn ra còn có nhiều cái khác nữa mà. Ví dụ muốn điều khiển cả tivi nữa. Bấm 1 nút là chuyển kênh, bấm 1 nút là tăng âm lượng. Hừm, ok. Mình sẽ áp dụng strategy pattern tương tự cho tivi để phù hợp với các hãng tivi khác nhau như samsung, sony, lg.... Sau đó gia công thêm vài nút lên cái remote, rồi cài đặt cho những nút đó là xong.
Nhưng trong nhà lại có thêm máy giặt, tủ lạnh, quạt điện.... với biết bao nhiêu là chức năng cần thực hiện. Mà mỗi vật như thế thì có biết bao nhiêu là loại khác nhau. Giờ làm sao đây nhỉ? Nếu cứ thiết kế theo kiểu trên chắc người ta cười vào mặt công ty mình mất.
Và đây chính là lúc, chúng ta nên áp dụng Command Pattern.
Command Pattern: đóng gói mỗi yêu cầu thành những đối tượng để bạn có thể tương tác với những đối tượng bằng những yêu cầu khác nhau. Thể loại Pattern này cũng cho phép ta thực hiện được cái hành động undo nữa
Okie! như thường lệ, ví dụ sẽ làm sáng tỏ định nghĩa.
Với ví dụ smart home ở trên, thay vì ta gộp từng loại thiết bị theo strategy pattern, chúng ta sẽ phân chia mỗi yêu cầu thành từng đối tượng. Có nghĩa rằng, với yêu cầu bật đèn - nó sẽ là 1 đối tượng. Yêu cầu tắt đèn - nó cũng sẽ là 1 đối tượng. Yêu cầu chuyển kênh, tăng âm lượng, giặt áo quần, sấy áo quần, bật quạt, tắt quạt.... - mỗi yêu cầu như thế sẽ là 1 đối tượng.
Và bây giờ, những nút bấm của remote không cần phải gán từng chức năng riêng lẽ như on off nữa, mà nó sẽ được gắn vào những đối tượng đã nêu trên. Ví dụ, nếu gọi cái remote là invoker, các thiết bị của chúng ta là receiver, chúng ta sẽ có mô hình Command pattern sau đây.
Quá trình hoạt động sẽ như sau. Đầu tiên ta sẽ set nút bấm trên remote sẽ làm command nào. Sau đó, khi ta bấm nút, class command sẽ trigger hoạt động execute để gọi tới action của thiết bị. Vậy thôi.
Để rõ hơn, ta đi vào vài dòng code nhé. Đầu tiên là ta có các thiết bị.
public interface Light {
   public void turnOn();
   public void turnOff();
}
public interface Tivi{
   public void nextChannel();
}
public class PhilipLight implements Light{
   public void turnOn() {
       // bat bong den
   }   
   public void turnOff() {
       // tat bong den
   }
}
public class SamsungTivi implements Tivi{
   public void nextChannel() {
       // chuyen kenh
   }   
}
Tiếp đến ta tạo interface cho ICommand và các hiện thực của nó.
public interface ICommand {
   public void execute();
   public void undo();
}
public class TurnLightOnCommand implements ICommand {
   private Light lightl
   public void TurnLightOnCommand (Light light) {
       this.light = light;
   }
   public void execute() {
       this.light.turnOn();
   }
   public void undo() {
      // redo turn on.
   }
}
public class SwitchChannelCommand implements ICommand {
   private Tivi tivi
   public void TurnLightOnCommand (Tivi tivi) {
       this.tivi = tivi;
   }
   public void execute() {
       this.tivi.nextChannel();
   }
   public void undo() {
      // redo next channel.
   }
}
Tiếp đến bạn tạo ra invoker cho cái điều khiển.
public class Invoker {
   private ICommand command;
   public void setCommand(ICommand command) {
       this.command = command;
   }
   public void press() {
      this.command.excute();
   }
}
Bây giờ giả sử bạn muốn nút bấm trên remote của bạn dùng để bật bóng đèn Philip ngoài phòng khách chẳng hạn. Bạn chỉ cần:
ICommand command = new TurnLightOnCommand(new PhilipLight());
Invoker invoker = new Invoker();
invoker.setCommand(command);
Và khi bạn ấn nút thì chạy dòng code sau.
invoker.press();
Vậy là xong. Đó chính là Command Pattern. Còn về phần mà undo tác vụ ấy. Chúng ta hãy liên tưởng tới các phần mềm kiểu như photoshop, khi chúng ta thực hiện thao tác nào đó, thì hãy xem mỗi thao tác đó là 1 command. Ví dụ ta vẽ 1 đường thẳng, chính là 1 command, chúng ta đổi màu cũng là 1 command.... Để tổ chức các command đó thì chúng ta có thể dùng stack để lưu, và khi ta ctrl + Z, thì chỉ cần bóc cái command trong stack ra và chạy hàm undo là được. Rất đơn giản.
Share:

Singleton Pattern

Singleton Patern, một pattern supper thú vị sắp được giới thiệu với bạn đây. Tại sao lại thú vị ư? Để mình nói cho bạn nghe nhé: singleton pattern có hàm constructor là private. Nhớ nhé, private đấy! Không phải public đâu. Vậy tại sao lại là private, chúng ta phải đến với tình huống cụ thể mới biết hiểu được nhé.
Okie, giả dụ bạn là một nhà lập trình trang web gạ "cờ hịch" online. Những người nào muốn được cờ hịch free thì sẽ lên trang web của bạn tìm đối tượng. Vậy đầu tiên chúng ta sẽ phải có 1 cái gì nhỉ? À, chúng ta phải có 1 cái room chát tổng, để cho những con trống vào gạ gẫm những con mái, hoặc con trống gạ con trống ._.. Nói chung là ko quan trọng, quan trọng là chúng ta phải có 1 cái room chat tổng để tất cả mọi người cùng vào. Và trang web của chúng ta có rất nhiều theme nhé, có theme hồng cánh sen cho mấy chị bánh bèo, có theme đen mạnh mẽ cho mấy anh 6 múi.... Nhưng dù ở theme nào đi nữa, chúng ta cũng chỉ sử dụng chung 1 cái room chat duy nhất mà thôi, đó là room chat tổng.
Vậy làm sao để từ 1 class room chat, chúng ta chỉ có thể tạo 1 đối tượng room chat duy nhất, mà không thể tạo ra 1 cái room chat thứ 2. Nghe có vẻ khoai đúng ko? Nếu đã là 1 lớp, thì chúng ta làm quái nào mà ngăn người ta tạo ra nhiều hơn 1 thực thể được. Và đây chính là lúc Singleton Pattern được sử dụng.
Singleton Pattern: đảm bảo rằng 1 lớp chỉ có 1 thực thể duy nhất và cho phép ta truy cập ở cấp độ toàn cục
Tức là sao? Tức là: chúng ta có 1 class A nào đó chẳng hạn, thì nếu thiết kế theo singleton pattern, chúng ta không thể nào tạo ra được 2 thực thể khác nhau của A. Nghe có vẻ lạ đúng không, nhưng điều này là hoàn toàn có thể. Bằng cách nào? Haha, đây là lúc bạn nhìn thấy private constructor. Nghe thôi đã thấy hài vl =)) Cấu trúc của 1 singleton pattern.
1 class A thiết kế theo của singleton pattern, sẽ có 1 biến static có kiểu A, 1 hàm constructor private, và 1 hàm createSingleton static. Vì constructor là private, nên chúng ta không thể tạo thực thể cho class bằng lệnh new A(); được, mà thay vào đó, chúng ta sẽ phải dùng hàm createSingleton để khởi tạo 1 thực thể. Và tất nhiên, trong hàm createSingleton này, chúng ta sẽ trả về 1 biến static duy nhất. Điều này ngăn cản việc khởi tạo 2 thực thể khác nhau của 1 lớp.
OK! Vào code để xem thằng singleton này nó được code như nào xem nào. Đầu tiên là khởi tạo 1 lớp ChatRoom, với thiết kế của singleton như trên.
public class ChatRoom {
   private static ChatRoom chatRoom;
   private ChatRoom() {
   }
   public static ChatRoom createChatRoom(){
   
   }
}
Bây h ta chỉ cần viết logic cho createChatRoom là xong.
public class ChatRoom {
   private static ChatRoom chatRoom;
   private ChatRoom() {
   }
   public static ChatRoom createChatRoom(){
       if(chatRoom == null) {
           chatRoom = new ChatRoom();
       }
       return chatRoom;
   }
}
Vậy là xong. Nếu bạn muốn khởi tạo 1 ChatRoom thì chỉ cần.
ChatRoom chatRoom = ChatRoom.createChatRoom();
Lần thứ 2 bạn khởi tạo ChatRoom bằng câu lệnh trên, thì nó vẫn trả về cho bạn đúng 1 thực thể duy nhất. Và đó chính là Singleton Pattern. Chúc trang web gạ cờ hịch của bạn không bị công an bắt và kiếm được nhiều tiền nhé!
Share:

Thứ Năm, 27 tháng 12, 2018

Abstract Factory Pattern

Nếu bạn đã được đọc về Factory Method Pattern, thì mình nghĩ Abstract Factory Pattern chỉ là 1 cái trường hợp đặc biệt của Factory Method Pattern mà thôi.
Abstract Factory Pattern: cung cấp 1 interface cho phép người dùng tạo ra 1 nhóm các đối tượng độc lập hoặc có liên hệ với nhau mà không cần quan tâm tới lớp thực thi của chúng
Nghe có vẻ bù cả đầu đúng không? Nhưng mà sự thật thì cực kì đơn giản.
Quay lại với ví dụ trong bài Factory Method Pattern, chúng ta có cái game phi thuyền bắn súng đấy. Bạn để ý nhé, trong game lúc nào cũng có background (tức là cái hình nền ấy) nó xuất hiện khác nhau với mỗi level. Ví dụ với level 1, mục tiêu là bắn các mảnh thiên thạch thì background sẽ là bầu trời đêm chẳng hạn. Level 2 bắn phá các phi thuyền thì background sẽ là các linh kiện máy móc... vân vân và mây mây. Và tất nhiên, không phải background của chúng ta là random được, nó phải có lý với bối cảnh của enemy. Không phải tự dưng enemy là các mảnh thiên thạch, mà background lại là đồng cỏ được, nó không có phù hợp gì hết. Hay là enemy là mấy con bò, mà background lại là trên dải ngân hà, đúng lafk hông hợp lý. Vậy nên, ứng với mỗi enemy, chúng ta phải có 1 background đi kèm. Vậy để làm được thế thì ta không thể tạo 2 cái Factory riêng biệt cho enemy và background được. Vì có thể, trong tương lai, không những chỉ có background mà có thể có các hiệu ứng mây mưa, sương khói các thứ cũng phải làm sao cho hợp lý. Như vậy sẽ dẫn tới chúng ta có vô vàn Factory riêng lẻ. Và Abstract Factory Pattern lúc này mới được đưa vào sử dụng.
Cùng xem lại mô hình củ Factory Method Pattern nhé.
Ta thấy, mỗi factory sẽ tạo cho chúng ta 1 đối tượng là IProduct. Nhưng ở bài toán hiện tại, chúng ta không chỉ có 1 product (là enemy) nữa, mà chúng ta có đến 2, hoặc 3 đối tượng cơ. Vậy nên, Abstract Factory Pattern cải tiến lại, giúp cho Factory có thể cho ra được 2 nhiều hơn 1 product.
Ví dụ ta có 2 interface IProductEnemy và IProductBackground, mô hình cảu Abstract Factory Pattern sẽ như sau:
Ta thấy, bây h factory của chúng ta đã có thể cho ra 2 product. Và tất nhiên, khi hiện thực IFactory, thì chúng ta cũng đã định nghĩa được những background nào nên đi kèm với enemy nào rồi, nên sẽ ko có hiện tượng bất hợp lý ở UI khi người ta chơi game cả. Vậy thôi, đó là Abstract Factory Pattern.
Bây h thử code vài dòng để hiểu hơn nó là như thế nào nà. Đầu tiên vẫn như cũ, ta tạo interface của enemy và các hiện thực của nó.
public interface Enemy{
    public void speed();
    public void healthy();
} 
public interface FirstLevelEnemy extends Enemy{
} 
public interface SecondLevelEnemy extends Enemy{
}
public class BigMeteorite implements FirstLevelEmemy {
   public void speed() {
       //lam cai gi do
   }
    public void healthy(){
       //lam cai gi do
   }
}
public class VietNamSpaceShip implements SecondLevelEnemy {
   public void speed() {
       //lam cai gi do
   }
    public void healthy(){
       //lam cai gi do
   }
}
Bây giờ chúng ta tạo ra interface cho background và các hiện thực của nó.
public interface Background{
    public void display();
} 
public interface FirstLevelBackground extends Background{
} 
public interface SecondLevelBackground extends Background{
}
public class DarkGalaxy implements FirstLevelBackground {
   public void display() {
       //lam cai gi do
   }   
}
public class OldShipFLoor implements SecondLevelBackground {
   public void display() {
       //lam cai gi do
   } 
}
Tiếp đến ta tạo ra interface Factory và các hiện thực của nó.
public interface IFactory{
    public Enemy createEnemy();
    public Background createBackground();
} 
public class FirstLevelFactory implements IFactory {
    public Enemy createEnemy() {
       return RandomEnemy();
    }
    public Background createBackground() {
       return RandomEnemy();
    }  
    private FirstLevelEnemy RandomEnemy() {
       //lam gi do de random cac kieu
    }
    private FirstLevelBackground RandomBackground() {
       //lam gi do de random cac kieu
    }
}
public class SecondLevelFactory implements IFactory {
    public Enemy createEnemy() {
       return RandomEnemy();
    } 
    public Background createBackground() {
       return RandomEnemy();
    }  
    private SecondLevelEnemy RandomEnemy() {
       //lam gi do de random cac kieu
    }
    private SecondLevelBackground RandomBackground() {
       //lam gi do de random cac kieu
    }
}
Và cuối cùng là thêm 1 lớp factory bên ngoài IFactory được nữa. Ví dụ:
public class MotherOfFactory {
     public static IFactory createFactory(int level) {
         switch (level) {
             case 1: return FirstLevelFactory(); break;  
             case 2: return SecondLevelFactory(); break;  
             //and so on 
         }
     }
}
Bây giờ giả sử bạn ở level 1, thì có thể khởi tạo enemy bằng cách.
Enemy enemy = MotherOfFactory.createFactory(1).createEnemy();
Nếu bây giờ, bạn muốn lấy background, thì chỉ cần:
Background background = MotherOfFactory.createFactory(1).createBackground();
Lúc này Background và Enemy của bạn sẽ rất hợp lý, vì bạn đã định nghĩa nó ngay trong hiện thực của IFactory rồi.
Vậy thôi, quá đơn giản phải không?
Share:

Factory Method Pattern

Bao nhiêu bạn đã từng chơi game phi thuyền bắn súng? Giơ tay lên đi đừng ngại ngần. Có bạn nào chưa chơi game phi thuyền bắn súng không? Nếu chưa chơi thì nói thật, tuổi thơ bạn bất hạnh ghê gớm. Vì sao? vì trò chơi này là cả một tuổi thơ dữ dội, nó là cả 1 tinh hoa của nghệ thuật bắn súng trong đó. Vậy nên, chưa chơi thì hãy đi chơi đi, chơi ngay đi, vì trong bài này, mình sẽ lấy game đó làm ví dụ đấy. =))
Okie, trờ lại với bài này. Nếu như các bạn chơi game phi thuyền bắn súng thì các bạn sẽ thấy, trò chơi có các mảnh thiên thạch to nhỏ khác nhau, xuất hiện liên tục, nhiệm vụ của bạn là lo bắn phá hết tất cả cái đống đó. Vậy để generate ra được cái đống thiên thạch đó thì như nào. I zì, bạn dùng strategy pattern như trong đã học ở bài này rồi thiết kế ra thôi là được.
Giả sử bạn có thiên thạch to, thiên thạch nhỏ, thiên thạch vừa. Mỗi thiên thạch có tốc độ bay khác nhau, có lượng máu khác nhau, thì các bạn có thể dễ cmn dàng áp dụng strategy pattern để thiết kế.
Tất nhiên, bạn có thể tạo interface cho speed và healthy, nhưng ở đây mình tối giản nó lại, vì chúng ta đang học factory chứ ko phải strategy. Okie! Nói chung bạn cứ tạm hiểu là cái chúng ta đang thiết kế mấy viên thiên thạch rất hợp lý vào logic rồi.
Tiếp! Bây giờ làm thế nào để ta tạo ra mấy viên thiên thạch này bay lung tung xì phèo như game đây? Không lẽ cứ khởi tạo từng viên thiên thạch rồi ném nó vào UI à? Nghe như cứt ấy nhỉ? Vậy nên, người ta mới tạo ra ý tưởng là: "À, mình phải tạo 1 lớp, có nhiệm vụ sản sinh ra mấy cái IEnemy. IEnemy này có thể là thiên thạch to, thiên thạch nhỏ, thiên thạch vừa. Chỉ cần ta truyền parameter vào cho nó là nó sẽ tự động trả về cho mình 1 viên thiên thạch." . Ố dè, và đây chính là Factory Pattern.
Có thể hiểu ở đây, Factory là 1 cái nhà máy dùng để sản xuất IEnemy, dựa vào tên của enemy bạn muốn tạo. Và cái thể loại này ấy, người ta gọi là Simple Factory Pattern. Nhiều người cho rằng đây ko phải là pattern, ok thôi, vì mình là thằng ko thích định nghĩa nên người ta muốn bàn gì thì bàn, quan trọng là mình hiểu nó là cái gì là được rồi.
Ok! nhìn chung có vẻ ổn đúng không? Vậy tại sao người ta lại sản sinh ra thêm cái Factory Method Pattern làm gì? Sao không xài Simple Factory Pattern cho rồi, nhìn cũng ngon mà. Hờ, vấn đề là ở đây: chả có cái game củ chuối nào mà cứ bắn hoài mấy cái thiên thạch từ đầu tới cuối cả. Bạn chơi 1 hồi, tất nhiên độ khó của game sẽ tăng dần, mà độ khó game tăng tức là enemy cũng phải tăng dần. Ví dụ bạn chơi được 100 điểm, game sẽ chuyển qua level 2, chơi được 200 điểm game sẽ chuyển qua level 3. Và khi qua level 2 , level 3, game sẽ không còn là bắn thiên thạch nữa, mà chúng ta sẽ phải bắn tàu chiến, bắn rocket chẳng hạn. Vậy để giải quyết cái này thì làm sao?
Hừm, có thể chúng ta sẽ tạo ra những cái factory mới cho từng level tương ứng. Ví dụ ta có: Factory1 để sản sinh ra enemy của level 1, Factory2 để sản inh ra enemy của level 2... Nhưng nếu có 1000 level thì sao? Có thể nhiều bạn bảo: "Làm cứt gì có game 1000 level". Ôi con xin lạy, ý con chỉ là muốn lấy ví dụ để cho thấy cái vấn đề gặp phải của chúng ta thôi. Là bây giờ chúng ta sẽ có rất nhiều factory, và để đưa ra được enemy cho người chơi bắn, chúng ta phải xử lý để tiếp tục chọn factory tương ứng với level. Như vậy thì không hay. Giờ phải nghĩ ra cách mới thôi... Làm sao nhỉ???
À, đúng rồi!!! Chúng ta sẽ tạo ra 1 cái interface tên là IFactory, và những factory của từng level sẽ là hiện thực của IFactory này. Chúng sẽ có chung cơ chế sản sinh enemy là createEnemy, nhưng sẽ sản sinh ra những nhóm enemy khác nhau. Đấy, bạn bây giờ đã sử dụng Factory Method Pattern rồi đấy.
Factory Method Pattern: định nghĩa ra 1 cái interface cho phép ta khởi tạo 1 đối tượng, NHƯNG! Nó sẽ để cho lớp con của nó quyết định việc tạo ra đối tượng cụ thể. Factory Method Pattern nhường lại quyền khởi tạo cho lớp con của nó
Vậy cấu trúc cơ bản của 1 Factory Method Pattern nó là như nào? Đầu tiên ta sẽ có 1 cái interface product, và những hiện thực của nó.
Sau đó ta có 1 cái interface của Factory và những hiện thực con của nó.
Vậy đấy, Factory Method Pattern là vậy. Okie, chúng ta sẽ thử code 1 cách ngắn gọn xem nó hoạt động như nào nhá.
Đầu tiên là interface Enemy và 2 hiện thực con ở 2 level khác nhau. Chúng ta sẽ phân tầng nó để nhìn code có vẻ đẹp hơn.
public interface Enemy{
    public void speed();
    public void healthy();
} 
public interface FirstLevelEnemy extends Enemy{
} 
public interface SecondLevelEnemy extends Enemy{
}
public class BigMeteorite implements FirstLevelEmemy {
   public void speed() {
       //lam cai gi do
   }
    public void healthy(){
       //lam cai gi do
   }
}
public class VietNamSpaceShip implements SecondLevelEnemy {
   public void speed() {
       //lam cai gi do
   }
    public void healthy(){
       //lam cai gi do
   }
}
Tiếp đến ta tạo ra interface Factory và các hiện thực của nó.
public interface IFactory{
    public Enemy createEnemy();
} 
public class FirstLevelFactory implements IFactory {
    public Enemy createEnemy() {
       return RandomEnemy();
    } 
    private FirstLevelEnemy RandomEnemy() {
       //lam gi do de random cac kieu
    }
}
public class SecondLevelFactory implements IFactory {
    public Enemy createEnemy() {
       return RandomEnemy();
    } 
    private SecondLevelEnemy RandomEnemy() {
       //lam gi do de random cac kieu
    }
}
Bạn có thể bọc thêm 1 lớp factory bên ngoài IFactory được nữa. Ví dụ:
public class MotherOfFactory {
     public static IFactory createFactory(int level) {
         switch (level) {
             case 1: return FirstLevelFactory(); break;  
             case 2: return SecondLevelFactory(); break;  
             //and so on 
         }
     }
}
H bạn muốn tạo enemy của level 1 thì chỉ cần gõ vài dòng.
Enemy enemy = MotherOfFactory.createFactory(1).createEnemy();
H bạn muốn tạo enemy của level 2 thì chỉ cần gõ:
Enemy enemy = MotherOfFactory.createFactory(2).createEnemy();
Share:
Được tạo bởi Blogger.