Mẫu thiết kế hướng đối tượng – Decorator Pattern

Decorator pattern

Nếu bạn nào chưa đọc bài: Học Design Pattern đúng cách thì xem lại bài viết của mình nhé để biết cách học Design Pattern hợp lý: OOP và Design Pattern

Decorator Pattern dịch ra Tiếng Việt có nghĩa là “Trang Trí”. Khi mình trang trí nhà của mình, mình không thay đổi kết cấu, kiến trúc của nhà mình, mình chỉ gắn thêm các phụ kiện để ngôi nhà đẹp hơn, thêm tính năng.

Decorator Pattern giải quyết bài toán, khi ta muốn bổ sung thêm tính năng cho một Class, một đối tượng mà không làm thay đổi đối tượng ban đầu. Ví dụ:
– Trong lập trình game, nhân vật lắp thêm các phụ kiện như trang phục, vũ khí.
– Trong javascript, trong java, mình thấy @Anotation để wrap lại function, class.
– High Order Component: Wrap lại function gốc, để làm gì đó, trước khi gọi tới function gốc.

Chúng ta đã nắm được ý tưởng của bài toán mà mình sẽ áp dụng mẫu Decorator. Bây giờ chúng ta sẽ đi chi tiết hơn làm sao để implement chúng. Tùy vào ngôn ngữ lập trình, mà cách thức apply sẽ khác nhau, do đó các bạn cần tìm sách nói về mẫu thiết kế đó đối với ngôn ngữ mà mình đang sử dụng.
Tiếp theo mình sẽ implement một ví dụ bằng ngôn ngữ Javascript:

– Ví dụ 1: Bài toán đặt ra, mình có một game, có nhân vật Hero, nhân vật này có thể gắn thêm một số phụ kiện: Hat, Ring. Khi gắn thêm những phụ kiện này thì sức mạnh của của nhân vật sẽ đổi theo.

+ Code bằng ES5: https://github.com/duongmngo/oop-design-pattern/blob/master/decorator_pattern/example.es5.js

function Hero(strength) {
    this.strength = strength
    this.decorators = []
}

Hero.prototype.getStrength = function() {
    var totalStrength = this.strength
    for (var decorator of this.decorators) {
        totalStrength += decorator.getPercentStrengthen() * this.strength
    }
    return totalStrength
}

Hero.prototype.render = function() {
    var decoratorsName = this.decorators.map(function(o) {return o.getName()})    
    console.log('Hero with ', decoratorsName.join(','))
}

Hero.prototype.decorate = function(decorator) {
    this.decorators.push(decorator)
}

function HeroDecorator(name, percentStrengthen) {
    this.name = name
    this.percentStrengthen = percentStrengthen
}

HeroDecorator.prototype.getPercentStrengthen = function() {
    return this.percentStrengthen
}

HeroDecorator.prototype.getName = function() {
    return this.name
}

function Hat(name) {
    HeroDecorator.call(this, name, 10/100)    
}

Hat.prototype = Object.create(HeroDecorator.prototype)
Hat.prototype.constructor = Hat

function Ring(name) {
    HeroDecorator.call(this, name, 20/100)        
}

Ring.prototype = Object.create(HeroDecorator.prototype)
Ring.prototype.constructor = Ring

var hero = new Hero(100)
hero.decorate(new Hat("a red hat"))
hero.decorate(new Ring("a diamon ring"))
hero.render() // Hero with  a red hat,a diamon ring
console.log(hero.getStrength()) // 130

+ Code bằng ES6:  https://github.com/duongmngo/oop-design-pattern/blob/master/decorator_pattern/example.es6.js

class Hero {
    constructor(strength) {
        this.strength = strength
        this.decorators = []
    }

    getStrength() {
        var totalStrength = this.strength
        for (var decorator of this.decorators) {
            totalStrength += decorator.getPercentStrengthen() * this.strength
        }
        return totalStrength
    }

    decorate(decorator) {
        this.decorators.push(decorator)
    }

    render() {
        var decoratorsName = this.decorators.map(function(o) {return o.getName()})    
        console.log('Hero with ', decoratorsName.join(','))
    }
}

class HeroDecorator {
    constructor(name, percentStrengthen) {
        console.log(`${name} - ${percentStrengthen}`)
        this.name = name
        this.percentStrengthen = percentStrengthen
    }

    getName() {
        return this.name
    }

    getPercentStrengthen() {
        return this.percentStrengthen
    }
}

class Hat extends HeroDecorator {        
    constructor(name) {
        super(name, 10/100)
    }
}

class Ring extends HeroDecorator {    
    constructor(name) {
        super(name, 20/100)
    }
}

var hero = new Hero(100)
hero.decorate(new Hat("a red hat"))
hero.decorate(new Ring("a diamon ring"))
hero.render()
console.log(hero.getStrength())

– Ví dụ 2: Xem trong link Javascript Decorator

Nguồn tham khảo:
– Book: Javascript Pattern
– https://www.tutorialspoint.com/design_pattern/decorator_pattern.htm

Leave A Reply

Your email address will not be published. Required fields are marked *