# 59: Thiếu cơ hội cho Polymorphism

Polymorphism là một trong những ý tưởng lớn làm nền tảng cho OO. Từ này được lấy từ tiếng Hy Lạp, có nghĩa là nhiều (poly) dạng (morph).

Trong lập trình, Polymorphism đề cập đến nhiều dạng của một class đối tượng hoặc phương thức cụ thể. Nhưng Polymorphism không chỉ đơn thuần là về việc triển khai thay thế. Nếu được sử dụng cẩn thận, Polymorphism sẽ tạo ra các context thực thi cục bộ nhỏ cho phép chúng ta làm việc mà không cần phải sử dụng câu lệnh if-then-else dài dòng. Ở bên trong context cho phép chúng ta làm việc trực tiếp, còn ở bên ngoài context, chúng ta buộc phải xây dựng lại nó rồi mới có thể làm việc. Với việc sử dụng cẩn thận các triển khai thay thế, chúng ta có thể nắm bắt context, qua đó giúp sản xuất ít mã hơn nhưng đem lại hiệu quả cao hơn. Điều này được thể hiện tốt nhất thông qua một số mã, chẳng hạn như giỏ hàng giả định đơn giản sau đây:

public class ShoppingCart {
    private ArrayList<Item> cart = new ArrayList<Item>(); 

    public void add(Item item) { 
        cart.add(item); 
    } 
    
    public Item takeNext() { 
        return cart.remove(0); 
    } 
    
    public boolean isEmpty() {
        return cart.isEmpty(); 
    }
}

Giả sử webshop của chúng tôi cung cấp các mặt hàng có thể tải xuống và các mặt hàng cần vận chuyển. Hãy xây dựng một đối tượng hỗ trợ hoạt động này:

public class Shipping {
    public boolean ship(Item item, SurfaceAddress address) {} 
    public boolean ship(Item item, EMailAddress address {}
}

Khi một khách hàng đã thanh toán xong, chúng tôi cần vận chuyển hàng hóa:

while (!cart.isEmpty()) { 
    shipping.ship(cart.takeNext(), ???);
}

Tham số ??? không phải là một số toán tử elvis mới. Nó hỏi tôi nên gửi email 📮 hay gửi thư cho mục này? Context cần thiết để trả lời câu hỏi này không còn tồn tại. Chúng tôi có thể nắm bắt được phương thức giao hàng trong boolean hoặc enum và sau đó sử dụng if-then-else để điền vào tham số bị thiếu. Một giải pháp khác sẽ là tạo hai class đều mở rộng Item. Hãy gọi đó là DownloadableItemSurfaceItem. Bây giờ hãy viết một số code. Tôi sẽ quảng cáo Item như một giao diện hỗ trợ phương thức duy nhất- xuất xưởng. Để gửi giỏ hàng, chúng tôi sẽ gọi item.ship(shipper). Các class DownloadableItemSurfaceItem sẽ cùng thực hiện việc giao hàng.

public class DownloadableItem implements Item { 
    public boolean ship(Shipping shipper) { 
        shipper.ship(this, customer.getEmailAddress());
    }
}

public class SurfaceItem implements Item { 
    public boolean ship(Shipping shipper) { 
        shipper.ship(this, customer.getSurfaceAddress());
    }
}

Trong ví dụ này, chúng tôi đã ủy thác trách nhiệm làm việc với Shipping cho từng Item. Vì mỗi mặt hàng đều được vận chuyển tốt, nên sự sắp xếp này cho phép chúng tôi tiếp tục mà không cần if-then-else. Code này cũng minh hoạ cho hai mẫu thường được kết hợp với nhau: CommandDouble Dispatch. Việc sử dụng hiệu quả các mẫu này phụ thuộc vào việc sử dụng Polymorphism một cách cẩn thận. Khi điều đó xảy ra, số lượng câu lệnh if-then-else trong code sẽ giảm thiểu đáng kể

Mặc dù có những trường hợp sẽ hiệu quả hơn nhiều khi sử dụng if-then-else thay vì Polymorphism, nhưng thông thường, Polymorphism coding sẽ mang lại một cơ sở code nhỏ hơn, dễ đọc hơn và ít mong manh hơn. Số cơ hội bị bỏ lỡ là số lượng các câu lệnh if-then-else trong code.