Background
Hầu hết anh em developer đều đã nghe qua về Design Pattern
Tuy nhiên mình thấy còn nhiều người (gồm cả mình) đều không có kinh nghiêm áp dụng nó.
Nên mình lập topic về design pattern để mọi người có thể chia sẻ kinh nghiệm và cách áp dụng nó trong các bài toán cụ thể.
Vậy đầu tiên chúng ta phải hiểu design pattern là gì?
- Theo mình hiểu design pattern đơn giản là các giải pháp mẫu tối ưu cho từng tình huống cụ thể trong lập trình OOP.
Taị sao lại sử dụng design pattern?
Một thực tế là mình nghe rất nhiều câu như refactor đi (dm code như shit, fucking coding …),đập hết đi xây lại.
Tại sao chúng ta lại muốn như thế?
Vì hầu hết các source code ban đầu đều rất khó maintain và mở rộng, nên khi có một yêu cầu mới hay một bug,
anh em lại cặm cụi sửa code, sửa bug này lại sinh ra bug khác nên effort để giải quyết một vấn đề rất tốn kém.
Do đó nếu mình không chỉ "code để chạy được" mà suy nghĩ, áp dụng các design pattern trươc khi viết ra
sẽ rút ngắn phần lớn thời gian development và maintain.
Sau đây mình xin trình bầy một số design pattern mà mình đã đọc qua.
Đầu tiên mình xin tập trung vào một loại pattern mà chắc anh em ai cũng nghe qua, đó là Factory Pattern
Factory Pattern có 3 loại: Simple factory, Factory method và Abstract Factory
Bài lần này mình sẽ trình bày về Simple Factory Pattern
Simple Factory Pattern
Bài toán
Nào chúng ta hãy làm quen với Simple Factory Pattern với một ví dụ như sau:
Giả sử bạn đang viết chương trình đặt hàng và ship hàng cho một cửa hàng Pizza
Quá trình đặt hàng một chiếc Pizza sẽ qua các công đoạn như chuẩn bị (prepare), nướng (bake) và đóng hộp (box)
Xử lý để chạy được & vấn đề
Chương trình có thể được viết như sau
Class PizzaStore {
public Pizza orderPizza() {
Pizza pizza = new Pizza();
pizza.prepare();
pizza.bake();
pizza.box();
return pizza;
}
public void shipPizza() {
Pizza pizza = new Pizza();
pizza.ship();
}
}
OK như vậy là có sample về chương trình orderPizza và shipPizza.
Nhưng chủ cửa hàng lại muốn có nhiều món pizza cơ mà.
Ví du: Pizza gà (ChickenPizza), Pizza phô mai(CheesePizza)
Bạn nên làm thế nào ???
Dễ mà tạo một lớp cha Pizza và create 2 lớp con là ChickenPizza và CheesePizza
Sau đó add thêm parameter type cho orderPizza và shipPizza
Class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza;
if ("chicken".equals(type) {
pizza = new ChickenPizza();
} esle ("cheese".equals(type) {
pizza = new ChickenPizza();
}
pizza.prepare();
pizza.bake();
pizza.box();
return pizza;
}
public void shipPizza(String type) {
Pizza pizza;
if ("chicken".equals(type) {
pizza = new ChickenPizza();
} esle if ("cheese".equals(type) {
pizza = new ChickenPizza();
}
pizza.ship();
}
}
OK các bạn thấy thế nào, chương trình chạy ngon không có lỗi gì luôn :D.
Một tuần sau cửa hàng thấy cần thêm món Pizza hải sản (SeaFoodPizza) để tăng thêm khách hàng
Bạn sẽ vào sửa method orderPizza???
Class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza;
if ("chicken".equals(type) {
pizza = new ChickenPizza();
} esle if ("cheese".equals(type) {
pizza = new ChickenPizza();
} else if ("seafood".equals(type) {
pizza = new SeaFoodPizza();
}
pizza.prepare();
pizza.bake();
pizza.box();
}
public void shipPizza(String type) {
Pizza pizza;
if ("chicken".equals(type) {
pizza = new ChickenPizza();
} esle if ("cheese".equals(type) {
pizza = new ChickenPizza();
}
pizza.ship();
}
}
Cơn ác mộng mới chỉ bắt đầu :)). Đấy là mình chỉ liệt kê 2 chức năng cơ bản, điều gì sẽ xảy ra nếu còn rất nhiều chức năng khác cần lấy thông tin pizza theo từng loại
Ví du: Lấy thông tin giá, tên, … của từng loại Pizza
Bạn sẽ phải hì hục sửa code tất cả cả các method đấy nếu cửa hàng tạo thêm một loại Pizza.
Vâng bạn sẽ vẫn cố gắng sửa để nó có thể chạy được nhưng bạn sẽ tốn rất nhiều effort để làm việc này nếu có hàng chục method cần sửa.
Bạn cũng lo lắng mình có thể quên xử lý ở một method nào đấy,…
-> Giờ bạn đã sợ maintain chưa
Áp dụng Simple Factory Pattern vào bài toán
Quá nhiều điều để lo lắng chúng ta phải refactor thôi.
Đầu tiên chắc các bạn cũng thấy luôn, chúng ta cần đóng gói việc khởi tạo object bên dưới
if ("chicken".equals(type) {
pizza = new ChickenPizza();
} esle if ("cheese".equals(type) {
pizza = new ChickenPizza();
} else if ("seafood".equals(type) {
pizza = new SeaFoodPizza();
}
Chuyển đoạn code trên sang một class factory
Class SimplePizzaFactory {
public Pizza createPizza(type) {
Pizza pizza;
if ("chicken".equals(type) {
pizza = new ChickenPizza();
} esle if ("cheese".equals(type) {
pizza = new ChickenPizza();
} else if ("seafood".equals(type) {
pizza = new SeaFoodPizza();
}
return pizza;
}
}
Tiếp theo chúng ta sẽ sử dụng SimplePizzaFactory để tạo trong PizzaStore để tạo các object
Class PizzaStore {
SimplePizzaFactory mFactory;
public PizzaStore (SimplePizzaFactory factory) {
mFactory = factory;
}
public Pizza orderPizza(String type) {
Pizza pizza = mFactory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.box();
}
public void shipPizza(String type) {
Pizza pizza = mFactory.createPizza(type);
pizza.ship();
}
}
Bạn thấy công việc đơn giản hơn chưa, việc khởi tạo object cần thiết được xử lý trong Factory
Như vậy chúng ta không cần phải lo lắng sửa code ở từng method như orderPizzahay shipPizza, … nữa 🙂
Conlution
Đây chỉ là một ví dụ rất cơ bản về Design Pattern giúp mọi người dễ hình dung và tiếp cận
Chúng ta còn rất nhiều bài toán phức tạp khác cần Design Pattern để xử lý
Quan trọng mọi người hiểu được việc "code để chạy" có thể nhanh trong thời điểm đấy
Tuy nhiên việc mọi người phải rework để phát triển nó sẽ tốn gấp đôi gấp mười lần effort nếu mọi người có thể làm Design cẩn thận từ đầu
Do đó việc hiểu các Design pattern hỗ trợ rất tốt cho mọi người trong việc triển khai các bài toán cụ thể
Hy vọng bài này giúp mọi người hiểu được cơ bản Design Pattern là gì và tầm quan trọng của nó trong software