Decorator design pattern

Trong bài viết này chúng ta sẽ cùng tìm hiểu về decorator design pattern bằng ngôn ngữ scala

Mục đích

  • Gắn các trách nhiệm bổ sung cho một object động. Decorator cung cấp một thay thế linh hoạt cho sub-class cho chức năng mở rộng.
  • Thêm vào client được chỉ định của core object bằng cách đệ quy đóng gói nó
  • Đóng gói quà, đặt vào trong một cái hộp và đóng gói hộp đó

Cấu trúc

Client thường chỉ quan tâm đến CoreFunctionality.doThis(). Client có thể hoặc không quan tâm đến OptionalOne.doThis() và OptionalTwo.doThis(). Mỗi class này luôn luôn đại diện cho base class Decorator và class đó luôn đại diện cho object “wrappee” chứa

Decorator design pattern là gì?

Decorator design pattern là một structural design pattern

Structural design pattern tập trung vào các thành phần class và object.

Pattern này là về tạo một decorator class mà có thể đóng gói class ban đầu và cung cấp thêm các chức năng giữ cho các phương thức class đăng ký nguyên vẹn

Một thiết kế sử dụng Decorator thường đưa ra kết quả trong hệ thống bao gồm nhiều các object nhỏ mà tất cả trông giống nhau

Ví dụ

Giả sử chúng ta có một cửa hàng Pizza và chúng ta đều hiểu rằng khách hàng sẽ có những khẩu vị khác nhau, do đó có thể cần kết hợp phần khác nhau trên lớp bề mặt.

Nếu chúng ta có n phần bề mặt pizza, thì chúng ta phải tạo p(n) = 2*p(n-1) sub-class.

p(0) = 0

p(1) = 2 * p(1-1) + 1 = 1

p(2) = 2 * p(2-1) + 1 = 2 * p(1) + 1 = 2 * 1 + 1 = 3

p(3) = 2 * p2 + 1 = 2 * 3 + 1 = 7

p(4) = 2 * p3 + 1 = 2 * 7 + 1 = 15

Nếu chúng ta có 3 bề mặt thì số sub-class sẽ là p(3) = 7

Số lượng khách hàng ngày càng đông nên tôi muốn mở rộng nó. Nên tôi sẽ thêm 2 lựa chọn bề mặt nữa cho các khách hàng quan trọng. Nên bây giờ chúng ta có 5 bề mặt và số sub-class sẽ là 31.

Nhưng đây đúng là một công việc nhàn chán mà chúng ta phải làm. Nên chúng ta sẽ thử sử dụng decorator design pattern để giải quyết vấn đề này.

 

Đầu tiên chúng ta sẽ tạo một Topping trait mà được implement bởi BasePizza và ToppingDecorator class. Pizza class tổng hợp nó.

ToppingDecorator được extend bởi CheeseTopping và OnionTopping

BasePizza.scala

class BasePizza extends Topping {
    def getName() : String = "Pizza"

    def getPrice() : Double = 77.0

    def addTopping() : Topping = this
}

CheeseTopping.scala

class CheeseTopping(override val topping : Topping) extends ToppingDecorator(topping) {
    override def getPrice() : Double = {
        super.getPrice() + 59.0
    }

    override def getName() : String = {
        val previous = super.getName()
        "Ocean Cheese " + previous
    }
}

OnionTopping.scala

class OnionTopping(override val topping : Topping) extends ToppingDecorator(topping) {
    override def getPrice() : Double = {
        super.getPrice() + 39.0
    }

    override def getName() : String = {
        val previous = super.getName()
        "Sprinkled Onion " + previous
    }
}

Pizza.scala

class Pizza {
    var topping : Topping = new BasePizza

    def getPrice() : Double = {
        topping.getPrice()
    }

    def getName() : String = {
        topping.getName()
    }

    def addNewTopping(toppingName : String) : Boolean = {
        toppingName match
        {
            case "Onion" =>
            {
                this.topping = new OnionTopping(topping)
                true
            }
            case "Cheese" =>
            {
                this.topping = new CheeseTopping(topping)
                true
            }
            case _ =>
                println("Topping unavailable! Better luck next time! :(")
                false
        }
    }
}

PizzaStore.scala

object PizzaStore extends App {
    val pizza = new Pizza
    pizza.addNewTopping("Cheese")
    pizza.addNewTopping("Onion")
    println(s"You have ordered ${pizza.getName}")
    println(s"You have to pay Rupees ${pizza.getPrice}")
}

Topping.scala

trait Topping {
    def getName() : String

    def getPrice() : Double

    def addTopping() : Topping
}

ToppingDecorator.scala 

class ToppingDecorator(val topping : Topping) extends Topping {
    var nextTopping : Topping = topping

    def getName() : String = nextTopping.getName()

    def getPrice() : Double = nextTopping.getPrice()

    def addTopping() : Topping = this
}

 

You May Also Like

About the Author: Nguyen Dinh Thuc

Leave a Reply

Your email address will not be published.