Trước tiên, chúng ta hãy cùng cafedev giới thiệu mọi thứ về Flyweight Design Pattern và phần code ví dụ chi tiết nhằm giúp ace dễ hiểu khi áp dụng trên các ngôn ngữ khác nhau. Ace có thể tham khảo thêm các bài khác tại series Design Pattern tại đây.

Flyweight pattern là một trong những structural design patterns

 vì pattern này cung cấp các cách để giảm số lượng đối tượng do đó cải thiện cấu trúc đối tượng theo yêu cầu của ứng dụng. Flyweight pattern được sử dụng khi chúng ta cần tạo một số lượng lớn các đối tượng tương tự (giả sử 10 5 ). Một tính năng quan trọng của objects flyweight là chúng không thay đổi được . Điều này có nghĩa là chúng không thể được sửa đổi khi chúng đã được xây dựng.

1. Tại sao chúng ta quan tâm đến số lượng đối tượng trong chương trình của mình?

  • Số lượng đối tượng ít hơn làm giảm mức sử dụng bộ nhớ và giúp chúng ta tránh xa các lỗi liên quan đến bộ nhớ như java.lang.OutOfMemoryError.
  • Mặc dù việc tạo một đối tượng trong Java thực sự rất nhanh, chúng ta vẫn có thể giảm thời gian thực thi chương trình của mình bằng cách chia sẻ các đối tượng.

Trong Flyweight pattern, chúng ta sử dụng HashMap lưu trữ tham chiếu đến đối tượng đã được tạo, mọi đối tượng đều được liên kết với một khóa. Bây giờ khi một khách hàng muốn tạo một đối tượng, anh ta chỉ cần chuyển một khóa được liên kết với nó và nếu đối tượng đã được tạo, chúng ta chỉ cần lấy tham chiếu đến đối tượng đó, nếu không nó sẽ tạo một đối tượng mới và sau đó trả về tham chiếu cho client.

2. Các trạng thái bên trong và bên ngoài(Intrinsic and Extrinsic States)

Để hiểu trạng thái Nội tại và Ngoại tại, chúng ta hãy xem xét một ví dụ.

Giả sử trong trình soạn thảo văn bản khi ta nhập ký tự, một đối tượng của lớp Character được tạo ra, các thuộc tính của lớp Character là {name, font, size}. Chúng ta không cần tạo một đối tượng mỗi khi khách hàng nhập một ký tự vì chữ ‘B’ không khác chữ ‘B’ khác. Nếu máy khách nhập lại chữ ‘B’, chúng ta chỉ cần trả lại đối tượng mà chúng ta đã tạo trước đó. Bây giờ tất cả những điều này là trạng thái nội tại (tên, phông chữ, kích thước), vì chúng có thể được chia sẻ giữa các đối tượng khác nhau vì chúng tương tự với nhau.

Bây giờ chúng ta thêm nhiều thuộc tính hơn vào lớp Character, chúng là hàng và cột. Chúng chỉ định vị trí của một ký tự trong tài liệu. Giờ đây, các thuộc tính này sẽ không giống nhau ngay cả đối với các ký tự giống nhau, vì không có hai ký tự nào có cùng vị trí trong một tài liệu, các trạng thái này được gọi là trạng thái bên ngoài và chúng không thể được chia sẻ giữa các đối tượng.

Thực hiện: Chúng ta thực hiện việc tạo ra những kẻ khủng bố và chống lại những kẻ khủng bố trong trò chơi Counter Strike . Vì vậy, chúng ta có 2 lớp một cho T errorist ( T ) và một cho C ounter T errorist ( CT ). Bất cứ khi nào người chơi yêu cầu một vũ khí, chúng ta sẽ giao cho anh ta vũ khí được yêu cầu. Trong nhiệm vụ, nhiệm vụ của bọn khủng bố là đặt một quả bom trong khi bọn khủng bố chống trả phải khuếch tán quả bom.

Tại sao sử dụng Flyweight Design Pattern trong ví dụ này? Ở đây chúng ta sử dụng Flyweight Design Pattern, vì ở đây chúng ta cần giảm số lượng đối tượng cho người chơi. Bây giờ chúng ta có n số người chơi CS 1.6, nếu chúng ta không tuân theo Flyweight Design Pattern thì chúng ta sẽ phải tạo ra n số đối tượng, mỗi đối tượng một. Nhưng bây giờ chúng ta sẽ chỉ phải tạo ra 2 đối tượng một cho những kẻ khủng bố và một cho những kẻ khủng bố khác, chúng ta sẽ sử dụng lại sau đó nhiều lần bất cứ khi nào được yêu cầu.

Trạng thái nội tại(Intrinsic State): Ở đây ‘nhiệm vụ’ là trạng thái nội tại cho cả hai loại người chơi, vì điều này luôn giống nhau đối với T / CT. Chúng ta có thể có một số trạng thái khác như màu của chúng hoặc bất kỳ thuộc tính nào khác tương tự đối với tất cả Kẻ khủng bố / Chống khủng bố trong lớp Kẻ khủng bố / Chống khủng bố tương ứng của chúng.

Trạng thái bên ngoài(Extrinsic State): Vũ khí là trạng thái bên ngoài vì mỗi người chơi có thể mang theo bất kỳ vũ khí nào mà mình lựa chọn. Vũ khí cần được chính ứng dụng khách chuyển dưới dạng tham số.

Sơ đồ lớp:

// A Java program to demonstrate working of 
// FlyWeight Pattern with example of Counter 
// Strike Game 
import java.util.Random; 
import java.util.HashMap; 
  
// A common interface for all players 
interface Player 
{ 
    public void assignWeapon(String weapon); 
    public void mission(); 
} 
  
// Terrorist must have weapon and mission 
class Terrorist implements Player 
{ 
    // Intrinsic Attribute 
    private final String TASK; 
  
    // Extrinsic Attribute 
    private String weapon; 
  
    public Terrorist() 
    { 
        TASK = "PLANT A BOMB"; 
    } 
    public void assignWeapon(String weapon) 
    { 
        // Assign a weapon 
        this.weapon = weapon; 
    } 
    public void mission() 
    { 
        //Work on the Mission 
        System.out.println("Terrorist with weapon "
                           + weapon + "|" + " Task is " + TASK); 
    } 
} 
  
// CounterTerrorist must have weapon and mission 
class CounterTerrorist implements Player 
{ 
    // Intrinsic Attribute 
    private final String TASK; 
  
    // Extrinsic Attribute 
    private String weapon; 
  
    public CounterTerrorist() 
    { 
        TASK = "DIFFUSE BOMB"; 
    } 
    public void assignWeapon(String weapon) 
    { 
        this.weapon = weapon; 
    } 
    public void mission() 
    { 
        System.out.println("Counter Terrorist with weapon "
                           + weapon + "|" + " Task is " + TASK); 
    } 
} 
  
// Class used to get a player using HashMap (Returns 
// an existing player if a player of given type exists. 
// Else creates a new player and returns it. 
class PlayerFactory 
{ 
    /* HashMap stores the reference to the object 
       of Terrorist(TS) or CounterTerrorist(CT).  */
    private static HashMap <String, Player> hm = 
                         new HashMap<String, Player>(); 
  
    // Method to get a player 
    public static Player getPlayer(String type) 
    { 
        Player p = null; 
  
        /* If an object for TS or CT has already been 
           created simply return its reference */
        if (hm.containsKey(type)) 
                p = hm.get(type); 
        else
        { 
            /* create an object of TS/CT  */
            switch(type) 
            { 
            case "Terrorist": 
                System.out.println("Terrorist Created"); 
                p = new Terrorist(); 
                break; 
            case "CounterTerrorist": 
                System.out.println("Counter Terrorist Created"); 
                p = new CounterTerrorist(); 
                break; 
            default : 
                System.out.println("Unreachable code!"); 
            } 
  
            // Once created insert it into the HashMap 
            hm.put(type, p); 
        } 
        return p; 
    } 
} 
  
// Driver class 
public class CounterStrike 
{ 
    // All player types and weapon (used by getRandPlayerType() 
    // and getRandWeapon() 
    private static String[] playerType = 
                    {"Terrorist", "CounterTerrorist"}; 
    private static String[] weapons = 
      {"AK-47", "Maverick", "Gut Knife", "Desert Eagle"}; 
  
  
    // Driver code 
    public static void main(String args[]) 
    { 
        /* Assume that we have a total of 10 players 
           in the game. */
        for (int i = 0; i < 10; i++) 
        { 
            /* getPlayer() is called simply using the class 
               name since the method is a static one */
            Player p = PlayerFactory.getPlayer(getRandPlayerType()); 
  
            /* Assign a weapon chosen randomly uniformly 
               from the weapon array  */
            p.assignWeapon(getRandWeapon()); 
  
            // Send this player on a mission 
            p.mission(); 
        } 
    } 
  
    // Utility methods to get a random player type and 
    // weapon 
    public static String getRandPlayerType() 
    { 
        Random r = new Random(); 
  
        // Will return an integer between [0,2) 
        int randInt = r.nextInt(playerType.length); 
  
        // return the player stored at index 'randInt' 
        return playerType[randInt]; 
    } 
    public static String getRandWeapon() 
    { 
        Random r = new Random(); 
  
        // Will return an integer between [0,5) 
        int randInt = r.nextInt(weapons.length); 
  
        // Return the weapon stored at index 'randInt' 
        return weapons[randInt]; 
    } 
} 

Đầu ra:

Counter Terrorist Created
Counter Terrorist with weapon Gut Knife| Task is DIFFUSE BOMB
Counter Terrorist with weapon Desert Eagle| Task is DIFFUSE BOMB
Terrorist Created
Terrorist with weapon AK-47| Task is PLANT A BOMB
Terrorist with weapon Gut Knife| Task is PLANT A BOMB
Terrorist with weapon Gut Knife| Task is PLANT A BOMB
Terrorist with weapon Desert Eagle| Task is PLANT A BOMB
Terrorist with weapon AK-47| Task is PLANT A BOMB
Counter Terrorist with weapon Desert Eagle| Task is DIFFUSE BOMB
Counter Terrorist with weapon Gut Knife| Task is DIFFUSE BOMB
Counter Terrorist with weapon Desert Eagle| Task is DIFFUSE BOMB

Cài ứng dụng cafedev để dễ dàng cập nhật tin và học lập trình mọi lúc mọi nơi tại đây.

Tài liệu từ cafedev:

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!